Reputation: 2716
I have a very simple entity Product which has a code, name and tags. Tags are stored in another table (product_tag) with product_id and tag columns.
I need to search for products with certain tags using CriteriaQuery. To give an example I want to find products having 'fruit' and 'red' tags.
Using spring 4.1.x, spring-data-jpa 1.8 and hibernate 4.2.x.
My entity simply is;
@Entity
@Table(name = "product", uniqueConstraints ={
@UniqueConstraint(columnNames = "code")
}
)
@NamedQueries({
@NamedQuery(name = "Product.findAll", query = "select p from Product p")
})
public class Product extends EntityWithId {
@Column(name = "code", length = 128)
private String code;
@Column(name = "name", length = 512)
protected String name;
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name="product_tag", joinColumns=@JoinColumn(name="product_id"))
@Column(name="tag")
private Set<String> productTags = new HashSet<>();
}
here is the code how I initiate the search;
private void search() {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Product> criteriaQuery = builder.createQuery(Product.class);
Root<Product> product = criteriaQuery.from(Product.class);
Predicate where = builder.conjunction();
if (!StringUtils.isEmpty(nameSearch.getValue())) {
where = builder.and(where, builder.like(product.<String>get("name"), nameSearch.getValue() + "%"));
}
if (!StringUtils.isEmpty(codeSearch.getValue())) {
where = builder.and(where, builder.like(product.<String>get("code"), codeSearch.getValue() + "%"));
}
if (!StringUtils.isEmpty(tagsSearch.getValue())) {
//Util.parseCommaSeparated returns Set<String>
where = builder.and(where, product.get("productTags").in(Util.parseCommaSeparated(tagsSearch.getValue())));
}
criteriaQuery.where(where);
List<Product> resultList = entityManager.createQuery(criteriaQuery).getResultList();
}
However when I run the search for tags 'fruit' I get an exception
java.lang.IllegalArgumentException: Parameter value [fruit] did not match expected type [java.util.Set (n/a)]
I really wonder to use CriteriaQuery for ElementCollection and CollectionTable.
Upvotes: 14
Views: 11194
Reputation: 57381
Try to use isMember() rather than in()
Check the example 5 and 7
Upvotes: 1
Reputation: 3842
productTags
is mapped to a separate table, therefore you need to join with that table in your query.
...
if (!StringUtils.isEmpty(tagsSearch.getValue())) {
//Util.parseCommaSeparated returns Set<String>
where = builder.and(where, product.join("productTags").in(Util.parseCommaSeparated(tagsSearch.getValue())));
}
...
Note the product.join("productTags")
instead of product.get("productTags")
Upvotes: 26