Criteria API
Learn how to build queries programmatically using Hibernate's Criteria API. This approach is useful for constructing dynamic queries based on runtime conditions. We'll cover the basics of building criteria queries and executing them.
Hibernate Criteria API: Adding Restrictions (WHERE Clause)
This document explains how to add restrictions (the equivalent of a SQL WHERE
clause) to your Hibernate Criteria queries to filter results. We'll cover using predicates to create WHERE
clauses with operators like equals, greater than, less than, like, and more.
Introduction to Restrictions
The Hibernate Criteria API provides a programmatic way to build database queries. Adding restrictions is essential for filtering data based on specific criteria. Restrictions are added using the Restrictions
(prior to Hibernate 5.2) or CriteriaBuilder
(Hibernate 5.2+) and Predicate
interface. The newer approach with `CriteriaBuilder` is generally preferred for type safety and expressiveness.
Using `CriteriaBuilder` and `Predicate` (Hibernate 5.2+)
The CriteriaBuilder
interface, obtained from the EntityManager
, provides methods for creating predicates, which represent the conditions for your WHERE
clause. Here's a breakdown of how to use it:
Example Scenario
Let's assume we have an entity called Employee
with fields like id
, firstName
, lastName
, and salary
.
@Entity
@Table(name = "employees")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "salary")
private Double salary;
// Getters and setters
}
Creating a Criteria Query with Restrictions
Here's how to create a query to find all employees with a salary greater than 50000:
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.persistence.Predicate;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import java.util.List;
public class CriteriaExample {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("your_persistence_unit_name"); // Replace with your persistence unit name
EntityManager em = emf.createEntityManager();
try {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
Root<Employee> root = cq.from(Employee.class);
// Add a restriction: salary > 50000
Predicate salaryGreaterThan = cb.gt(root.get("salary"), 50000.0);
cq.where(salaryGreaterThan);
// Execute the query
List<Employee> employees = em.createQuery(cq).getResultList();
// Print the results
for (Employee employee : employees) {
System.out.println(employee.getFirstName() + " " + employee.getLastName() + ": " + employee.getSalary());
}
} finally {
em.close();
emf.close();
}
}
}
Explanation:
- `CriteriaBuilder cb = em.getCriteriaBuilder();`: Obtains the `CriteriaBuilder` from the `EntityManager`.
- `CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);`: Creates a `CriteriaQuery` for the `Employee` entity.
- `Root<Employee> root = cq.from(Employee.class);`: Specifies the entity to query from (the `Employee` entity). The `Root` object represents the root of the query.
- `Predicate salaryGreaterThan = cb.gt(root.get("salary"), 50000.0);`: Creates a predicate (condition) that checks if the `salary` field of the `Employee` entity (accessed using `root.get("salary")`) is greater than 50000.0. `cb.gt()` is the "greater than" operator.
- `cq.where(salaryGreaterThan);`: Adds the `salaryGreaterThan` predicate to the `CriteriaQuery`, effectively creating the
WHERE
clause. - `List<Employee> employees = em.createQuery(cq).getResultList();`: Executes the `CriteriaQuery` and retrieves the results as a list of `Employee` objects.
Common Predicate Operators
Here's a table summarizing common predicate operators available in CriteriaBuilder
:
Operator | Description | Example |
---|---|---|
cb.equal(x, y) | Equals (x = y ) | Predicate isEqual = cb.equal(root.get("firstName"), "John"); |
cb.notEqual(x, y) | Not equals (x != y ) | Predicate notEqual = cb.notEqual(root.get("id"), 1L); |
cb.gt(x, y) | Greater than (x > y ) | Predicate greaterThan = cb.gt(root.get("salary"), 60000.0); |
cb.ge(x, y) | Greater than or equal to (x >= y ) | Predicate greaterThanOrEqual = cb.ge(root.get("salary"), 60000.0); |
cb.lt(x, y) | Less than (x < y ) | Predicate lessThan = cb.lt(root.get("salary"), 40000.0); |
cb.le(x, y) | Less than or equal to (x <= y ) | Predicate lessThanOrEqual = cb.le(root.get("salary"), 40000.0); |
cb.like(x, pattern) | Like (string matching using SQL LIKE ) | Predicate like = cb.like(root.get("firstName"), "J%"); (Starts with "J") |
cb.notLike(x, pattern) | Not like (string matching using SQL NOT LIKE ) | Predicate notLike = cb.notLike(root.get("lastName"), "%son"); (Does not end with "son") |
cb.isNull(x) | Is null (x IS NULL ) | Predicate isNull = cb.isNull(root.get("middleName")); |
cb.isNotNull(x) | Is not null (x IS NOT NULL ) | Predicate isNotNull = cb.isNotNull(root.get("middleName")); |
cb.in(x) | In (x IN (value1, value2, ...) ) | Predicate in = root.get("id").in(Arrays.asList(1L, 2L, 3L)); |
Combining Predicates with `AND` and `OR`
You can combine multiple predicates using cb.and()
and cb.or()
to create more complex conditions:
// Find employees with salary > 50000 AND first name starts with "J"
Predicate salaryGreaterThan = cb.gt(root.get("salary"), 50000.0);
Predicate firstNameLike = cb.like(root.get("firstName"), "J%");
Predicate combinedPredicate = cb.and(salaryGreaterThan, firstNameLike); // AND condition
cq.where(combinedPredicate);
// Find employees with salary > 50000 OR first name starts with "J"
Predicate salaryGreaterThan2 = cb.gt(root.get("salary"), 50000.0);
Predicate firstNameLike2 = cb.like(root.get("firstName"), "J%");
Predicate combinedPredicate2 = cb.or(salaryGreaterThan2, firstNameLike2); // OR condition
cq.where(combinedPredicate2);
Using `Restrictions` (Hibernate < 5.2 - *Deprecated*)
Note: The Restrictions
class is deprecated in newer versions of Hibernate (5.2+). While it might still work, it's highly recommended to use the CriteriaBuilder
and `Predicate` approach as described above for better type safety and future compatibility.
Using Restrictions
involves creating a Criterion
object and adding it to the Criteria
object. The basic structure looks like this:
// Example using Restrictions (Deprecated)
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.hibernate.cfg.Configuration;
import java.util.List;
public class CriteriaExampleOld {
public static void main(String[] args) {
Configuration cfg = new Configuration().configure("hibernate.cfg.xml"); // Replace with your hibernate configuration file
SessionFactory sessionFactory = cfg.buildSessionFactory();
Session session = sessionFactory.openSession();
Criteria criteria = session.createCriteria(Employee.class);
// Example: Find employees with salary greater than 50000
criteria.add(Restrictions.gt("salary", 50000.0));
List<Employee> employees = criteria.list();
for (Employee employee : employees) {
System.out.println(employee.getFirstName() + " " + employee.getLastName() + ": " + employee.getSalary());
}
session.close();
sessionFactory.close();
}
}
The older style (Restrictions) provides similar methods like eq
(equals), ne
(not equals), gt
(greater than), lt
(less than), like
, isNull
, isNotNull
, and combinations with and
and or
. The fundamental difference is that it is not type-safe and relies on string-based property names, making it more prone to errors. It's strongly recommended to use the `CriteriaBuilder` and `Predicate` API instead.
Conclusion
Adding restrictions to your Criteria queries is a fundamental technique for filtering data and retrieving the specific information you need. By using CriteriaBuilder
and Predicate
(or Restrictions
in older versions), you can create powerful and flexible queries to extract relevant data from your database.