Chapter 11.  JPA Criteria

Table of Contents

1. Constructing a QueryDefinition
2. Executing a QueryDefinition
3. Extension to Criteria API

JPA 2.0 Specification introduces a new API to define queries dynamically via construction of an object-based javax.persistence.QueryDefinition instance, rather than string-based approach used in JPQL (Java Persistence Query Language). This dynamic query definition capability, referred as Criteria API, is based on the abstract persistent schema of the entities, their embedded objects and their relationships. The syntax is designed to construct a Query Tree whose nodes represent the semantic query elements such as projections, conditional predicates of WHERE clause or GROUP BY elements etc.

1. Constructing a QueryDefinition

The QueryBuilder interface is the factory for QueryDefinition. A QueryBuilder is obtained from either the EntityManagerFactory or the EntityManager as follows:

EntityManager em = ... ; 
QueryBuilder queryBuilder = em.getQueryBuilder();
QueryDefinition qdef = queryBuilder.createQueryDefinition();
    	

The first step in constructing a query definition is specification of query roots. Query roots specify the domain objects on which the query is evaluated. Query root is an instance of the DomainObject interface. A query root is added to a QueryDefinition by addRoot(Class c) method.

    	DomainObject customer = qdef.addRoot(Customer.class);
    	

Often a query definition has a single root, so the QueryBuilder interface allows to construct and add a root via a single method.

DomainObject customer = queryBuilder.createQueryDefinition(Customer.class);    	
    	

A query domain can be further refined by joining to other domain objects. For example, for the above query definition to operate over customers and their orders, use join(String attribute):

DomainObject order = customer.join("orders");
		

The condition of a query definition is set via where(Predicate p) where the argument designates a conditional predicate. Conditional predicates are often composed of one or more comparisons between the attribute values of the domain objects and some variable. For example, to select the Customers whose name is John Doe and has orders that are not yet delivered, you can build the predicate and set it to the query definition as:

qdef.where(customer.get("name").equal("John Doe")
      .and(order.get("status").equal(OrderStatus.DELIVERED).not()));
		

The select() method defines the result of the query. If left unspecified, the select projection is assumed to be the root domain object. However, you can specify the selected projections explicitly as a list:

qdef.select(customer.get("name"), order.get("status"));
		

An attribute of a domain object is specified by navigating via get(String attr). The attribute should refer to a valid persistent property of the receiving domain object, however no such validation is enforced during the construction of the query definition. All validation is deferred until the query is actually executed.