Clean • Professional
When applications require dynamic, complex, and type-safe queries, derived queries or JPQL strings are often insufficient.
The Criteria API solves this by allowing queries to be built programmatically using Java code instead of string-based JPQL.
The Criteria API is a Java-based query-building mechanism provided by JPA.
Instead of writing queries as strings (JPQL/SQL), queries are constructed step by step using Java objects, making them:
Use Criteria API when:
| Component | Purpose |
|---|---|
EntityManager | Entry point to interact with persistence context |
CriteriaBuilder | Creates query elements and conditions |
CriteriaQuery<T> | Represents the query definition |
Root<T> | Represents the entity (FROM clause) |
Predicate | Represents WHERE conditions |

EntityManagerCriteriaBuilderCriteriaQueryRootPredicateThis flow allows incremental and conditional query building.
CriteriaBuildercb= entityManager.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> user = cq.from(User.class);
cq.select(user)
.where(cb.equal(user.get("status"),"ACTIVE"));
List<User> users = entityManager.createQuery(cq).getResultList();
Here,
CriteriaBuilder → Builds query partsCriteriaQuery → Defines what to fetchRoot<User> → Refers to the User entityPredicate → Applies filteringEntityManager → Executes the queryThe Criteria API is very powerful for building type-safe, dynamic queries. Unlike JPQL, you can conditionally add query restrictions at runtime.

Example: Dynamic search filters
CriteriaBuildercb= entityManager.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> user = cq.from(User.class);
List<Predicate> predicates =newArrayList<>();
if (status !=null) {
predicates.add(cb.equal(user.get("status"), status));
}
if (minAge !=null) {
predicates.add(cb.greaterThan(user.get("age"), minAge));
}
// Apply predicates only if they exist
cq.where(predicates.toArray(newPredicate[0]));
TypedQuery<User> query = entityManager.createQuery(cq);
List<User> result = query.getResultList();
CriteriaBuilder provides methods like equal, greaterThan, lessThan, like, etc.Sorting with Criteria API

You can define order dynamically:
// Ascending order by name
cq.orderBy(cb.asc(user.get("name")));
// Descending order by creation date
cq.orderBy(cb.desc(user.get("createdDate")));
cq.orderBy(cb.asc(user.get("name")), cb.desc(user.get("createdDate")));
Pagination with Criteria API

Pagination can be applied using TypedQuery:
TypedQuery<User> query = entityManager.createQuery(cq);
query.setFirstResult(0);// Offset (starting row)
query.setMaxResults(10);// Limit (page size)
List<User> users = query.getResultList();
setFirstResult(offset) → skips rows before returning results.setMaxResults(limit) → controls the number of rows fetched.| Feature | Criteria API | JPQL |
|---|---|---|
| Query type | Java-based, object-oriented | String-based, similar to SQL |
| Type safety | Checked at compile-time | Checked at runtime only |
| Dynamic queries | Easy to build dynamic conditions | Harder; requires string concatenation |
| Readability | Verbose, more boilerplate | Simple and concise |
| Error detection | Compile-time errors caught early | Runtime errors (e.g., typos in field names) |
Use Criteria API when:
Avoid Criteria API when:
@Query annotations are sufficient