Criteria API


Criteria API Interview with follow-up questions

1. What is Criteria API in Hibernate?

The Criteria API is a JPA-standard, programmatic, type-safe query mechanism. In Hibernate 6 it is backed by the SQM (Semantic Query Model) engine, giving it the same validation power as HQL.

Core classes (all from jakarta.persistence.criteria):

Class Role
CriteriaBuilder Factory for all query components; obtained from EntityManager.getCriteriaBuilder()
CriteriaQuery Defines query type and structure (SELECT, WHERE, ORDER BY, etc.)
Root Represents the entity in the FROM clause
Predicate A boolean condition for WHERE
Path Navigates entity attributes
Expression An expression (column reference, function call, literal)

Basic query:

import jakarta.persistence.EntityManager;
import jakarta.persistence.criteria.*;

EntityManager em = ...; // injected in Spring

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(Product.class);
Root product = cq.from(Product.class);

cq.select(product)
  .where(cb.and(
      cb.equal(product.get("category"), "Electronics"),
      cb.greaterThan(product.get("price"), new BigDecimal("50"))
  ))
  .orderBy(cb.asc(product.get("name")));

List results = em.createQuery(cq)
    .setMaxResults(20)
    .getResultList();

Type-safe version with JPA metamodel:

// Requires annotation processor to generate Product_
cq.where(cb.equal(product.get(Product_.category), "Electronics"));

Main use case: Building dynamic queries where the number and type of WHERE conditions are not known until runtime — e.g., a product search page with optional filters.

Hibernate 6 addition: HibernateCriteriaBuilder extends CriteriaBuilder with Hibernate-specific functions not in the JPA standard (e.g., ilike, matchesPattern, array operations).

↑ Back to top

Follow-up 1

How does Criteria API differ from HQL?

Criteria API and HQL are both used to query data from a database in Hibernate, but they have some differences:

  • Criteria API is a type-safe and object-oriented way to build queries, while HQL is a string-based query language.
  • Criteria API queries are created using a set of predefined classes and methods, while HQL queries are written as strings.
  • Criteria API queries are checked for syntax errors at compile-time, while HQL queries are checked at runtime.
  • Criteria API queries can be easily modified and extended programmatically, while HQL queries require modifying the string query itself.

Follow-up 2

Can you give an example of using Criteria API?

Sure! Here's an example of using Criteria API to retrieve all the books published after a certain date:

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(Book.class);
Root bookRoot = criteriaQuery.from(Book.class);

Predicate predicate = criteriaBuilder.greaterThan(bookRoot.get("publishDate"), LocalDate.of(2022, 1, 1));
criteriaQuery.where(predicate);

List books = entityManager.createQuery(criteriaQuery).getResultList();

Follow-up 3

What are the advantages of using Criteria API?

There are several advantages of using Criteria API:

  • Type-safety: Criteria API queries are checked for syntax errors at compile-time, reducing the chances of runtime errors.
  • Object-oriented: Criteria API provides a more natural and intuitive way to build queries using classes and methods, making the code easier to read and maintain.
  • Dynamic queries: Criteria API queries can be easily modified and extended programmatically, allowing for dynamic filtering and sorting of data.
  • Reusability: Criteria API queries can be encapsulated into reusable methods or criteria builders, promoting code reuse.
  • Integration with JPA: Criteria API is part of the JPA (Java Persistence API) specification, making it compatible with other JPA implementations.

Follow-up 4

In what scenarios would you prefer Criteria API over HQL?

Criteria API is preferred over HQL in the following scenarios:

  • Dynamic queries: If the query needs to be dynamically constructed based on runtime conditions, Criteria API provides a more flexible and readable way to build such queries.
  • Type-safety: If type-safety is a concern, Criteria API ensures that the query is checked for syntax errors at compile-time.
  • Object-oriented approach: If the development team prefers an object-oriented approach for building queries, Criteria API provides a more natural and intuitive way to do so.
  • Integration with JPA: If the application is using JPA as the persistence framework, Criteria API is a better choice as it is part of the JPA specification and provides better compatibility.

2. How can you perform complex queries using Criteria API?

Complex Criteria API queries combine joins, multiple predicates, subqueries, and ordering. Here is a structured walkthrough:

Multi-condition WHERE with joins:

import jakarta.persistence.criteria.*;
import java.util.ArrayList;
import java.util.List;

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(Order.class);
Root order = cq.from(Order.class);

// Join to associated entity
Join customer = order.join("customer", JoinType.INNER);

// Build dynamic predicate list
List predicates = new ArrayList<>();
predicates.add(cb.equal(customer.get("country"), "US"));
predicates.add(cb.greaterThanOrEqualTo(order.get("total"), new BigDecimal("100")));
predicates.add(cb.equal(order.get("status"), OrderStatus.SHIPPED));

cq.select(order)
  .where(cb.and(predicates.toArray(new Predicate[0])))
  .orderBy(cb.desc(order.get("createdAt")));

List results = em.createQuery(cq)
    .setFirstResult(0)
    .setMaxResults(50)
    .getResultList();

Subquery:

// Orders whose total > average order total
Subquery avgSubquery = cq.subquery(BigDecimal.class);
Root subRoot = avgSubquery.from(Order.class);
avgSubquery.select(cb.avg(subRoot.get("total")).as(BigDecimal.class));

cq.where(cb.greaterThan(order.get("total"), avgSubquery));

Aggregation with GROUP BY and HAVING:

CriteriaQuery aggQuery = cb.createQuery(Object[].class);
Root r = aggQuery.from(Order.class);

aggQuery.multiselect(
    r.get("customer"),
    cb.count(r),
    cb.sum(r.get("total"))
)
.groupBy(r.get("customer"))
.having(cb.greaterThan(cb.count(r), 5L));

Key APIs to know:

  • cb.and(predicates) / cb.or(predicates) — combine conditions
  • root.join("association", JoinType.LEFT) — outer joins
  • cq.subquery(T.class) — correlated subqueries
  • cb.count(), cb.sum(), cb.avg(), cb.max(), cb.min() — aggregates
  • cq.groupBy() / cq.having() — grouping
↑ Back to top

Follow-up 1

Can you give an example of a complex query using Criteria API?

Sure! Here's an example of a complex query using Criteria API:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery query = cb.createQuery(Employee.class);
Root employee = query.from(Employee.class);

// Selecting employees with salary greater than 50000
query.select(employee)
    .where(cb.greaterThan(employee.get("salary"), 50000));

// Executing the query
List result = entityManager.createQuery(query).getResultList();

Follow-up 2

How can you use Criteria API to perform join operations?

To perform join operations using Criteria API, you can use the join() method provided by the CriteriaQuery and Root classes. The join() method allows you to specify the join type (inner, left, right) and the join condition. Here's an example:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery query = cb.createQuery(Employee.class);
Root employee = query.from(Employee.class);
Join department = employee.join("department");

// Selecting employees and their department names
query.multiselect(employee.get("name"), department.get("name"));

// Executing the query
List result = entityManager.createQuery(query).getResultList();

Follow-up 3

What are the limitations of Criteria API in complex queries?

While Criteria API provides a powerful way to construct complex queries, it also has some limitations. Here are a few limitations:

  1. Lack of support for complex subqueries: Criteria API does not provide direct support for complex subqueries, such as nested subqueries or correlated subqueries. To handle such scenarios, you may need to resort to native SQL queries or other query languages.

  2. Limited support for dynamic queries: Although Criteria API allows you to build dynamic queries, it can be cumbersome to construct complex queries with dynamic conditions or joins. In such cases, using JPQL or native SQL queries may be more convenient.

  3. Steep learning curve: Criteria API has a steep learning curve compared to JPQL or native SQL queries. It requires understanding of the CriteriaQuery, CriteriaBuilder, and other related classes, which may take some time to grasp.

Despite these limitations, Criteria API remains a powerful tool for constructing complex queries in a type-safe and programmatic manner.

3. What are Projections in Criteria API?

In the JPA Criteria API, projections (also called selections) control what the query returns — rather than returning full entity objects, you can select specific fields, computed expressions, or aggregations.

1. Single field projection:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(String.class);
Root root = cq.from(Product.class);

cq.select(root.get("name"))
  .where(cb.equal(root.get("category"), "Electronics"));

List names = em.createQuery(cq).getResultList();

2. Multiple field projection (Object array):

CriteriaQuery cq = cb.createQuery(Object[].class);
Root root = cq.from(Product.class);

cq.multiselect(root.get("id"), root.get("name"), root.get("price"));

List rows = em.createQuery(cq).getResultList();
for (Object[] row : rows) {
    Long id = (Long) row[0];
    String name = (String) row[1];
    BigDecimal price = (BigDecimal) row[2];
}

3. DTO projection using constructor expression:

// DTO class with matching constructor
public record ProductSummary(Long id, String name, BigDecimal price) {}

CriteriaQuery cq = cb.createQuery(ProductSummary.class);
Root root = cq.from(Product.class);

cq.select(cb.construct(ProductSummary.class,
    root.get("id"), root.get("name"), root.get("price")));

List summaries = em.createQuery(cq).getResultList();

4. Aggregate projections:

CriteriaQuery cq = cb.createQuery(Double.class);
Root root = cq.from(Order.class);
cq.select(cb.avg(root.get("total")));
Double avgTotal = em.createQuery(cq).getSingleResult();

Interview tip: The DTO/constructor approach is preferred over Object[] because it produces strongly-typed results and decouples the query from presentation logic. Java records work well as lightweight DTO projections in Hibernate 6.

↑ Back to top

Follow-up 1

Can you give an example of using Projections in Criteria API?

Sure! Here's an example of using Projections in Criteria API to retrieve specific columns:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery query = cb.createQuery(Object[].class);
Root root = query.from(Employee.class);

query.select(cb.array(root.get("name"), root.get("salary")));

List results = entityManager.createQuery(query).getResultList();

for (Object[] result : results) {
    String name = (String) result[0];
    BigDecimal salary = (BigDecimal) result[1];
    // Process the data
}

Follow-up 2

What are the different types of Projections available in Criteria API?

There are several types of Projections available in Criteria API:

  1. Projections.property(String propertyName): Retrieves a specific property of an entity.
  2. Projections.alias(Projection projection, String alias): Assigns an alias to a projection.
  3. Projections.rowCount(): Retrieves the count of rows in the result set.
  4. Projections.sum(Expression> expression): Calculates the sum of a numeric expression.
  5. Projections.avg(Expression> expression): Calculates the average of a numeric expression.
  6. Projections.max(Expression> expression): Retrieves the maximum value of an expression.
  7. Projections.min(Expression> expression): Retrieves the minimum value of an expression.
  8. Projections.groupProperty(String propertyName): Groups the result set by a specific property.
  9. Projections.distinct(Projection projection): Retrieves distinct values of a projection.

Follow-up 3

How can you use Projections to perform aggregation operations?

Projections in Criteria API can be used to perform aggregation operations such as sum, average, maximum, minimum, and grouping. Here's an example of using Projections to calculate the sum of salaries for each department:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery query = cb.createQuery(Object[].class);
Root root = query.from(Employee.class);

Expression sumSalary = cb.sum(root.get("salary"));
Expression department = root.get("department");

query.select(cb.array(department, sumSalary));
query.groupBy(department);

List results = entityManager.createQuery(query).getResultList();

for (Object[] result : results) {
    String departmentName = (String) result[0];
    BigDecimal totalSalary = (BigDecimal) result[1];
    // Process the data
}

4. How can you perform pagination using Criteria API?

Pagination in the Criteria API is applied to the TypedQuery (the compiled query object), not to the CriteriaQuery itself, using setFirstResult() and setMaxResults().

setFirstResult(int) — zero-based offset; the index of the first row to return. setMaxResults(int) — maximum number of rows to return (the page size).

Example — page 3 of 20 items per page:

int pageNumber = 3;   // 1-based page number
int pageSize = 20;

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(Product.class);
Root root = cq.from(Product.class);

cq.select(root)
  .where(cb.equal(root.get("category"), "Electronics"))
  .orderBy(cb.asc(root.get("name")));  // ORDER BY is required for stable pagination

List page = em.createQuery(cq)
    .setFirstResult((pageNumber - 1) * pageSize)  // offset = (3-1) * 20 = 40
    .setMaxResults(pageSize)                       // LIMIT 20
    .getResultList();

Getting total count (for page count calculation):

CriteriaQuery countQuery = cb.createQuery(Long.class);
Root countRoot = countQuery.from(Product.class);
countQuery.select(cb.count(countRoot))
          .where(cb.equal(countRoot.get("category"), "Electronics"));

Long totalItems = em.createQuery(countQuery).getSingleResult();
long totalPages = (totalItems + pageSize - 1) / pageSize;

Spring Data JPA: In practice, most Spring Boot applications use PagingAndSortingRepository or pass a Pageable object to repository methods, which handles setFirstResult/setMaxResults automatically and also runs the count query.

Critical note: Always include an ORDER BY clause when paginating. Without it, the database may return rows in arbitrary order, causing the same row to appear on multiple pages or be skipped entirely.

↑ Back to top

Follow-up 1

Can you give an example of pagination using Criteria API?

Sure! Here's an example of pagination using Criteria API:

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(Entity.class);
Root root = criteriaQuery.from(Entity.class);

criteriaQuery.select(root);
criteriaQuery.orderBy(criteriaBuilder.asc(root.get("id")));

int pageNumber = 1;
int pageSize = 10;
int firstResult = (pageNumber - 1) * pageSize;

TypedQuery query = entityManager.createQuery(criteriaQuery);
query.setFirstResult(firstResult);
query.setMaxResults(pageSize);

List entities = query.getResultList();

Follow-up 2

What methods are used in Criteria API for pagination?

The methods used in Criteria API for pagination are:

  • setFirstResult(int firstResult): Specifies the index of the first result to retrieve.
  • setMaxResults(int maxResults): Specifies the maximum number of results to retrieve.

Follow-up 3

What are the advantages of using pagination in Criteria API?

There are several advantages of using pagination in Criteria API:

  1. Performance: Pagination allows retrieving only a subset of results, which can improve query performance by reducing the amount of data transferred.
  2. User Experience: Pagination provides a better user experience by breaking down large result sets into smaller, more manageable pages.
  3. Memory Efficiency: By retrieving results in chunks, pagination helps conserve memory resources.
  4. Scalability: Pagination enables handling large result sets without overwhelming system resources.
  5. Flexibility: Pagination allows users to navigate through result sets and view specific subsets of data.

5. What is the role of Restrictions in Criteria API?

In the modern JPA Criteria API (Hibernate 6 / Jakarta Persistence), conditions are expressed as Predicate objects built by CriteriaBuilder. The old Hibernate-proprietary Restrictions class (from the deprecated Criteria API) has been removed in Hibernate 6.

CriteriaBuilder predicate methods — the current API:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(Employee.class);
Root emp = cq.from(Employee.class);

// Equality
Predicate isActive = cb.equal(emp.get("active"), true);

// Comparison
Predicate highEarner = cb.greaterThan(emp.get("salary"), 100000.0);
Predicate hired2020  = cb.greaterThanOrEqualTo(emp.get("hireDate"), LocalDate.of(2020, 1, 1));

// LIKE / ILIKE
Predicate nameMatch = cb.like(emp.get("name"), "A%");

// IS NULL / IS NOT NULL
Predicate noManager = cb.isNull(emp.get("manager"));

// IN
Predicate inDept = emp.get("department").get("name")
    .in("Engineering", "Product", "Design");

// BETWEEN
Predicate salaryRange = cb.between(emp.get("salary"), 50000.0, 90000.0);

// Combine predicates
cq.select(emp)
  .where(cb.and(isActive, highEarner, cb.or(inDept, noManager)))
  .orderBy(cb.asc(emp.get("name")));

Negation:

Predicate notActive = cb.not(isActive);
// or
Predicate notActive = cb.isFalse(emp.get("active"));

Historical note for interviews: Older Hibernate versions (pre-5.2) had a separate org.hibernate.Criteria API with Restrictions.eq(), Restrictions.gt(), etc. This API was deprecated in Hibernate 5.2 and removed in Hibernate 6. Any answer referencing session.createCriteria() or Restrictions is outdated. Use the JPA CriteriaBuilder API.

↑ Back to top

Follow-up 1

Can you give an example of using Restrictions in Criteria API?

Sure! Here's an example of using Restrictions in Criteria API to add a condition for selecting entities with a specific value:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery query = cb.createQuery(Entity.class);
Root root = query.from(Entity.class);

query.select(root)
    .where(cb.equal(root.get("property"), "value"));

List entities = entityManager.createQuery(query).getResultList();

Follow-up 2

What are the different types of Restrictions available in Criteria API?

There are several types of Restrictions available in Criteria API, including:

  • equal: Adds an equality condition
  • notEqual: Adds a not-equal condition
  • like: Adds a like condition
  • notLike: Adds a not-like condition
  • isNull: Adds a null condition
  • isNotNull: Adds a not-null condition
  • gt: Adds a greater-than condition
  • lt: Adds a less-than condition
  • ge: Adds a greater-than-or-equal condition
  • le: Adds a less-than-or-equal condition
  • between: Adds a between condition
  • in: Adds an in condition
  • notIn: Adds a not-in condition

These are just a few examples, and there are more types of Restrictions available depending on the Criteria API implementation.

Follow-up 3

How can you use Restrictions to perform conditional operations?

Restrictions in Criteria API can be used to perform conditional operations by combining them using logical operators such as and, or, and not. For example, to add multiple conditions using and, you can use the and method of the CriteriaBuilder class:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery query = cb.createQuery(Entity.class);
Root root = query.from(Entity.class);

Predicate condition1 = cb.equal(root.get("property1"), "value1");
Predicate condition2 = cb.equal(root.get("property2"), "value2");

query.select(root)
    .where(cb.and(condition1, condition2));

List entities = entityManager.createQuery(query).getResultList();

Live mock interview

Mock interview: Criteria API

Intermediate ~5 min Your own free AI key

Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.