Introduction

If you ever wanted a simple way to query your database without writing complicated Hibernate queries - Criteria API is the right thing for you. In this tutorial, you'll learn how to use JPA Criteria API  and how to build CriteriaQuery on a simple example.

Implementation

First, we'll define entity class Item with fields id, name, code. This class represents our data table. We will create one more class called ItemQueryCriteria that will contain only query parameters we would like to use to query out data with JPA Criteria API.

@Entity
public class Item {

  private Long id;
  private String code;
  private String name;
  
  public Long getId() {
    return id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
 
  public String getCode() {
    return code;
  }

  public void setCode(String code) {
    this.code = code;
  }
}

 Class ItemQueryCriteria will contain variables representing query parameters we would like to use to query the database. So, in this scenario, we'll use code and name as query parameters.

public class ItemQueryCriteria {

  private String code;
  private String name;

  public void setName(String name) {
    this.name = name;
  }
 
  public String getCode() {
    return code;
  }

  public void setCode(String code) {
    this.code = code;
  }
}

And finally, this is our repository method. In the following text, we'll go line by line.

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

@Repository
public class ItemsRepositoryCustomImpl {

 @PersistenceContext
 private EntityManager em;

 public List<Item> findByCriteria(ItemCriteria itemCriteria) {
  CriteriaBuilder cb = em.getCriteriaBuilder();
  CriteriaQuery<Item> cq = cb.createQuery(Item.class);
  Root<Item> item = cq.from(Item.class);
  List<Predicate> predicates = new ArrayList<Predicate>();
  // checking if parameter name is provided, if yes, adding new predicate
  if (itemCriteria.getName() != null) {
	predicates.add(cb.like(item.get("name"), "%" + itemCriteria.getName() + "%"));
  }
  // checking if parameter code is provided, if yes, adding new predicate
  if (itemCriteria.getCode() != null) {
	predicates.add(cb.equal(item.get("code"), itemCriteria.getCode()));
     }
  cq.where(predicates.toArray(new Predicate[] {}));
  TypedQuery<Item> query = em.createQuery(cq);
  List<Item> items = query.getResultList();
  return items;
 }
}

Here we initialize CriteriaBuilder and CriteriaQuery, binding it to the type we would like to query. Here it's Item, so we'll pass it in constructor od CriteriaQuery and to Root. 

  CriteriaBuilder cb = em.getCriteriaBuilder();
  CriteriaQuery<Item> cq = cb.createQuery(Item.class);
  Root<Item> item = cq.from(Item.class);

The most important part is creating a list of Predicates. CriteriaQuery accepts array for configuring where clause, so it's important to build an array of predicates. Predicate represents one single where clause. In this example, we used LIKE - for name and EQUAL for code. Sometimes it could happen that ItemQueryCriteria does not contain all params, so we should assure that we didn't provide nulls to the predicate.

After that, we pass our predicate list to CriteriaQuery, converting it to an array with predicates.toArray(new Predicate[]{}).

  List<Predicate> predicates = new ArrayList<Predicate>();
  // checking if parameter name is provided, if yes, adding new predicate
  if (itemCriteria.getName() != null) {
	predicates.add(cb.like(item.get("name"), "%" + itemCriteria.getName() + "%"));
  }
  // checking if parameter code is provided, if yes, adding new predicate
  if (itemCriteria.getCode() != null) {
	predicates.add(cb.equal(item.get("code"), itemCriteria.getCode()));
     }
  cq.where(predicates.toArray(new Predicate[] {}));

In the end, we call the EntityManager to create typed query based on our CriteriaQuery.

  TypedQuery<Item> query = em.createQuery(cq);
  List<Item> items = query.getResultList();