The Hibernate Criteria API provides a powerful and flexible way to build dynamic queries in a type-safe manner. This tutorial will guide you through creating dynamic queries using the Criteria API with practical examples.
The need for Dynamic Queries
Dynamic queries are particularly useful in scenarios where search criteria can vary significantly based on user input or application logic. Common use cases include advanced search functionalities in web applications, where users can filter results based on multiple optional parameters such as date ranges, categories, or status flags.
For instance, an e-commerce platform might allow users to search for products by price range, brand, and availability status, all of which are backed by dynamic WHERE
conditions in the query.
In its simplest form, you can construct the SQL statement dynamically by mixing single Strings. A better approach consists in using Hibernate Criteria API. Let’s dig in it!
Setting up Dependencies
Firstly, we need to set up dependencies. If you are using a Spring Boot application, all you need is the base spring-boot-starter-data-jpa:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
On the other hand, Jakarta Enterprise application just need the following dependency:
<dependency> <groupId>jakarta.persistence</groupId> <artifactId>jakarta.persistence-api</artifactId> <scope>provided</scope> </dependency>
Finally, whatever is your choice, you will need to access the EntityManager to access the Criteria API.
@PersistenceContext private EntityManager entityManager;
Exploring Predicates
In the Criteria API, a Predicate
represents a condition used to filter query results. It is essentially a construct that defines a boolean expression, which can be combined with other predicates to form complex query conditions. Predicates are created using the CriteriaBuilder
and can represent various SQL conditions such as equality, inequality, like, between, and more.
For example, to add the dynamic condition WHERE surname = x
to a SELECT * from Customer
, you can use the following method:
public List<Customer> findBySurname(String surname) { CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<Customer> query = cb.createQuery(Customer.class); Root<Customer> customer = query.from(Customer.class); Predicate surnamePredicate = cb.equal(customer.get("surname"), surname); query.where(surnamePredicate); return entityManager.createQuery(query).getResultList(); }
Dynamic Query by Multiple Fields
To create a more dynamic query that can filter by multiple fields, you can use a Map
to pass the field names and values:
public List<Customer> findByFields(Map<String, Object> fields) { CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<Customer> query = cb.createQuery(Customer.class); Root<Customer> customer = query.from(Customer.class); List<Predicate> predicates = new ArrayList<>(); for (Map.Entry<String, Object> field : fields.entrySet()) { predicates.add(cb.equal(customer.get(field.getKey()), field.getValue())); } query.where(predicates.toArray(new Predicate[0])); return entityManager.createQuery(query).getResultList(); }
In this method, we iterate over the Map
entries to create a list of Predicate
objects. These predicates are then combined using the where
method.
Fetching columns dynamically
Sometimes, you may want to select specific columns rather than the entire entity. Here’s how you can achieve that:
public List<Object[]> findWithProjection(List<String> columns, Map<String, Object> fields) { CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<Object[]> query = cb.createQuery(Object[].class); Root<Customer> customer = query.from(Customer.class); List<Predicate> predicates = new ArrayList<>(); for (Map.Entry<String, Object> field : fields.entrySet()) { predicates.add(cb.equal(customer.get(field.getKey()), field.getValue())); } query.where(predicates.toArray(new Predicate[0])); List<jakarta.persistence.criteria.Selection<?>> selections = new ArrayList<>(); for (String column : columns) { selections.add(customer.get(column)); } query.multiselect(selections); return entityManager.createQuery(query).getResultList(); }
In this example, we use the multiselect
method to specify the columns we want to retrieve. The result is a list of Object[]
, where each array contains the values of the selected columns.
Conclusion
The Hibernate Criteria API is a powerful tool for building dynamic and type-safe queries. By using CriteriaBuilder
, CriteriaQuery
, and Predicate
, you can create flexible queries that adapt to various requirements. The examples provided demonstrate how to filter by single fields, multiple fields, and even select specific columns.
Source: https://github.com/fmarchioni/mastertheboss/tree/master/jpa/criteria-repository