In Java enterprise development, data persistence and querying form the backbone of almost every application. While SQL and HQL are powerful, they can sometimes feel static and inflexible when queries need to be built dynamically at runtime. This is where Hibernate’s Criteria API shines.
Criteria API allows developers to build queries programmatically using a fluent, type-safe approach, reducing the chances of syntax errors and improving maintainability. Think of it as constructing queries like LEGO blocks—you combine smaller parts to form complex queries.
In this tutorial, we’ll cover the Criteria API in Hibernate, how it works, when to use it, and best practices for production-ready applications.
What is Criteria API in Hibernate?
The Criteria API is Hibernate’s object-oriented query language. Unlike HQL or native SQL, it allows developers to construct queries dynamically using Java objects rather than string-based queries.
Key Benefits:
- Type-safe queries (errors are caught at compile time).
- Dynamic query building (perfect for complex search filters).
- Cleaner code compared to concatenating strings.
- Works with Hibernate Session and JPA.
Setup and Configuration
Before diving into examples, ensure you have Hibernate dependencies in your project:
Maven Dependency
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.4.4.Final</version>
</dependency>
Entity Example
import jakarta.persistence.*;
@Entity
@Table(name = "employees")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String department;
private double salary;
// Getters and Setters
}
Basic Usage of Criteria API
Creating a Criteria Query
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
Root<Employee> root = cq.from(Employee.class);
// Select all employees
cq.select(root);
Query<Employee> query = session.createQuery(cq);
List<Employee> results = query.getResultList();
tx.commit();
session.close();
Filtering Records (WHERE Clause)
Example: Fetch employees from IT department
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
Root<Employee> root = cq.from(Employee.class);
cq.select(root).where(cb.equal(root.get("department"), "IT"));
List<Employee> employees = session.createQuery(cq).getResultList();
Sorting Results (ORDER BY)
cq.orderBy(cb.desc(root.get("salary")));
This will return employees sorted by salary in descending order.
Multiple Conditions
cq.where(
cb.and(
cb.equal(root.get("department"), "Finance"),
cb.greaterThan(root.get("salary"), 50000)
)
);
Aggregations (COUNT, AVG, SUM, MAX, MIN)
CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
countQuery.select(cb.count(countQuery.from(Employee.class)));
Long totalEmployees = session.createQuery(countQuery).getSingleResult();
Joins in Criteria API
Example: Employee → Department relationship
Join<Employee, Department> departmentJoin = root.join("department");
cq.select(root).where(cb.equal(departmentJoin.get("name"), "HR"));
CRUD Operations with Criteria API
While Criteria API mainly focuses on Read operations, you can combine it with Hibernate’s session methods for Create, Update, Delete.
Create
Employee emp = new Employee();
emp.setName("John Doe");
emp.setDepartment("IT");
emp.setSalary(60000);
session.persist(emp);
Update with CriteriaUpdate
CriteriaUpdate<Employee> update = cb.createCriteriaUpdate(Employee.class);
Root<Employee> root = update.from(Employee.class);
update.set("salary", 70000).where(cb.equal(root.get("name"), "John Doe"));
session.createQuery(update).executeUpdate();
Delete with CriteriaDelete
CriteriaDelete<Employee> delete = cb.createCriteriaDelete(Employee.class);
Root<Employee> root = delete.from(Employee.class);
delete.where(cb.equal(root.get("department"), "Temporary"));
session.createQuery(delete).executeUpdate();
Performance Considerations
- Use pagination (
setFirstResult
,setMaxResults
) to avoid loading large datasets. - Leverage caching (2nd level cache for repeated queries).
- Avoid N+1 select problem by using
fetch joins
. - Always close sessions properly.
Real-World Use Case: Search Filters in Spring Boot
In enterprise apps, Criteria API is often used for dynamic search filters:
public List<Employee> searchEmployees(String dept, Double minSalary) {
Session session = sessionFactory.openSession();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
Root<Employee> root = cq.from(Employee.class);
List<Predicate> predicates = new ArrayList<>();
if (dept != null) {
predicates.add(cb.equal(root.get("department"), dept));
}
if (minSalary != null) {
predicates.add(cb.greaterThan(root.get("salary"), minSalary));
}
cq.select(root).where(predicates.toArray(new Predicate[0]));
return session.createQuery(cq).getResultList();
}
This approach ensures flexible querying without hardcoding conditions.
📌 Hibernate Version Notes
Hibernate 5.x
- Relies on
javax.persistence
namespace. - Legacy
Criteria
API (org.hibernate.Criteria
) exists but is deprecated.
Hibernate 6.x
- Uses Jakarta Persistence (
jakarta.persistence
). - Enhanced Criteria API with better type safety.
- Improved SQL rendering and new query APIs.
Best Practices
- Prefer Criteria API for dynamic queries, but use HQL for static queries.
- Combine Criteria API with DTO projections for efficiency.
- Monitor lazy loading to avoid performance pitfalls.
- Use batch fetching and caching where possible.
Common Pitfalls
- Overusing Criteria API for simple queries (adds unnecessary complexity).
- Forgetting to close sessions.
- Not handling large result sets with pagination.
- Ignoring the N+1 select problem.
Conclusion and Key Takeaways
The Hibernate Criteria API is a powerful tool for building dynamic, type-safe queries in enterprise applications. By combining it with best practices, caching, and proper fetching strategies, developers can achieve highly efficient and maintainable persistence logic.
FAQ
Q1: What’s the difference between Hibernate and JPA?
Hibernate is a JPA implementation with additional features like Criteria API, caching, and HQL.
Q2: How does Hibernate caching improve performance?
It avoids redundant database hits by storing entities and queries in memory.
Q3: What are the drawbacks of eager fetching?
It loads unnecessary data upfront, causing performance overhead.
Q4: How do I solve the N+1 select problem in Hibernate?
Use fetch joins
or batch fetching.
Q5: Can I use Hibernate without Spring?
Yes, Hibernate works standalone, but Spring simplifies configuration.
Q6: What’s the best strategy for inheritance mapping?
Depends on use case—Single Table for speed, Joined for normalization, Table per Class for flexibility.
Q7: How does Hibernate handle composite keys?
With @EmbeddedId
or @IdClass
annotations.
Q8: How is Hibernate 6 different from Hibernate 5?
Hibernate 6 uses Jakarta namespace, better SQL support, and enhanced Criteria API.
Q9: Is Hibernate suitable for microservices?
Yes, but lightweight alternatives like JOOQ may be better for small services.
Q10: When should I not use Hibernate?
When raw SQL performance is critical or schema is highly dynamic.