When JPA 2 was released in 2009 it included the new criteria API. The purpose of the API was to get away from using JQL strings , (JPA Query Language), in your code. Although JQL seems like a great way to leverage your existing SQL knowledge ,in the OO world it has a major drawback namely; there is no compile time checking of your query strings. The first time you find out about a spelling or syntactical error in your query string is at run time. This can be quiet a productivity drain with developers having to correct, compile and redeploy to continue.

Unit testing your code goes some way to addressing this problem but one area that cannot be addressed by unit tests is refactoring. Most refactoring tools battle with strings and you are stuck with rerunning unit tests and correcting each string that slip through the manual changes on each iteration of the test until all is well. Now with the JPA criteria API it's possible to have type safe queries that are checked at compile time and refactoring is much more efficient!

JPA Criteria API

There is a price to pay for this static checking though. First you need to generate a set of meta model classes, more on what these are is given below, that describe the fields of your entities; luckily this is easily achieved by placing a step in your maven build process. The second price you pay is verbosity and a less than intuitive-at-first-glance api.

Generating the JPA Criteria Meta Model Classes

To generate the meta model classes during your build process add the following plugin details to your maven pom.xml. In my case, because I have a different persistence unit for our unit tests I had to specify which persistence unit I wanted built, otherwise a bug causes the plugin to throw an error when it finds an entity listed in two persistence units. (i.e the production config and test unit config). I make use of Eclipselink in the pom.xml below.

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-source-plugin</artifactId>

</plugin>

<plugin>

<groupId>org.bsc.maven</groupId>

<artifactId>maven-processor-plugin</artifactId>

<version>1.3.5</version>

<executions>

<execution>

<id>process</id>

<goals>

<goal>process</goal>

</goals>

<phase>generate-sources</phase>

<configuration>

<compilerArguments>-Aeclipselink.persistencexml=src/main/resources/META-INF/persistence.xml -Aeclipselink.persistenceunits=mypu</compilerArguments>

<processors>

<processor>org.eclipse.persistence.internal.jpa.modelgen.CanonicalModelProcessor</processor>

</processors>

</configuration>

</execution>

</executions>

</plugin>

Now, at compile time, the set of meta model classes should be automatically generated for you.

How to use the JPA Critiera API

The criteria api can seem quiet daunting at first but it's not that bad once you grok its basic design approach. There are two main object that you will use to create your query, namely the CriteriaBuilder object and a CriteriaQuery object. The first step is to get a handle to a CriteriaBuilder object and then create a CriteriaQuery object. This is is done with the following boiler plate code, where em is an EntityManager object.

CriteriaBuilder cb = em.getCriteriaBuilder();

CriteriaQuery cqry = em.createQuery();

From the CriteriaQuery object you will create the four "components" of a query namely:

Criteria Components Component Description How to create Select The objects or object fields you wish to return. Pretty much like the select part of a JQL query. You can do aggregations here too and return fields from objects but in this brief tutorial we will just select objects. CriteriaQuery.select From This is where we stipulate which entity (table) we are quering. You can also follow relationships to other entities that are part of the root entity i.e "joins" and also subSelects. CriteriaQuery.from Where This is the where you stipulate the criteria you wish to apply to the entities that you are selecting. You create "Predicates" which are used to build up the "where" clause. CriteriaQuery.where CriteriaBuilder.equals

CriteriaBuilder.lessThanOEquals

etc. Order By If you wish to stipulate the order object should be returned in, you do it here CriteriaQuery.orderBy Group By For aggregations you stipulate the object fields or objects here. CriteriaQuery.groupBy

In practise nearly every method from the CriteriaQuery and CriteriaBuilder takes an object that implements the interface java.persistence.criteria.Expression, Selection or Predicate, which can be very unhelpful when deciding what to send into the method for a beginner; especially since a lot of the objects implement both interfaces and the Expression interface inherits from the Selection interface! (Does that make your brain hurt? I am sure the API desinger must have had an anuerism).

It really doesn't make sense that a Path object can be sent to the where method. But it best not to pay too much attention to these high level interfaces and understand the process of creating a CritieriaQuery, at least in the beginning.

The simplest approach to understanding the API is to separate out the task of creating a query into the following steps:

Create your CriteriaBuilder and CriteriaQuery objects, Setup your from clause, Setup your select clause, Setup your criterias or predicates Setup the where clause using your predicates Execute the query.

A Simple CriteriaQuery Example

Lets assume we have a simple object called MyEntity which has the following fields:

dateCreated - Date, and

age - Integer

Here is a simple CriteiraQuery example.

//Boilerplate

CriteriaBuilder cb = em.getCriteriaBuilder(); //Step 1

CriteriaQuery cqry = em.createQuery(); //Step 1

//Interesting stuff happens here

Root<MyEntity> root = cqry.from(MyEntity.class); //Step 2

cqry.select(root); Step 3

//Boilerplate code

Query qry = em.createQuery(cqry); //Step 6

List<MyEnity> results = qry.getResultList(); //Step 6



So we have two boilerplate sections that you need to type up, section 1 and section 6. Section 1 creates the criteria objects you need and the section 6 , at the end, executes the query.

The main work happens in the middle, steps 2 - 5, where you construct your query. The simple example above will return all entities in the table mapped to the MyEntity class. We had to tell the query which entity we were selecting from with the "from" method, and then what we wanted returned from the query with the "select" method.

Using our step-by-step approach we can begin to build more complicated queries. Lets say we want to use some criteria in the query. To do this we need to populate the "where" method of the CriteiraQuery with "Predicate" objects.

A predicate object is usually created by using one of the methods on the CritieraBuilder class, although the Expression interface also creates some Predicates, but for now just think of using the CriteriaBuilder class for this task. So lets say we want to find all MyEntity objects with age > 10.

Root<MyEntity> root = cqry.from(MyEntity.class); //Step 2

cqry.select(root); //Step 3

Predicate pGtAge = cb.gt(root.get("age"),10); //Step 4

cqry.where(pGtAge); //Step 5

When creating a predicate we have to tell the API which field of which object we are comparing against. We may have more than one entity that we are quering across, as with a join for example, so you need a reference to the entity being queried.

The object returned by the "from" method above is what we use here. We then use the "get" method to stipulate which field from the entity object we want to compare against. Yes. the API is sadly very verbose.

If we wanted to string more than one criteria together , lets say age >10 and dateCreated>"2011-07-01" then we would create two Predicates and "and" them as follows:

//assume we have created a date object for 2011-07-01

//called date

Root<MyEnity> root = cqry.from(MyEntity.class); //Step 2 cqry.select(root); //Step 3

Predicate pGtAge = cb.gt(root.get("age"),10); //Step 4

Predicate pGtDateCreated= cb.greaterThan(root.get("dateCreated"),date); //Step 4

Predicate pAnd = cb.and(pGtDateCreated,pGtAge); //Step 4

cqry.where(pAnd); //Step 5 Using Meta Model Classes in Query

Using the many methods on CriteriaBuilder you can build up sophisticated "where" clauses. Use the autocomplete on your IDE to see what methods are available or check out the API docs.

You may be wondering why we are using strings in our "Predicate" objects. Afterall doesn't this defeat the purpose of not using JQL? Are CriteriaQueries subject to the same failings then as JQL queries that we setout in the introduction?

The answer to that question is to use the Meta Model Classes that we created in the beginning of this tutorial. Once the classes have been generated you can refer to fields in Entity objects using the meta model classes. The meta model class has the same name as your Entity class with an underscore(_) appeneded. So to say we are comparing against the age field we would use MyEntity_.age. The query above is rewritten below using the meta model classes.

//assume we have created a date object for 2011-07-01 //called date Root<MyEnity> root = cqry.from(MyEntity.class); //Step 2 cqry.select(root); //Step 3

Predicate pGtAge = cb.gt(root.get(MyEntity_.age),10); //Step 4 Predicate pGtDateCreated= cb.greaterThan(root.get(MyEntity_.dateCreated),date); //Step 4 Predicate pAnd = cb.and(pGtDateCreated,pGtAge); //Step 4 cqry.where(pAnd); //Step 5

Criteria Query Using Joins

So what is we want to query across entities i.e do a "join" query? Lets say we have another Entity object called AnotherEntity with fields as follows:

name - String

enabled - boolean

In addition we extend the MyEntity object to have a reference to AnotherEntity object. So MyEntity becomes:

dateCreated - Date,

age - Integer and

anotherEntity - AnotherEntity

Now lets say we want to query for all MyEntity objects that have an AnotherEntity object which are disabled. We would do this as follows:

Root<MyEnity> root = cqry.from(MyEntity.class); //Step 2 Join<MyEntity,AnotherEntity> join = root.join(MyEntity_.anotherEntity); //Step 2 //Join<MyEntity,AnotherEntity> join = root.join("anotherEntity"); //Step 2

cqry.select(root); //Step 3 Predicate pGtAge = cb.gt(root.get(MyEntity_.age),10); //Step 4 Predicate pGtDateCreated= cb.greaterThan(root.get(MyEntity_.dateCreated),date); //Step 4 Predicate pEqEnabled = cb.equals(join.get(AnotherEntity_.enabled),false); Predicate pAnd = cb.and(pGtDateCreated,pGtAge,pEqEnabled); //Step 4

cqry.where(pAnd); //Step 5

You can see that our form step is getting more complex as we stipulate what to join on. As with the Predicates one can use string or the meta model classses to stipulate the desired field to join on. If you going through the hassle of using the criteria API then there really is no point in using strings!

More Complex Select Clause

We will now look at more complex select clauses, our step 2. Lets say we are only interested in the dateCreated field of our MyEntity object. Our code would look something like:

Root<MyEnity> root = cqry.from(MyEntity.class); //Step 2 cqry.select(root.get(MyEntity_.dateCreated)); //Step 3 If we wanted to use an aggregate function and get the minimum dateCreated we could use something like:

Root<MyEnity> root = cqry.from(MyEntity.class); //Step 2 Expression min = cb.min(root.get(MyEntity_.dateCreated));//Step3 cqry.select(min); //Step 3