Parameter value did not match expected type java.util.Set

I have the following entity:

@Entity
public class Person extends BaseEntity {
    private static final String PETS_SEPARATOR = ";";

    @Id
    @Nonnull
    private String id;

    @Column(name = "pets")
    @Convert(converter = StringSetConverter.class)
    @Nonnull
    private Set<String> pets;
}

And the StringSetConverter basically parses between String (separated by ;) and Set.

Notice that this is inherited code and I cannot easily change the database structure to have the pet relations in a separated table.

This causes having in the database a string like: "DOG;CAT;BIRD". And I want to find all the users that have "CAT", for instance.

So I tried a silly search like this one

Specification<UserEntity> petsSpecification = 
    (root, criteriaQuery, cb) -> {
        return cb.like(root.get("pets"), "%CAT%"));
    };

This is not elegant, I'm aware of that, and here we should take into account the separator (what happens if I have another animal called "PERSIAN_CAT"?). But anyway, this is failing with this:

Caused by: java.lang.IllegalArgumentException: Parameter value [CAT] did not match expected type [java.util.Set (n/a)]

So it seems right as long as we are working in Hibernate, and the attribute pets is in fact a Set, not a String. But after looking into the internet and checking the documentation, I cannot see a clear way of making this query using Criteria API. I could use a native query to work with the String instead of the Set and make the "like" approach, but I would like to do it by code, non-depending in the technology of the database.

Upvotes: 1

Views: 6472

Answers (1)

İsmail Y.
İsmail Y.

Reputation: 3945

You can simply perform a typecast upon the expression, returning a new expression object.

Specification<AttributePerson> rawPetSpecification =
                (root, criteriaQuery, cb) -> cb.like(root
                        .get("pets").as(String.class), "%CAT%");

OR

You could map the Set<String> column in another property of String type:

@Column(name = "pets", insertable = false, updatable = false)
private String rawPets;

This column with the attribute insertable = false, updatable = false as being neither insertable nor updatable, so that its value is not persisted, so you get the stored value. https://stackoverflow.com/a/3805682/2039546

Now you can write a query like this:

Specification<AttributePerson> rawPetSpecification =
                (root, criteriaQuery, cb) -> 
                        cb.like(root.get("rawPets"), "%CAT%");

Upvotes: 4

Related Questions