Hibernate.orgCommunity Documentation
Table of Contents
Criteria queries offer a type-safe alternative to HQL, JPQL and native-sql queries.
Hibernate offers an older, legacy org.hibernate.Criteria
API which should be
considered deprecated. No feature development will target those APIs. Eventually, Hibernate-specific
criteria features will be ported as extensions to the JPA
javax.persistence.criteria.CriteriaQuery
. For details on the
org.hibernate.Criteria
API, see ???.
This chapter will focus on the JPA APIs for declaring type-safe criteria queries.
Criteria queries are a programmatic, type-safe way to express a query. They are type-safe in terms of
using interfaces and classes to represent various structural parts of a query such as the query itself,
or the select clause, or an order-by, etc. They can also be type-safe in terms of referencing attributes
as we will see in a bit. Users of the older Hibernate org.hibernate.Criteria
query API will recognize the general approach, though we believe the JPA API to be superior
as it represents a clean look at the lessons learned from that API.
Criteria queries are essentially an object graph, where each part of the graph represents an increasing
(as we navigate down this graph) more atomic part of query. The first step in performing a criteria query
is building this graph. The javax.persistence.criteria.CriteriaBuilder
interface is the first thing with which you need to become acquainted to begin using criteria queries. Its
role is that of a factory for all the individual pieces of the criteria. You obtain a
javax.persistence.criteria.CriteriaBuilder
instance by calling the
getCriteriaBuilder
method of either
javax.persistence.EntityManagerFactory
or
javax.persistence.EntityManager
.
The next step is to obtain a javax.persistence.criteria.CriteriaQuery
. This
is accomplished using one of the 3 methods on
javax.persistence.criteria.CriteriaBuilder
for this purpose:
<T> CriteriaQuery<T> createQuery(Class<T> resultClass); CriteriaQuery<Tuple> createTupleQuery(); CriteriaQuery<Object> createQuery();
Each serves a different purpose depending on the expected type of the query results.
Chapter 6 Criteria API of the JPA Specification already contains a decent amount of reference material pertaining to the various parts of a criteria query. So rather than duplicate all that content here, lets instead look at some of the more widely anticipated usages of the API.
The type of the criteria query (aka the <T>) indicates the expected types in the query result. This might be an entity, an Integer, or any other object.
This is probably the most common form of query. The application wants to select entity instances.
Example 12.1. Selecting the root entity
CriteriaQuery<Person> criteria = builder.createQuery( Person.class ); Root<Person> personRoot = criteria.from( Person.class ); criteria.select( personRoot ); criteria.where( builder.equal( personRoot.get( Person_.eyeColor ), "brown" ) ); List<Person> people = em.createQuery( criteria ).getResultList(); for ( Person person : people ) { ... }
The example uses createQuery
passing in the Person
class reference as the results of the query will be Person objects.
The call to the CriteriaQuery.select
method in this example is
unnecessary because personRoot will be the implied selection since we
have only a single query root. It was done here only for completeness of an example.
The Person_.eyeColor reference is an example of the static form of JPA metamodel reference. We will use that form exclusively in this chapter. See the documentation for the Hibernate JPA Metamodel Generator for additional details on the JPA static metamodel.
The simplest form of selecting an expression is selecting a particular attribute from an entity. But this expression might also represent an aggregation, a mathematical operation, etc.
Example 12.2. Selecting an attribute
CriteriaQuery<Integer> criteria = builder.createQuery( Integer.class ); Root<Person> personRoot = criteria.from( Person.class ); criteria.select( personRoot.get( Person_.age ) ); criteria.where( builder.equal( personRoot.get( Person_.eyeColor ), "brown" ) ); List<Integer> ages = em.createQuery( criteria ).getResultList(); for ( Integer age : ages ) { ... }
In this example, the query is typed as java.lang.Integer
because that
is the anticipated type of the results (the type of the Person#age
attribute
is java.lang.Integer
). Because a query might contain multiple references to
the Person entity, attribute references always need to be qualified. This is accomplished by the
Root#get
method call.
There are actually a few different ways to select multiple values using criteria queries. We will explore 2 options here, but an alternative recommended approach is to use tuples as described in Section 12.2, “Tuple criteria queries”. Or consider a wrapper query; see Section 12.1.4, “Selecting a wrapper” for details.
Example 12.3. Selecting an array
CriteriaQuery<Object[]> criteria = builder.createQuery( Object[].class ); Root<Person> personRoot = criteria.from( Person.class ); Path<Long> idPath = personRoot.get( Person_.id ); Path<Integer> agePath = personRoot.get( Person_.age ); criteria.select( builder.array( idPath, agePath ) ); criteria.where( builder.equal( personRoot.get( Person_.eyeColor ), "brown" ) ); List<Object[]> valueArray = em.createQuery( criteria ).getResultList(); for ( Object[] values : valueArray ) { final Long id = (Long) values[0]; final Integer age = (Integer) values[1]; ... }
Technically this is classified as a typed query, but you can see from handling the results that this is sort of misleading. Anyway, the expected result type here is an array.
The example then uses the array
method of
javax.persistence.criteria.CriteriaBuilder
which explicitly
combines individual selections into a
javax.persistence.criteria.CompoundSelection
.
Example 12.4. Selecting an array (2)
CriteriaQuery<Object[]> criteria = builder.createQuery( Object[].class ); Root<Person> personRoot = criteria.from( Person.class ); Path<Long> idPath = personRoot.get( Person_.id ); Path<Integer> agePath = personRoot.get( Person_.age ); criteria.multiselect( idPath, agePath ); criteria.where( builder.equal( personRoot.get( Person_.eyeColor ), "brown" ) ); List<Object[]> valueArray = em.createQuery( criteria ).getResultList(); for ( Object[] values : valueArray ) { final Long id = (Long) values[0]; final Integer age = (Integer) values[1]; ... }
Just as we saw in Example 12.3, “Selecting an array” we have a typed criteria
query returning an Object array. Both queries are functionally equivalent. This second example
uses the multiselect
method which behaves slightly differently based on
the type given when the criteria query was first built, but in this case it says to select and
return an Object[].
Another alternative to Section 12.1.3, “Selecting multiple values” is to instead select an object that will “wrap” the multiple values. Going back to the example query there, rather than returning an array of [Person#id, Person#age] instead declare a class that holds these values and instead return that.
Example 12.5. Selecting an wrapper
public class PersonWrapper { private final Long id; private final Integer age; public PersonWrapper(Long id, Integer age) { this.id = id; this.age = age; } ... } ... CriteriaQuery<PersonWrapper> criteria = builder.createQuery( PersonWrapper.class ); Root<Person> personRoot = criteria.from( Person.class ); criteria.select( builder.construct( PersonWrapper.class, personRoot.get( Person_.id ), personRoot.get( Person_.age ) ) ); criteria.where( builder.equal( personRoot.get( Person_.eyeColor ), "brown" ) ); List<PersonWrapper> people = em.createQuery( criteria ).getResultList(); for ( PersonWrapper person : people ) { ... }
First we see the simple definition of the wrapper object we will be using to wrap our result
values. Specifically notice the constructor and its argument types. Since we will be returning
PersonWrapper
objects, we use PersonWrapper
as the
type of our criteria query.
This example illustrates the use of the
javax.persistence.criteria.CriteriaBuilder
method
construct
which is used to build a wrapper expression. For every row in the
result we are saying we would like a PersonWrapper instantiated with
the remaining arguments by the matching constructor. This wrapper expression is then passed as
the select.
A better approach to Section 12.1.3, “Selecting multiple values” is to use either a
wrapper (which we just saw in Section 12.1.4, “Selecting a wrapper”) or using the
javax.persistence.Tuple
contract.
Example 12.6. Selecting a tuple
CriteriaQuery<Tuple> criteria = builder.createTupleQuery(); Root<Person> personRoot = criteria.from( Person.class ); Path<Long> idPath = personRoot.get( Person_.id ); Path<Integer> agePath = personRoot.get( Person_.age ); criteria.multiselect( idPath, agePath ); criteria.where( builder.equal( personRoot.get( Person_.eyeColor ), "brown" ) ); List<Tuple> tuples = em.createQuery( criteria ).getResultList(); for ( Tuple tuple : valueArray ) { assert tuple.get( 0 ) == tuple.get( idPath ); assert tuple.get( 1 ) == tuple.get( agePath ); ... }
This example illustrates accessing the query results through the
javax.persistence.Tuple
interface. The example uses the explicit
createTupleQuery
of
javax.persistence.criteria.CriteriaBuilder
. An alternate approach
is to use createQuery
passing Tuple.class
.
Again we see the use of the multiselect
method, just like in
Example 12.4, “Selecting an array (2)”. The difference here is that the type of the
javax.persistence.criteria.CriteriaQuery
was defined as
javax.persistence.Tuple
so the compound selections in this case are
interpreted to be the tuple elements.
The javax.persistence.Tuple
contract provides 3 forms of access to
the underlying elements:
The Example 12.6, “Selecting a tuple” example illustrates this form of access
in the tuple.get( idPath )
and tuple.get( agePath )
calls.
This allows typed access to the underlying tuple values based on the
javax.persistence.TupleElement
expressions used to build
the criteria.
Allows access to the underlying tuple values based on the position. The simple Object get(int position) form is very similar to the access illustrated in Example 12.3, “Selecting an array” and Example 12.4, “Selecting an array (2)”. The <X> X get(int position, Class<X> type form allows typed positional access, but based on the explicitly supplied type which the tuple value must be type-assignable to.
Allows access to the underlying tuple values based an (optionally) assigned alias. The
example query did not apply an alias. An alias would be applied via the
alias
method on
javax.persistence.criteria.Selection
. Just like
positional
access, there is both a typed
(Object get(String alias)) and an untyped
(<X> X get(String alias, Class<X> type form.
A CriteriaQuery object defines a query over one or more entity, embeddable, or basic abstract schema types. The root objects of the query are entities, from which the other types are reached by navigation. | ||
--JPA Specification, section 6.5.2 Query Roots, pg 262 |
All the individual parts of the FROM clause (roots, joins, paths) implement the
javax.persistence.criteria.From
interface.
Roots define the basis from which all joins, paths and attributes are available in the query.
A root is always an entity type. Roots are defined and added to the criteria by the overloaded
from
methods on
javax.persistence.criteria.CriteriaQuery
:
<X> Root<X> from(Class<X>); <X> Root<X> from(EntityType<X>)
Example 12.7. Adding a root
CriteriaQuery<Person> personCriteria = builder.createQuery( Person.class ); // create and add the root person.from( Person.class );
Criteria queries may define multiple roots, the effect of which is to create a cartesian product between the newly added root and the others. Here is an example matching all single men and all single women:
Example 12.8. Adding multiple roots
CriteriaQuery query = builder.createQuery(); Root<Person> men = query.from( Person.class ); Root<Person> women = query.from( Person.class ); Predicate menRestriction = builder.and( builder.equal( men.get( Person_.gender ), Gender.MALE ), builder.equal( men.get( Person_.relationshipStatus ), RelationshipStatus.SINGLE ) ); Predicate womenRestriction = builder.and( builder.equal( women.get( Person_.gender ), Gender.FEMALE ), builder.equal( women.get( Person_.relationshipStatus ), RelationshipStatus.SINGLE ) ); query.where( builder.and( menRestriction, womenRestriction ) );
Joins allow navigation from other javax.persistence.criteria.From
to either association or embedded attributes. Joins are created by the numerous overloaded
join
methods of the
javax.persistence.criteria.From
interface
Example 12.9. Example with Embedded and ManyToOne
CriteriaQuery<Person> personCriteria = builder.createQuery( Person.class ); Root<Person> personRoot = person.from( Person.class ); // Person.address is an embedded attribute Join<Person,Address> personAddress = personRoot.join( Person_.address ); // Address.country is a ManyToOne Join<Address,Country> addressCountry = personAddress.join( Address_.country );
Example 12.10. Example with Collections
CriteriaQuery<Person> personCriteria = builder.createQuery( Person.class ); Root<Person> personRoot = person.from( Person.class ); Join<Person,Order> orders = personRoot.join( Person_.orders ); Join<Order,LineItem> orderLines = orders.join( Order_.lineItems );
Just like in HQL and JPQL, criteria queries can specify that associated data be fetched along
with the owner. Fetches are created by the numerous overloaded fetch
methods of the javax.persistence.criteria.From
interface.
Example 12.11. Example with Embedded and ManyToOne
CriteriaQuery<Person> personCriteria = builder.createQuery( Person.class ); Root<Person> personRoot = person.from( Person.class ); // Person.address is an embedded attribute Fetch<Person,Address> personAddress = personRoot.fetch( Person_.address ); // Address.country is a ManyToOne Fetch<Address,Country> addressCountry = personAddress.fetch( Address_.country );
Technically speaking, embedded attributes are always fetched with their owner. However in
order to define the fetching of Address#country we needed a
javax.persistence.criteria.Fetch
for its parent path.
Example 12.12. Example with Collections
CriteriaQuery<Person> personCriteria = builder.createQuery( Person.class ); Root<Person> personRoot = person.from( Person.class ); Fetch<Person,Order> orders = personRoot.fetch( Person_.orders ); Fetch<Order,LineItem> orderLines = orders.fetch( Order_.lineItems );
Example 12.13. Using parameters
CriteriaQuery<Person> criteria = build.createQuery( Person.class ); Root<Person> personRoot = criteria.from( Person.class ); criteria.select( personRoot ); ParameterExpression<String> eyeColorParam = builder.parameter( String.class ); criteria.where( builder.equal( personRoot.get( Person_.eyeColor ), eyeColorParam ) ); TypedQuery<Person> query = em.createQuery( criteria ); query.setParameter( eyeColorParam, "brown" ); List<Person> people = query.getResultList();
Use the parameter
method of
javax.persistence.criteria.CriteriaBuilder
to obtain a parameter
reference. Then use the parameter reference to bind the parameter value to the
javax.persistence.Query