A Look at JPA 2.0 Criteria API

Java Persistence API (JPA) provides an abstraction above JDBC that makes the life of the developer easy to perform Object Relational Mapping (ORM). With the introduction of Java EE6, the new JPA 2.0 provides several enhancements to the previous JPA specification. The enhancements include the enhancements to the JPQL, controlling the L2 cache, new locking modes, and several standardized properties.

Criteria API is introduced with JPA 2.0 which allows queries to be created in a flexible and an object oriented way. Criteria API prevents developers to create syntactically incorrect JPQL queries. The problems in the queries are detected at compile time rather than at runtime which makes the development process less painful. The main classes used to create queries are the CriteriaQuery class which is used to specify clauses like FROM, WHERE, ORDER BY, and HAVING, and the TypedQuery class which is used to execute type-safe queries to the database.

To give a concrete example, assume that we have a Person entity with attributes id, name, ssn, and age. To retrieve all Person entities from the database:
EntityManager em = emf.createEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(Person.class);
TypedQuery q = em.createQuery(cq);
List results = q.getResultList();

In this example, first an entity manager is created which is a session used to interact with the database. Then a criteria query that does not specify any predicates is created, and hence all Person entities are retrieved from the database as a result of the getResultList method call.

To retrieve a Person with a specified SSN we need to restrict the results. To this end, we need to specify the attribute and its corresponding value to filter the results.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(Person.class);
Root personEntity = cq.from(Person.class);
Path ssnAttr = personEntity.get("ssn");
cq.where(cb.equal(ssnAttr, ssn));
TypedQuery q = em.createQuery(cq);
Person p = q.getSingleResult();

With the where and equal methods of the CriteriaQuery class we specify that the ssn attribute of the Person entity is equal to the specified value. As a result, the JPA implementation executes the following SQL:

SELECT ID, NAME, AGE, SSN FROM PERSON WHERE (SSN = ?)

and binds the specified SSN value to the SSN parameter in the SQL.

Similarly to retrieve Person entities who are older than some specified age:
Path ageAttr = personEntity.get("age");
cq.where(cb.gt(ageAttr, parseInt));
TypedQuery q = em.createQuery(cq);
List results = q.getResultList();

In the above example, the gt method specifies that the attribute value is greater than the specified value. As a results the following SQL is executed:

SELECT ID, NAME, AGE, SSN FROM PERSON WHERE (AGE > ?)

and the specified age value is bound to the parameter value.

It is also possible to specify functional expressions with the Criteria API. To retrieve the average age of all Person entities:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(Person.class);
Root personEntity = cq.from(Person.class);
CriteriaQuery c = cb.createQuery(Double.class);
Path ageAttr = personEntity.get("age");
Double averageAge = em.createQuery(c.select(cb.avg(ageAttr))).getSingleResult();

The avg method applies the average function to the age attribute of the Person entities resulting in the query:
SELECT AVG(AGE) FROM PERSON

Finally it is possible to create even more complex queries with the Criteria API:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(Person.class);
Root personEntity = cq.from(Person.class);
Path ageAttr = personEntity.get("age");
cq.where(cb.and(cb.greaterThan(ageAttr, 5),
           cb.lessThan(ageAttr, 15)));
TypedQuery q = em.createQuery(cq);
List results = q.getResultList();

The and method of the CriteriaBuilder class is used to apply greaterThan and lessThan predicates to the age attribute of the Person entity to retrieve Person entities who have an age between 5 and 15. The following SQL is executed against the database:

SELECT ID, NAME, AGE, SSN FROM PERSON WHERE ((AGE > ?) AND (AGE < ?))

These examples demonstrate the power of the new Criteria API to express predicates and expressions in a flexible object oriented way. However the API is much more complicated and provides several advanced features. I recommend you to check [1] and [2] for the technical details of the API.

You can find the source code here. The examples are tested on Glassfish v3 using the EclipseLink JPA provider.

References:

  1. OpenJpa Criteria API Javadoc
  2. Java Persistence 2.0 Proposed Final Draft
  • Share/Bookmark

About this entry