Key Takeaways
- The Criteria API offers a type-safe method for defining queries programmatically, allowing for the selection of a set of entity attributes and mapping each result set record to a Plain Old Java Object (POJO).
- To select POJOs with a CriteriaQuery, you need to use a constructor expression similar to those used in JPQL queries. This involves defining the CriteriaQuery, executing it, and then retrieving a list of the selected POJOs.
- CriteriaQuery is a flexible and dynamic query mechanism in Java that prevents SQL injection attacks by defining query structure programmatically rather than through string manipulation. It enables the construction of complex database queries using an object-oriented API.
Table of Contents
The Criteria API provides a type-safe way to define queries programmatically. Similar to JPQL, you can use it to select a set of entity attributes and map each result set record to a Plain Old Java Object (POJO).
The Criteria API supports the same feature set as JPQL, and I show in my new book Hibernate Tips – More than 70 solutions to common Hibernate problems how you can use it to implement a variety of use cases. It’s a Hibernate cookbook with more than 70 ready-to-use recipes for topics like basic and advanced mappings, logging, Java 8 support, caching and statically and dynamically defined queries. You can get it this week on Amazon at a special launch price of just $2.99.
Let’s take a look at one of the recipes included in the book. It uses the Criteria API to programmatically define a query that selects a set of database columns and returns them as POJOs. To get the most out of this post, you should already be familiar with Hibernate’s basic concepts.
How to select POJOs with a CriteriaQuery
Problem
JPQL supports constructor expressions to select POJOs instead of entities or scalar values. Can I do the same with the Criteria API?
Solution
You can use a similar constructor expression with the Criteria API as you use in JPQL queries. In the following example, I want to select AuthorValue
objects.
public class AuthorValue {
private String firstName;
private String lastName;
public AuthorValue(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
...
}
The definition of the CriteriaQuery
follows the same approach as you use to select entities.
You first need to get a CriteriaBuilder
instance from the EntityManager
. It’s a factory class that helps you to define different parts of your query, like bind parameters, function calls, and predicates.
You then create a CriteriaQuery
instance that is the root of the object graph that represents your query. I recommend providing the return type of your query as a parameter to the createQuery
method. It creates a typed instance of the CriteriaQuery
interface. I want to select AuthorValue
objects in this example and, therefore, provide AuthorValue.class
as the parameter.
In the next step, you define the FROM
clause of the query. In this example, I use the Author
entity as the root of the query.
Then you can define the projection of your query. In this example, it’s a call of the AuthorValue
constructor. The construct
method of the CriteriaBuilder
allows you to define constructor calls. Call this method using the class that Hibernate instantiates as the first parameter and an optional list of Selections
that are used as constructor parameters.
In this example, I use the JPA Metamodel class Author_
to reference the firstName
and lastName
attributes of the Author
entity. If you’re not familiar with the JPA Metamodel, take a look at the Hibernate tip How to reference entity attributes in a type-safe way, which provides a comfortable and type-safe way to reference entity attributes.
The definition of the WHERE
clause is optional. If you want to select all records from the database, you can skip this block and execute your query. That’s what I do in this example. You can see an example of a WHERE
clause definition in the How to select entities with a CriteriaQuery chapter.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<AuthorValue> q = cb.createQuery(AuthorValue.class);
Root<Author> root = q.from(Author.class);
q.select(
cb.construct(
AuthorValue.class,
root.get(Author_.firstName),
root.get(Author_.lastName)));
That’s all you need to do to define the CriteriaQuery
. You can now execute it in two steps. You first need to call the createQuery
method of the EntityManager
with your CriteriaQuery
. This method call returns the same TypedQuery
interface as you use in your JPQL queries. You can use it to set bind parameter values or to paginate the query result. I don’t use any bind parameters in this example and therefore, can skip this part. In the final step, you need to call the getResultList
method on the TypedQuery
interface to execute the query and retrieve a List
of AuthorValue
objects.
TypedQuery<AuthorValue> query = em.createQuery(q);
List<AuthorValue> authors = query.getResultList();
When I created the query, I referenced the firstName
and lastName
attributes in the constructor call definition. As you can see in the following log messages, Hibernate used these reference to generates an SQL query that selects the firstName
and lastName
columns from the Author
table.
13:43:09,884 DEBUG [org.hibernate.SQL] -
select
author0_.firstName as col_0_0_,
author0_.lastName as col_1_0_
from
Author author0_
Source Code
You can find a download link for a project with executable test cases for this Hibernate tip in the book.
Learn More
If you don’t want to create a POJO to represent your query result, you can also select a set of scalar values. I show you how to do that in the chapter How to select multiple scalar values in a CriteriaQuery.
And if you want to use the Criteria API in your project, you should also have a look at the JPA metamodel. It provides a great way to create queries in a type-safe way. I show you how to use and generate the required classes in the chapter How to reference entity attributes in a type-safe way.
Summary
The Criteria API provides a type-safe option to define queries programmatically. As you have seen in the book excerpt, you can use it to select a set of database columns and let Hibernate map each result set record to a POJO.
Get more recipes like this in my new book Hibernate Tips: More than 70 solutions to common Hibernate problems. It gives you more than 70 ready-to-use recipes for topics like basic and advanced mappings, logging, Java 8 support, caching and statically and dynamically defined queries. For just a few days, you can get the ebook for $2.99 and the paperback for $12.99.
Frequently Asked Questions (FAQs) about Selecting POJOs with a CriteriaQuery
What is the purpose of using CriteriaQuery in Java?
CriteriaQuery is a type of query mechanism in Java that allows you to construct complex database queries using an object-oriented API, rather than hard-coding query strings. This approach provides a more flexible and dynamic way to build queries, making it easier to adapt your code to changing requirements. It also helps to prevent SQL injection attacks, as the query structure is defined programmatically rather than through string manipulation.
How do I create a CriteriaQuery instance?
To create a CriteriaQuery instance, you first need to obtain a CriteriaBuilder instance from the EntityManager. The CriteriaBuilder interface serves as a factory for all criteria queries. Here’s a simple example:CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<YourEntity> cq = cb.createQuery(YourEntity.class);
How can I select specific columns using CriteriaQuery?
To select specific columns in a CriteriaQuery, you can use the multiselect() method of the CriteriaBuilder interface. This method takes a variable number of Selection arguments and returns a CompoundSelection instance. Here’s an example:CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<YourEntity> root = cq.from(YourEntity.class);
cq.multiselect(root.get("column1"), root.get("column2"));
How can I add conditions to my CriteriaQuery?
You can add conditions to your CriteriaQuery using the where() method of the CriteriaQuery interface. This method takes a Predicate instance, which you can create using the various predicate methods of the CriteriaBuilder interface. Here’s an example:CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<YourEntity> cq = cb.createQuery(YourEntity.class);
Root<YourEntity> root = cq.from(YourEntity.class);
cq.where(cb.equal(root.get("column1"), "value1"));
How can I order the results of my CriteriaQuery?
You can order the results of your CriteriaQuery using the orderBy() method of the CriteriaQuery interface. This method takes a variable number of Order instances, which you can create using the asc() and desc() methods of the CriteriaBuilder interface. Here’s an example:CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<YourEntity> cq = cb.createQuery(YourEntity.class);
Root<YourEntity> root = cq.from(YourEntity.class);
cq.orderBy(cb.asc(root.get("column1")));
How can I execute my CriteriaQuery?
You can execute your CriteriaQuery using the createQuery() method of the EntityManager interface. This method takes a CriteriaQuery instance and returns a TypedQuery instance, which you can then execute using the getResultList() or getSingleResult() method. Here’s an example:CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<YourEntity> cq = cb.createQuery(YourEntity.class);
Root<YourEntity> root = cq.from(YourEntity.class);
TypedQuery<YourEntity> query = em.createQuery(cq);
List<YourEntity> results = query.getResultList();
How can I join tables using CriteriaQuery?
You can join tables using the join() method of the Root or Join interface. This method takes the name of the attribute representing the relationship to the other entity. Here’s an example:CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<YourEntity> cq = cb.createQuery(YourEntity.class);
Root<YourEntity> root = cq.from(YourEntity.class);
Join<YourEntity, OtherEntity> join = root.join("otherEntity");
How can I group results using CriteriaQuery?
You can group results using the groupBy() method of the CriteriaQuery interface. This method takes a variable number of Expression instances, which you can create using the get() method of the Root or Path interface. Here’s an example:CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<YourEntity> cq = cb.createQuery(YourEntity.class);
Root<YourEntity> root = cq.from(YourEntity.class);
cq.groupBy(root.get("column1"));
How can I use aggregate functions with CriteriaQuery?
You can use aggregate functions like count, sum, avg, min, and max with CriteriaQuery using the corresponding methods of the CriteriaBuilder interface. Here’s an example:CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Long> cq = cb.createQuery(Long.class);
Root<YourEntity> root = cq.from(YourEntity.class);
cq.select(cb.count(root));
How can I handle exceptions when using CriteriaQuery?
When using CriteriaQuery, exceptions can be handled using a try-catch block. The most common exception you might encounter is the PersistenceException, which is a runtime exception that wraps all failures that occur within the persistence mechanism. Here’s an example:try {
// Your CriteriaQuery code here
} catch (PersistenceException e) {
// Handle exception here
}
Thorben Janssen is an independent trainer with more than 15 years of experience with Hibernate. You can find more of his posts and several free cheat sheets about JPA and Hibernate on his blog or you can take his free video course about finding and fixing n+1 select issues.