JPA 2 Criteria API Tutorial

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
ComponentDescriptionHow to create
SelectThe 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
FromThis 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
WhereThis 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 ByIf you wish to stipulate the order object should be returned in, you do it hereCriteriaQuery.orderBy
Group ByFor 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:

  1. Create your CriteriaBuilder and CriteriaQuery objects,
  2. Setup your from clause,
  3. Setup your select clause,
  4. Setup your criterias or predicates
  5. Setup the where clause using your predicates
  6. 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
 
There are method for returning array of objects or tuples from a query and also a way to create new object on the fly from fields or queries entities. I may cover these in a future tutorial but for now this should be enough to get you going on the Criteria API.
 
As you start using the Criteria API from JPA2 you will see that there are other ways of creating or manipulating some of the "components" we set out above other than following the step-by-step approach, but if you ever get lost just follow the steps. 
 

Comments

Very good post. I'm using JPA2 with metamodel classes and it's very usefull, but there is a issue very critic https://hibernate.onjira.com//browse/HHH-5024

A workaround for this problem is use a String in our "Predicate" objects.

Congrats for the post.

Thank you very much. THis helped me a lot. It solved several issues I had with JPA criteria queries. May all your hopes come true as you help others in this way. Thanks again.

This is quite a comprehensive tutorial. Was of a much help to me
10X

I was utterly mystified by the criteria api. You broke it into very simple steps. Exactly what I needed. Much appreciated!

Nice tutorial. I´ve tried Hibernate Criteria, Query DSL and JPA type safe queries and I´ve ended using JPQL, is the fastest an easiest way for building complex searches and they can be understood by everyone including "novice" developers. Obviously you have to pay a price, but IMHO it worths the cost.

Nice Tutorial! Helped me a lot. Such good examples are rare to find!

I used the Hibernate jpa model generation with maven.


<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<compilerArguments>
<processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
</compilerArguments>
</configuration>
</plugin>

and as dependency


<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
<version>1.3.0.Final</version>
</dependency>

Great work Mark. Even if this article was written in the year 2011, it is still so very helpful to many people like me who wants a brief and concise explanation of CriteriaBuilder/CriteriaQuery, supported with short, simple, to the point practical examples, which many of the articles on the internet are totally lacking.

You've filled the void and bridged the gap. Thank you for the beautiful effort.