Blog

"Knowledge has no value unless you use it and share it"

JPA 2: CRITERIA BUILDER

Objective

Create dynamic queries in a typesafe manner.

The Criteria API includes mechanisms for building queries dynamically at run time. A Java compiler can check for errors, in contrast to string-based Java Persistence Query Language (JPQL) queries.

Simple Example

The following example returns a list of Groups.


    CriteriaBuilder criteriaBuilder = getCriteriaBuilder();
    CriteriaQuery<Group> query = criteriaBuilder.createQuery(Group.class);
    Root<Group> group = query.from(Group.class);
    
    query.select(group);
    TypedQuery<Group> typedQuery = createTypedQuery(query);
    return typedQuery.getResultList();

Dynamic Where Conditions

The following example dynamically adds Predicates to filter the results at run-time. The example also includes ordering and the application of limits.


    CriteriaBuilder cb = getCriteriaBuilder();
    CriteriaQuery<User> query = cb.createQuery(User.class);
    Root<User> user = query.from(User.class);

    List<Predicate> predicates = new ArrayList();
    if (criteria.getSchoolId() != null){
        Join<User, UserSchoolRelationship> userSchool = user.join("userSchoolRelationships");
        predicates.add(cb.equal(userSchool.get("school"), criteria.getSchoolId()));
    }
    if (criteria.getEnabled() != null)
        predicates.add(cb.equal(user.get("enabled"), criteria.getEnabled()));
    if (criteria.isAllowedOnTablet() != null)
        predicates.add(cb.equal(user.get("allowedOnTablet"), criteria.isAllowedOnTablet()));

    query.select(user)
            .where(predicates.toArray(new Predicate[]{}));

    query.orderBy(cb.asc(user.get("username")));
    return entityManager.createQuery(query)
            .setFirstResult(criteria.getStart())
            .setMaxResults(criteria.getLimit())
            .getResultList();

Casting to Custom Types

The following example shows how to return a custom object from a query. Here we have created a new object to return to the service layer, CustomModelForCast. This can be useful for any aggregate functions used in queries. In the example we are returning a simple object that contains the company and the count of users in that company.


    public class CustomModelForQueryResults{
       private Company company;
       private Long count;

       public CustomModelForQueryResults(Company company, Long count){
           this.company = company;
           this.count = count;
       }

       public Company getCompany() {
           return company;
       }

       public void setStore(Company company) {
           this.company = company;
       }

       public Long getCount() {
           return count;
       }

       public void setCount(Long count) {
           this.count = count;
       }
    }


    public List<CustomModelForQueryResults> countRequestsGroupedByStore(List<Status> statuses) {

       CriteriaBuilder builder = entityManager.getCriteriaBuilder();
       CriteriaQuery<CustomModelForQueryResults> query = builder.createQuery(CustomModelForQueryResults.class);
       Root<User> sr = query.from(User.class);
       Join<User, Company> company= sr.join("company");

       query.multiselect(company, builder.count(user)).where(sr.get("status").in(statuses)).groupBy(sr.get("company"));

       TypedQuery<CustomModelForQueryResults> q = entityManager.createQuery(query);
       return q.getResultList();
    }


Conclusion

This article demonstrates some of the benefits of using CriteriaBuilder in JPA 2. Besides avoiding the issues associated with JPQL we can build flexible queries and avail of the CriteriaQuery’s query functions.

VIEW ALL BLOGS