LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 1317 min read

Criteria API Basics

Build queries programmatically and type-safely with the Hibernate/JPA Criteria API — an alternative to writing HQL as raw strings.

Introduction

HQL is powerful, but it is still just a string — the compiler cannot check it, and a typo in a field name only surfaces at runtime. The Criteria API solves this by letting you build queries as Java objects and method calls instead of text. It is especially useful when a query needs to be assembled dynamically, for example adding filters conditionally based on which search fields a user actually filled in.

What You Will Learn
  • Why the Criteria API exists alongside HQL
  • How to build a basic query with CriteriaBuilder and CriteriaQuery
  • How to add WHERE-style filtering with predicates
  • How to sort results using the Criteria API

Why the Criteria API?

The Criteria API is the JPA-standard, object-oriented way to construct queries. Instead of writing "WHERE s.marks > :minMarks" as text, you build that condition using Java method calls on a CriteriaBuilder. This gives you compile-time checking of structure (though field names are still resolved as strings via the metamodel or plain string literals) and makes it much easier to build queries whose shape changes based on runtime conditions.

AspectHQLCriteria API
FormString queryJava objects and method chains
Dynamic queriesRequires manual string buildingNaturally composable with if-statements
Readability for simple queriesVery readableMore verbose
Refactoring safetyBreaks silently on renameSafer with the static metamodel

Building a Basic Criteria Query

Every Criteria query starts the same way: get a CriteriaBuilder from the Session, use it to create a CriteriaQuery, define the root entity, and execute it.

Session session = sessionFactory.openSession();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Student> cq = cb.createQuery(Student.class);
Root<Student> root = cq.from(Student.class);
cq.select(root);
Query<Student> query = session.createQuery(cq);
List<Student> students = query.getResultList();
for (Student s : students) {
System.out.println(s.getName());
}
session.close();
Console Output

Click Run to see what this code prints.

This is the Criteria equivalent of "FROM Student" — cq.select(root) with no filtering fetches every row, translated into the same underlying SQL you saw with HQL.

Filtering with Predicates

Conditions in the Criteria API are represented as Predicate objects, built using methods on CriteriaBuilder such as equal(), greaterThan(), like(), and, and or().

CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Student> cq = cb.createQuery(Student.class);
Root<Student> root = cq.from(Student.class);
Predicate highMarks = cb.greaterThan(root.get("marks"), 75.0);
Predicate nameStartsWithA = cb.like(root.get("name"), "A%");
cq.select(root).where(cb.and(highMarks, nameStartsWithA));
List<Student> results = session.createQuery(cq).getResultList();
results.forEach(s -> System.out.println(s.getName() + " - " + s.getMarks()));
Console Output

Click Run to see what this code prints.

Building Filters Conditionally

Because predicates are just Java objects, you can collect them into a List<Predicate>, add to that list only when a filter value is present, and combine everything at the end with cb.and(predicates.toArray(new Predicate[0])) — exactly the pattern that makes Criteria shine for search screens with optional filters.

Ordering Results

Sorting works the same way — through the CriteriaBuilder, using asc() or desc() on a path expression.

CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Student> cq = cb.createQuery(Student.class);
Root<Student> root = cq.from(Student.class);
cq.select(root).orderBy(cb.desc(root.get("marks")));
List<Student> rankedStudents = session.createQuery(cq).getResultList();
Console Output

Click Run to see what this code prints.

Common Mistakes

Avoid These Mistakes
  • Reaching for the Criteria API for every simple query — it adds verbosity that plain HQL avoids for straightforward cases.
  • Passing raw field name strings like root.get("marks") without validating them, reintroducing the same typo risk Criteria is meant to avoid (the static metamodel solves this but adds build-time generation).
  • Forgetting to call .select(root) before executing the query.
  • Mixing up cb.and() (all conditions must match) with cb.or() (any condition matches) when combining predicates.

Best Practices

  • Reach for the Criteria API specifically when a query's filters are built up dynamically at runtime.
  • Keep simple, fixed queries in HQL — it is more readable for anyone maintaining the code later.
  • Consider generating the JPA static metamodel (Student_ class) for compile-time-safe field references instead of string literals.
  • Build predicate lists incrementally and combine them once at the end, rather than nesting conditional and/or logic.

Frequently Asked Questions

No — both ultimately compile down to the same SQL and go through the same execution engine. The Criteria API's benefit is in how the query is constructed in Java, not in runtime performance.

Not for every query. Use HQL for fixed, readable queries, and reach for Criteria specifically when the query structure needs to change based on runtime conditions, like optional search filters.

It is a set of generated classes (e.g., Student_) with static fields matching your entity's properties, letting you write root.get(Student_.marks) instead of root.get("marks") for compile-time safety.

Key Takeaways

  • The Criteria API builds queries as Java objects instead of strings.
  • CriteriaBuilder, CriteriaQuery, and Root are the three core building blocks.
  • Predicate objects represent WHERE-style conditions and combine with cb.and()/cb.or().
  • Criteria queries shine when filters are assembled dynamically at runtime.

Summary

The Criteria API trades some readability for type safety and dynamic composability, making it a valuable tool for building flexible search and filter logic. With single-entity querying covered from both HQL and Criteria angles, it is time to model relationships between entities, starting with the simplest kind: one-to-one.

Next Lesson →

One-to-One Mappings