- Published on
JPA Criteria Queries
- Authors
- Name
- David Nguyen
Table of Contents
- 1. What Are JPA Criteria Queries?
- 2. Why Use Criteria Queries?
- Key benefits to using Criteria Queries:
- Key Components of JPA Criteria Queries
- 3. Building a Basic Criteria Query
- Set Up the Entities
- Building the Criteria Query
- Adding Filters with Predicates
- Combining Multiple Predicates
- Ordering and Pagination.
- 4. Conclusion.
1. What Are JPA Criteria Queries?
JPA Criteria Queries are a programmatic way to create and execute database queries in Java. Instead of writing your queries as strings (like with JPQL), you build them using the JPA Criteria API, which is part of the
javax.persistence
package. This approach allows you to construct queries dynamically, using Java code.The main advantage of Criteria Queries is type safety — since you work directly with your Java entity classes, the compiler can catch potential issues like typos in field names or invalid types, reducing runtime errors.
Additionally, Criteria Queries are ideal for scenarios where query conditions need to be built based on user input or other dynamic factors.
2. Why Use Criteria Queries?
Key benefits to using Criteria Queries:
Type Safety: Since the queries are constructed programmatically using Java classes, the code is type-checked at compile-time.
Dynamic Query Building: Criteria Queries allow you to build queries dynamically based on conditions that aren’t known until runtime.
Readability: Though programmatic queries can sometimes seem verbose, they are easier to read and maintain in complex applications compared to long JPQL strings.
Tooling Support: Integrated development environments (IDEs) offer better refactoring support for Criteria Queries than string-based JPQL.
Key Components of JPA Criteria Queries
CriteriaBuilder: The entry point for creating Criteria Queries. It provides factory methods for building expressions, predicates, and query objects.
CriteriaQuery: Represents the query itself. It defines the result type (such as entities or scalar values) and contains the overall query structure.
Root: This is the query's starting point. It represents the entity that you are querying (like a table in SQL).
Predicate: Represents conditions (WHERE clauses) in the query. You can combine multiple predicates using logical operators such as AND and OR.
TypedQuery: Once your query is built, you execute it using a TypedQuery, which returns a list of results.
3. Building a Basic Criteria Query
Let’s walk through the steps to create a simple query using the Criteria API. Suppose we have a Customer
entity, and we want to retrieve all customers from the database.
Set Up the Entities
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
// Getters and Setters
}
Building the Criteria Query
Now let’s write the query to retrieve all Customer
entities.
public List<Customer> getAllCustomers(EntityManager em) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Customer> cq = cb.createQuery(Customer.class);
Root<Customer> customer = cq.from(Customer.class); // From clause
cq.select(customer);
TypedQuery<Customer> query = em.createQuery(cq);
return query.getResultList();
}
In this sample code:
- We obtain a
CriteriaBuilder
instance from the EntityManager. - We create a
CriteriaQuery<Customer>
object to define the query result type. - A
Root<Customer>
object specifies the root entity being queried. - The
cq.select(customer)
method tells JPA to retrieve all customers. - Finally, we create a
TypedQuery
and execute it withgetResultList()
.
- We obtain a
Adding Filters with Predicates
Let’s say we want to filter customers based on their last name. We can add conditions using the Predicate
interface.
public List<Customer> getCustomersByLastName(EntityManager em, String lastName) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Customer> cq = cb.createQuery(Customer.class);
Root<Customer> customer = cq.from(Customer.class);
cq.select(customer);
Predicate lastNamePredicate = cb.equal(customer.get("lastName"), lastName);
cq.where(lastNamePredicate);
TypedQuery<Customer> query = em.createQuery(cq);
return query.getResultList();
}
- Here, we use
cb.equal()
to create a predicate that checks whether thelastName
field matches the provided value. Thecq.where()
method adds this condition to the query.
Combining Multiple Predicates
- You can combine multiple conditions using
AND
andOR
logical operators. Suppose we want to retrieve customers with both a specific first name and last name.
public List<Customer> getCustomersByFullName(EntityManager em, String firstName, String lastName) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Customer> cq = cb.createQuery(Customer.class);
Root<Customer> customer = cq.from(Customer.class);
cq.select(customer);
Predicate firstNamePredicate = cb.equal(customer.get("firstName"), firstName);
Predicate lastNamePredicate = cb.equal(customer.get("lastName"), lastName);
cq.where(cb.and(firstNamePredicate, lastNamePredicate));
TypedQuery<Customer> query = em.createQuery(cq);
return query.getResultList();
}
- Here,
cb.and()
combines the predicates forfirstName
andlastName
into a single condition.
Ordering and Pagination.
You can also add sorting and pagination to your queries. For example, to sort the customers by last name in ascending order:
public List<Customer> getCustomersSortedByLastName(EntityManager em) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Customer> cq = cb.createQuery(Customer.class);
Root<Customer> customer = cq.from(Customer.class);
cq.select(customer);
cq.orderBy(cb.asc(customer.get("lastName")));
TypedQuery<Customer> query = em.createQuery(cq);
return query.getResultList();
}
For pagination, you can use the setFirstResult()
and setMaxResults()
methods:
public List<Customer> getPaginatedCustomers(EntityManager em, int page, int pageSize) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Customer> cq = cb.createQuery(Customer.class);
Root<Customer> customer = cq.from(Customer.class);
cq.select(customer);
TypedQuery<Customer> query = em.createQuery(cq);
query.setFirstResult((page - 1) * pageSize);
query.setMaxResults(pageSize);
return query.getResultList();
}
4. Conclusion.
JPA Criteria Queries offer a flexible and type-safe way to build dynamic queries. They are especially useful in scenarios where you need to construct queries based on user input or complex logic. By leveraging the Criteria API, you can reduce the risks associated with hardcoded queries, making your code easier to maintain and debug.
See you in the next posts. Happy Coding!