Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use CriteriaQuery for ElementCollection and CollectionTable

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.

like image 681
hevi Avatar asked Apr 23 '15 23:04

hevi


People also ask

What is the use of ElementCollection?

An ElementCollection can be used to define a one-to-many relationship to an Embeddable object, or a Basic value (such as a collection of String s).

What is @CollectionTable?

In the simplest term, @ElementCollection tells the compiler that we are mapping a collection, in which, @CollectionTable gives the name of the target table and then @JoinColumn specifies the actually column we join on like below: 1`

Which method can be used for ordering the results using criteria queries?

The CriteriaQuery interface defines the orderBy method to order query results according to attributes of an entity.

How do you use JPA criteria?

Create an instance of Session from the SessionFactory object. Create an instance of CriteriaBuilder by calling the getCriteriaBuilder() method. Create an instance of CriteriaQuery by calling the CriteriaBuilder createQuery() method. Create an instance of Query by calling the Session createQuery() method.


2 Answers

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")

like image 111
Donovan Muller Avatar answered Oct 06 '22 00:10

Donovan Muller


Try to use isMember() rather than in()

Check the example 5 and 7

like image 32
StanislavL Avatar answered Oct 06 '22 01:10

StanislavL