The Java EE 6 Tutorial, Volume I

Chapter 22 Creating Queries Using the Criteria API

The Criteria API is used to define queries for entities and their persistent state by creating query-defining objects. Criteria queries are written using Java programming language APIs, are type-safe, and are portable queries that work regardless of the underlying data store.

Overview of the Criteria and Metamodel APIs

Similar to JPQL, the Criteria API is based on the abstract schema of persistent entities, their relationships, and embedded objects. The Criteria API operates on this abstract schema to allow developers to find, modify, and delete persistent entities by invoking Java Persistence API entity operations.

The Metamodel API works in concert with the Criteria API to model persistent entity classes for Criteria queries.

The Criteria API and JPQL are closely related, and designed to allow similar operations in their queries. Developers familiar with JPQL syntax will find equivalent object-level operations in the Criteria API.


Example 22–1 A Simple Criteria Query

The following simple Criteria query returns all instances of the Pet entity in the data source.

EntityManager em = ...;
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.select(pet);
TypeQuery<Pet> q = em.createQuery(cq);
List<Pet> allPets = q.getResultList();

The equivalent JPQL query is:

SELECT p
FROM Pet p

This query demonstrates the basic steps to create a Criteria query:

The tasks associated with each step are discussed in detail in this chapter.

To create a CriteriaBuilder instance call the getCriteriaBuilder method on the EntityManager instance:

CriteriaBuilder cb = em.getCriteriaBuilder();

The actual query object is created using the CriteriaBuilder instance:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);

The query will return instances of the Pet entity, so the type of the query is specified when the CriteriaQuery object is created to create a type-safe query.

The from clause of the query is set, and the root of the query specified, by calling the from method of the query object:

Root<Pet> pet = cq.from(Pet.class);

The select clause of the query is set by calling the select method of the query object, and passing in the query root:

cq.select(pet);

The query object is now used to create a TypedQuery<T> object that can be executed against the data source. The modifications to the query object are captured to create a ready-to-execute query.

TypeQuery<Pet> q = em.createQuery(cq);

This typed query object is executed by calling its getResultList method, because this query will return multiple entity instances. The results are stored in a List<Pet> collection-valued object.

List<Pet> allPets = q.getResultList();

Modeling Entity Classes with the Metamodel API

The Metamodel API is used to create a metamodel of the managed entities in a particular persistence unit. For each entity class in a particular package, a metamodel class is created with a trailing underscore, and with attributes that correspond to the persistent fields or properties of the entity class.


Example 22–2 Example Entity Class and Corresponding Metamodel Class

The following entity class com.example.Pet has four persistent fields, id, name, color, and owners.

package com.example;

...

@Entity
public class Pet {
  @Id
  protected Long id;
  protected String name;
  protected String color;
  @ManyToOne
  protected Set<Person> owners;
  ...
}

The corresponding Metamodel class is:

package com.example;

...

@Static Metamodel(Pet.class)
public class Pet_ {
  
  public static volatile SingularAttribute<Pet, Long> id;
  public static volatile SingularAttribute<Pet, String> name;
  public static volatile SingularAttribute<Pet, String> color;
  public static volatile SetAttribute<Pet, Person> owners;
}

The metamodel class and its attributes are used in Criteria queries to refer to the managed entity classes and their persistent state and relationships.

Using Metamodel Classes

Metamodel classes that correspond to entity classes are of type javax.persistence.metamodel.EntityType<T>, and are typically generated by annotation processors either at development time or at runtime. Developers of applications that use Criteria queries may generate static metamodel classes using the persistence provider's annotation processor, or may obtain the metamodel class either by calling the getModel method on the query root object or first obtaining an instance of the Metamodel interface and then passing the entity type to the instance's entity method.


Example 22–3 Obtaining a Metamodel Class Dynamically Using the Root<T>.getModel Method

The following code snippet shows how to obtain the Pet entity's metamodel class by calling Root<T>.getModel.

EntityManager em = ...;
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
EntityType<Pet> Pet_ = pet.getModel();


Example 22–4 Obtaining a Metamodel Class Dynamically Using the Metamodel.getMetamodel Method

The following code snippet shows how to obtain the Pet entity's metamodel class by first obtaining a metamodel instance using EntityManager.getMetamodel, and then calling entity on the metamodel instance.

EntityManager em = ...;
Metamodel m = em.getMetamodel();
EntityType<Pet> Pet_ = m.entity(Pet.class);

Basic Type-Safe Queries Using the Criteria API and Metamodel API

The basic semantics of a Criteria query consists of a select clause, a from clause, and an optional where clause, similar to a JPQL query. Criteria queries set these clauses using Java programming language objects, so the query can be created in a type-safe manner.

Creating a Criteria Query

The javax.persistence.criteria.CriteriaBuilder interface is used to construct:

To obtain an instance of the CriteriaBuilder interface, call the getCriteriaBuilder method on either an EntityManager or EntityManagerFactory instance.


Example 22–5 Obtaining a CriteriaBuilder Instance Using the EntityManager.getCriteriaManager Method

The following code shows how to obtain a CriteriaBuilder instance using the EntityManager.getCriteriaBuilder method.

EntityManager em = ...;
CriteriaBuilder cb = em.getCriteriaBuilder();

Criteria queries are constructed by obtaining an instance of the javax.persistence.criteria.CriteriaQuery interface. CriteriaQuery objects define a particular query that will navigate over one or more entities. Obtain CriteriaQuery instances by calling one of the CrtieriaBuilder.createQuery methods. For creating type-safe queries, call the CriteriaBuilder.createQuery method as follows:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);

The CriteriaQuery object's type should be set to the expected result type of the query. In the code above, the object's type is set to CriteriaQuery<Pet> for a query which will find instances of the Pet entity.


Example 22–6 Creating a CriteriaQuery Instance for a Query that Returns a String

In the following code snippet, a CriteriaQuery object is created for a query that returns a string.

CriteriaQuery<String> cq = cb.createQuery(String.class);

Query Roots

For a particular CriteriaQueryobject, the root entity of the query, from which all navigation originates, is called the query root. It is similar to the FROM clause in a JPQL query.

Create the query root by calling the from method on the CriteriaQuery instance. The argument to the from method is either the entity class, or an EntityType<T> instance for the entity.


Example 22–7 Setting the Query Root Using the Entity Class

The following code sets the query root to the Pet entity.

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);


Example 22–8 Setting the Query Root Using a Metamodel EntityType<T> Instance

The following code sets the query root to the Pet class using an EntityType<T> instance.

EntityManager em = ...;
Metamodel m = em.getMetamodel();
EntityType<Pet> Pet_ = m.entity(Pet.class);
Root<Pet> pet = cq.from(Pet_);

Criteria queries may have more than one query root. This usually occurs when the query navigates from several entities.


Example 22–9 Creating a Query with Multiple Query Roots

The following code has two Root instances.

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet1 = cq.from(Pet.class);
Root<Pet> pet2 = cq.from(Pet.class);

Querying Relationships Using Joins

For queries that navigate to related entity classes, the query must define a join to the related entity by calling one of the From.join methods on the query root object, or another join object. The join methods are similar to the JOIN keyword in JPQL.

The target of the join uses the Metamodel class of type EntityType<T> to specify the persistent field or property of the joined entity.

The join methods return an object of type Join<X, Y>, where X is the source entity and Y is the target of the join.


Example 22–10 Joining a Query

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Metamodel m = em.getMetamodel();
EntityType<Pet> Pet_ = m.entity(Pet.class);

Root<Pet> pet = cq.from(Pet.class);
Join<Pet, Owner> owner = pet.join(Pet_.owners);

Joins can be chained together to navigate to related entities of the target entity without having to create a Join<X, Y> instance for each join.


Example 22–11 Chaining Joins Together in a Query

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Metamodel m = em.getMetamodel();
EntityType<Pet> Pet_ = m.entity(Pet.class);
EntityType<Owner> Owner_ = m.entity(Owner.class);

Root<Pet> pet = cq.from(Pet.class);
Join<Owner, Address> address = cq.join(Pet_.owners).join(Owner_.addresses);

Path Navigation in Criteria Queries

Path objects are used in the select and where clauses of a Criteria query, and can be query root entities, join entities, or other Path objects. The Path.get method is used to navigate to attributes of the entities of a query.

The argument to the get method is the corresponding attribute of the entity's Metamodel class. The attribute can either be a single-valued attribute (specified by @SingularAttribute in the Metamodel class) or a collection-valued attribute (specified by one of @CollectionAttribute, @SetAttribute, @ListAttribute, or @MapAttribute).


Example 22–12 Using Path Objects in the Select Clause of a Query

CriteriaQuery<String> cq = cb.createQuery(String.class);
Metamodel m = em.getMetamodel();
EntityType<Pet> Pet_ = m.entity(Pet.class);

Root<Pet> pet = cq.from(Pet.class);
cq.select(pet.get(Pet_.name));

This query returns the names of all the pets in the data store. The get method is called on the query root, pet, with the name attribute of the Pet entity's Metamodel class, Pet_ as the argument.


Restricting Criteria Query Results

The results of a query can be restricted on the CriteriaQuery object according to conditions set by calling the CriteriaQuery.where method. Calling the where method is analogous to setting the WHERE clause in a JPQL query.

The where method evaluates instances of the Expression interface to restrict the results according to the conditions of the expressions. Expression instances are created using methods defined in the Expression and CriteriaBuilder interfaces.

The Expression Interface Methods

An Expression object is used to in a query's select, where, or having clause

Table 22–1 Conditional Methods in the Expression Interface

Method 

Description 

isNull

Tests whether an expression is null. 

isNotNull

Tests whether an expression is not null. 

in

Tests whether an expression is within a list of values. 


Example 22–13 Using the Expression.isNull Method

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Metamodel m = em.getMetamodel();
EntityType<Pet> Pet_ = m.entity(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.where(pet.get(Pet_.color).isNull());

This query finds all pets where the color attribute is null.



Example 22–14 Using the Expression.in Method

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Metamodel m = em.getMetamodel();
EntityType<Pet> Pet_ = m.entity(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.where(pet.get(Pet_.color).in("brown", "black");

This query finds all brown and black pets.


The in method also can check whether an attribute is a member of a collection.

Expression Methods in the CriteriaBuilder Interface

The CriteriaBuilder interface defines additional methods for creating expressions. These methods correspond to the arithmetic, string, date, time, and case operators and functions of JPQL.

Table 22–2 Conditional Methods in the CriteriaBuilder Interface

Conditional Method 

Description 

equal

Tests whether two expressions are equal. 

notEqual

Tests whether two expressions are not equal. 

gt

Tests whether the first numeric expression is greater than the second numeric expression. 

ge

Tests whether the first numeric expression is greater than or equal to the second numeric expression. 

lt

Tests whether the first numeric expression is less than the second numeric expression. 

le

Tests whether the first numeric expression is less than or equal to the second numeric expression. 

between

Tests whether the first expression is between the second and third expression in value. 

like

Tests whether the expression matches a given pattern. 


Example 22–15 Using the CriteriaBuilder.equal Method

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Metamodel m = em.getMetamodel();
EntityType<Pet> Pet_ = m.entity(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.where(cb.equal(pet.get(Pet_.name)), "Fido");
...


Example 22–16 Using the CriteriaBuilder.gt Method

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Metamodel m = em.getMetamodel();
EntityType<Pet> Pet_ = m.entity(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
Date someDate = new Date(...);
cq.where(cb.gt(pet.get(Pet_.birthday)), date);


Example 22–17 Using the CriteriaBuilder.between Method

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Metamodel m = em.getMetamodel();
EntityType<Pet> Pet_ = m.entity(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
Date firstDate = new Date(...);
Date secondDate = new Date(...);
cq.where(cb.between(pet.get(Pet_.birthday)), firstDate, secondDate);


Example 22–18 Using the CriteriaBuilder.like Method

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Metamodel m = em.getMetamodel();
EntityType<Pet> Pet_ = m.entity(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.where(cb.like(pet.get(Pet_.name)), "*do");

Multiple conditional predicates can be specified by using the compound predicate methods of the CriteriaBuilder interface.

Table 22–3 Compound Predicate Methods in the CriteriaBuilder Interface

Method 

Description 

and

A logical conjunction of two boolean expressions. 

or

A logical disjunction of two boolean expressions. 

not

A logical negation of the given boolean expression. 


Example 22–19 Using Compound Predicates in Queries

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Metamodel m = em.getMetamodel();
EntityType<Pet> Pet_ = m.entity(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.where(cb.equal(pet.get(Pet_.name), "Fido")
    .and(cb.equal(pet.get(Pet_.color), "brown");

Managing Criteria Query Results

For queries that return more than one result, it's often helpful to organize those results. The CriteriaQuery interface defines the orderBymethod to order query results according to attributes of an entity. The CriteriaQuery interface also defines the groupBy method to group the results of a query together according to attributes of an entity, and the and having method to restrict those groups according to a condition.

Ordering Results

The order of the results of a query can be set by calling the CriteriaQuery.orderBy method and passing in an Order object. Order objects are created by calling either the CriteriaBuilder.asc or CriteriaBuilder.desc methods. The asc method is used to order the results by ascending value of the passed expression parameter. The desc method is used to order the results by descending value of the passed expression parameter.


Example 22–20 Ordering Results in Descending Order

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.select(pet);
cq.orderBy(cb.desc(pet.get(Pet_.birthday));

In this query, the results will be ordered by the pet's birthday from highest to lowest. That is, pets born in December will appear before pets born in May.



Example 22–21 Ordering Results in Ascending Order

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
Join<Owner, Address> address = cq.join(Pet_.owners).join(Owner_.address);
cq.select(pet);
cq.orderBy(cb.asc(address.get(Address_.postalCode));

In this query, the results will be ordered by the pet owner's postal code from lowest to highest. That is, pets whose owner lives in the 10001 zip code will appear before pets whose owner lives in the 91000 zip code.


If more than one Order object is passed to orderBy, the precedence is determined by the order in which they appear in the argument list of orderBy. The first Order object has precedence.


Example 22–22 Ordering Results By More than One Criteria

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
Join<Pet, Owner> owner = cq.join(Pet_.owners);
cq.select(pet);
cq.orderBy(cb.asc(owner.get(Owner_.lastName), owner.get(Owner_.firstName));

The results of this query will be ordered alphabetically by the pet owner's last name, then first name.


Grouping Results

The CriteriaQuery.groupBy method partitions the query results into groups. These groups are set by passing an expression to groupBy.


Example 22–23 Grouping Results Using the CriteriaQuery.groupBy Method

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.groupBy(pet.get(Pet_.color));

This query returns all Pet entities, and groups the results by pet's color.


The CriteriaQuery.having method is used in conjunction with groupBy to filter over the groups. The having method takes a conditional expression as a parameter. By calling the having method, the query result is restricted according to the conditional expression.


Example 22–24 Grouping Results Using the CriteriaQuery.groupBy and CriteriaQuery.having Methods

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.groupBy(pet.get(Pet_.color));
cq.having(cb.in(pet.get(Pet_.color)).value("brown").value("blonde");

In this example, the query groups the returned Pet entities by color, as in the example above. However, the only returned groups will be Pet entities where the color attribute is set to brown or blonde. That is, no grey colored pets will be returned in this query.


Executing Queries

To prepare a query for execution, create a TypedQuery<T> object with the type of the query result by passing the CriteriaQuery object to EntityManager.createQuery.

Queries are executed by calling either getSingleResult or getResultList on the TypedQuery<T> object.

Single-Valued Query Results

The TypedQuery<T>.getSingleResult method is used for executing queries that return a single result.


Example 22–25 Retrieving Single-Valued Query Result

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
...
TypedQuery<Pet> q = em.createQuery(cq);
Pet result = q.getSingleResult();

Collection-Valued Query Results

The TypedQuery<T>.getResultList method is used for executing queries that return a collection of objects.


Example 22–26 Retrieving Collection-Valued Query Results

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
...
TypedQuery<Pet> q = em.createQuery(cq);
List<Pet> results = q.getResultList();