Named Queries and Criteria Query


Named Queries and Criteria Query Interview with follow-up questions

1. What are Named Queries in Hibernate?

Named Queries in Hibernate are predefined, named JPQL or HQL queries declared on an entity class (or in XML) and registered with the SessionFactory at startup. They can be referenced by name throughout the application rather than repeating the query string.

Defining a Named Query (Hibernate 6 / Jakarta Persistence):

import jakarta.persistence.*;

@Entity
@NamedQuery(
    name = "Product.findByCategory",
    query = "SELECT p FROM Product p WHERE p.category = :category ORDER BY p.name"
)
@NamedQuery(
    name = "Product.findExpensive",
    query = "SELECT p FROM Product p WHERE p.price > :minPrice"
)
public class Product {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private String category;
    private BigDecimal price;
}

Multiple named queries: use @NamedQueries({...}) or repeat @NamedQuery (Java 8+ repeatable annotation).

Executing a Named Query:

// Via JPA EntityManager
List products = em.createNamedQuery("Product.findByCategory", Product.class)
    .setParameter("category", "Electronics")
    .setMaxResults(50)
    .getResultList();

// Via Hibernate Session
List products = session.createNamedQuery("Product.findByCategory", Product.class)
    .setParameter("category", "Electronics")
    .getResultList();

Named Native SQL Queries:

@NamedNativeQuery(
    name = "Product.findByTagNative",
    query = "SELECT * FROM product WHERE tags @> ARRAY[:tag]",
    resultClass = Product.class
)

Advantages:

  • Validated at SessionFactory startup — syntax errors surface immediately, not at runtime.
  • Potentially pre-compiled by the JPA provider.
  • Centralises query definitions on the entity, making them easy to locate.
  • Encourages reuse and avoids scattered query strings.

Spring Data JPA note: In Spring Data repositories, @Query on repository methods is the more common pattern, but @NamedQuery is still fully supported and is preferred when the query is shared across multiple repositories.

↑ Back to top

Follow-up 1

How do you define a Named Query?

To define a Named Query in Hibernate, you can use either the mapping file or the entity class. In the mapping file, you can define a Named Query using the element inside the or `` element. Here's an example:






In the entity class, you can use the @NamedQuery annotation to define a Named Query. Here's an example:

@Entity
@NamedQuery(name = "getUserByEmail", query = "SELECT u FROM User u WHERE u.email = :email")
public class User {
    // ...
}

Follow-up 2

What are the advantages of using Named Queries?

There are several advantages of using Named Queries in Hibernate:

  1. Reusability: Named Queries can be reused throughout the application, reducing code duplication.
  2. Performance: Named Queries are precompiled and cached, resulting in improved performance compared to dynamically created queries.
  3. Maintainability: By defining queries in a central location, it is easier to manage and update them.
  4. Security: Named Queries can help prevent SQL injection attacks by using parameterized queries.
  5. Readability: Named Queries make the code more readable by separating the query logic from the application logic.

Follow-up 3

Can you modify a Named Query at runtime?

No, you cannot modify a Named Query at runtime. Named Queries are defined and compiled during the application startup, and their definitions cannot be changed dynamically. If you need to modify a query at runtime, you can use dynamic queries or Criteria API instead.

Follow-up 4

How do you call a Named Query in your code?

To call a Named Query in your code, you can use the createQuery method of the EntityManager or Session interface. Here's an example using JPA:

String queryName = "getUserByEmail";
TypedQuery query = entityManager.createNamedQuery(queryName, User.class);
query.setParameter("email", "[email protected]");
List users = query.getResultList();

2. What is Criteria Query in Hibernate?

The Criteria API is a programmatic, type-safe way to build JPA/Hibernate queries at runtime using Java objects rather than query strings. It is particularly useful when query conditions are dynamic and not known until runtime.

Core components (jakarta.persistence.criteria):

  • CriteriaBuilder — factory for predicates, expressions, and orderings; obtained from EntityManager
  • CriteriaQuery — defines the return type and structure of the query
  • Root — represents the queried entity (like the FROM clause)
  • Predicate — a boolean condition (like a WHERE clause element)

Basic example:

import jakarta.persistence.criteria.*;

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

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

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

Type-safe with JPA metamodel (preferred):

// Generated metamodel class: Product_
query.select(root)
     .where(cb.greaterThan(root.get(Product_.price), new BigDecimal("100")));

When to use Criteria API vs HQL:

  • Criteria API: dynamic queries where filter conditions are determined at runtime (e.g., search forms with optional filters)
  • HQL/JPQL: static or nearly-static queries that are easier to read as strings

Hibernate 6 improvement: The Criteria API implementation is backed by SQM (Semantic Query Model), giving better validation at build time and more consistent behaviour with HQL.

↑ Back to top

Follow-up 1

How do you create a Criteria Query?

To create a Criteria Query in Hibernate, you need to follow these steps:

  1. Obtain a CriteriaBuilder instance from the EntityManager or Session.
  2. Create a CriteriaQuery object, specifying the result type.
  3. Use the CriteriaBuilder to define the query criteria, such as selecting specific columns, adding conditions, joining tables, etc.
  4. Execute the query by calling the appropriate method on the EntityManager or Session, such as getResultList() or getSingleResult().

Here's an example of creating a Criteria Query in Hibernate:

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(Employee.class);
Root root = criteriaQuery.from(Employee.class);
criteriaQuery.select(root).where(criteriaBuilder.equal(root.get("department"), "IT"));
List employees = entityManager.createQuery(criteriaQuery).getResultList();

Follow-up 2

What are the benefits of using Criteria Query?

There are several benefits of using Criteria Query in Hibernate:

  1. Object-oriented approach: Criteria Query allows developers to build queries using a fluent and type-safe API, which makes the code more readable and maintainable.
  2. Dynamic queries: Criteria Query provides a way to build queries dynamically at runtime, allowing for flexible and customizable queries based on changing requirements.
  3. Type safety: Criteria Query uses the type system of Java, which helps to catch errors at compile-time rather than runtime.
  4. Automatic query generation: Criteria Query can automatically generate SQL queries based on the criteria defined, reducing the need for manual SQL query writing.
  5. Integration with JPA: Criteria Query is part of the JPA specification, so it can be used with any JPA-compliant ORM framework, not just Hibernate.

Overall, Criteria Query provides a powerful and flexible way to build queries in Hibernate.

Follow-up 3

Can you give an example of a complex Criteria Query?

Sure! Here's an example of a complex Criteria Query in Hibernate:

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

Join customerJoin = root.join("customer");
Join productJoin = root.join("products");

criteriaQuery.select(root).distinct(true);
criteriaQuery.where(
    criteriaBuilder.and(
        criteriaBuilder.equal(customerJoin.get("country"), "USA"),
        criteriaBuilder.between(root.get("orderDate"), startDate, endDate),
        criteriaBuilder.or(
            criteriaBuilder.like(productJoin.get("name"), "%iPhone%"),
            criteriaBuilder.like(productJoin.get("name"), "%iPad%")
        )
    )
);

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

Follow-up 4

How does Criteria Query handle pagination and sorting?

Criteria Query in Hibernate provides methods for handling pagination and sorting. Here's how you can do it:

  1. Pagination: To implement pagination, you can use the setFirstResult() and setMaxResults() methods on the Query object. The setFirstResult() method specifies the index of the first result to retrieve, and the setMaxResults() method specifies the maximum number of results to retrieve.

  2. Sorting: To implement sorting, you can use the orderBy() method on the CriteriaQuery object. This method takes one or more Order objects as parameters, which can be created using the CriteriaBuilder's asc() and desc() methods.

Here's an example of using pagination and sorting in Criteria Query:

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

criteriaQuery.orderBy(criteriaBuilder.asc(root.get("lastName")));

List employees = entityManager.createQuery(criteriaQuery)
    .setFirstResult(0)
    .setMaxResults(10)
    .getResultList();

3. What is the difference between Named Queries and Criteria Query?

Named Queries and Criteria Query are complementary approaches that suit different scenarios:

Named Queries

  • Defined statically as JPQL/HQL strings annotated with @NamedQuery on the entity class (or in XML).
  • The query structure is fixed at compile time; only parameters change between executions.
  • Validated and potentially pre-compiled at SessionFactory startup.
  • Best for: well-known, frequently reused queries with predictable structure.
@NamedQuery(name = "Order.findByStatus",
            query = "SELECT o FROM Order o WHERE o.status = :status")

Criteria Query

  • Built programmatically at runtime using CriteriaBuilder, CriteriaQuery, and Root.
  • The query structure is determined at runtime based on application logic.
  • Type-safe (especially with the JPA metamodel); refactoring-friendly.
  • Best for: dynamic search forms where different combinations of filters are applied per request.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(Order.class);
Root root = cq.from(Order.class);

List predicates = new ArrayList<>();
if (status != null) predicates.add(cb.equal(root.get("status"), status));
if (minTotal != null) predicates.add(cb.greaterThanOrEqualTo(root.get("total"), minTotal));

cq.where(predicates.toArray(new Predicate[0]));
List orders = em.createQuery(cq).getResultList();

Summary comparison:

Named Query Criteria Query
Query definition Static string Programmatic API
Flexibility Fixed structure Fully dynamic
Readability High (plain JPQL) Lower (verbose Java)
Type safety Runtime param binding Compile-time with metamodel
Validation At startup At build time (with metamodel)
Best use Reusable static queries Dynamic/conditional search
↑ Back to top

Follow-up 1

In what scenarios would you prefer Named Queries over Criteria Query and vice versa?

Named Queries are preferred in the following scenarios:

  • When the query is fixed and does not change frequently.
  • When the query is simple and can be expressed using JPQL syntax.
  • When performance is a concern, as Named Queries are pre-compiled and cached.

Criteria Query is preferred in the following scenarios:

  • When the query needs to be constructed dynamically at runtime.
  • When the query involves complex conditions or joins that are difficult to express using JPQL syntax.
  • When type-safety is important, as Criteria Query is a type-safe way to build queries.

In general, the choice between Named Queries and Criteria Query depends on the specific requirements of the application and the complexity of the queries.

Follow-up 2

How do Named Queries and Criteria Query handle SQL injection?

Both Named Queries and Criteria Query provide protection against SQL injection by automatically escaping special characters in the query parameters. When using Named Queries, you can pass parameters using named placeholders or positional placeholders, and the JPA implementation will handle the parameter binding and escaping. Similarly, when using Criteria Query, you can use the setParameter method to bind parameters, and the JPA implementation will handle the parameter binding and escaping.

By using parameter binding and automatic escaping, Named Queries and Criteria Query help prevent SQL injection attacks by ensuring that user input is treated as data and not as part of the SQL query.

Follow-up 3

How do they handle complex queries?

Both Named Queries and Criteria Query can handle complex queries, but they have different approaches.

  • Named Queries: Named Queries are defined using JPQL syntax, which allows you to express complex queries involving multiple entities, joins, and conditions. You can use JPQL functions, operators, and expressions to build complex queries. However, the complexity of the query is limited to what can be expressed using JPQL syntax.

  • Criteria Query: Criteria Query provides a more flexible and expressive way to build complex queries at runtime. You can use a set of Java classes and methods to construct queries involving multiple entities, joins, conditions, aggregations, and subqueries. Criteria Query allows you to build queries dynamically based on runtime conditions, making it suitable for complex and dynamic queries.

In summary, Named Queries are limited to the complexity of JPQL syntax, while Criteria Query provides more flexibility and expressiveness for handling complex queries.

4. How do you use parameters in Named Queries?

Named Queries support named parameters (:paramName) and positional parameters (?1). Named parameters are strongly preferred for readability and safety.

Named parameters (recommended):

import jakarta.persistence.*;

@Entity
@NamedQuery(
    name = "Employee.findByDeptAndSalary",
    query = "SELECT e FROM Employee e WHERE e.department.name = :deptName AND e.salary >= :minSalary"
)
public class Employee {
    @Id @GeneratedValue private Long id;
    private String name;
    private double salary;
    @ManyToOne private Department department;
}
// Binding named parameters at execution time
List employees = em.createNamedQuery("Employee.findByDeptAndSalary", Employee.class)
    .setParameter("deptName", "Engineering")
    .setParameter("minSalary", 80000.0)
    .getResultList();

Positional parameters (less common, JPQL-compliant):

@NamedQuery(
    name = "Employee.findByName",
    query = "SELECT e FROM Employee e WHERE e.name = ?1"
)
em.createNamedQuery("Employee.findByName", Employee.class)
    .setParameter(1, "Alice")
    .getSingleResult();

Collection parameters (IN clause):

@NamedQuery(
    name = "Product.findByCategories",
    query = "SELECT p FROM Product p WHERE p.category IN :categories"
)
em.createNamedQuery("Product.findByCategories", Product.class)
    .setParameter("categories", List.of("Electronics", "Books"))
    .getResultList();

Key rules:

  • Parameter names must match exactly between the query string and setParameter() calls.
  • You cannot use null directly as a parameter value in a WHERE field = :param clause — use IS NULL in the query instead.
  • Hibernate 6 validates parameter types against the entity metadata at query preparation, catching type mismatches early.
↑ Back to top

Follow-up 1

What happens if you don't provide a value for a parameter in a Named Query?

If you don't provide a value for a parameter in a Named Query, the query execution may fail or return unexpected results, depending on the database and ORM framework you are using. Some frameworks may throw an exception indicating a missing parameter, while others may substitute a default value or treat the parameter as null. It's important to ensure that all required parameters are properly bound before executing a Named Query to avoid errors or incorrect results.

Follow-up 2

What is the syntax for using parameters in Named Queries?

The syntax for using parameters in Named Queries depends on the programming language or framework you are using. In most cases, you can use a placeholder, such as a question mark (?) or a colon followed by the parameter name (:param), in the query string. Then, when executing the query, you can bind values to these parameters using the appropriate method or function provided by the database library or ORM.

Follow-up 3

Can you give an example of a Named Query with parameters?

Sure! Here's an example of a Named Query in Java using the Hibernate ORM framework:

@NamedQuery(
    name = "findUsersByAge",
    query = "SELECT u FROM User u WHERE u.age >= :minAge AND u.age <= :maxAge"
)
public class User {
    // ...
}

In this example, the Named Query findUsersByAge takes two parameters, minAge and maxAge, and selects all users whose age falls within the specified range.

5. What are the restrictions of Criteria Query?

The JPA Criteria API is powerful, but interviewers expect you to know its genuine limitations compared to HQL/JPQL:

1. Verbosity and reduced readability

A simple HQL query takes one line; the equivalent Criteria API requires 5-10 lines of boilerplate. This makes complex queries hard to read and review.

2. No support for HQL-exclusive features

Several Hibernate HQL extensions are not available or incomplete in the Criteria API:

  • INSERT INTO ... SELECT (bulk insert) — not supported
  • UPDATE and DELETE DML use a separate CriteriaUpdate/CriteriaDelete API (added in JPA 2.1), but support varies
  • Some Hibernate-specific functions (e.g., array_agg, database-specific functions) are harder to express

3. Subqueries are cumbersome

Correlated subqueries require Subquery objects and are significantly more verbose than in HQL.

// Subquery: products whose price > average
Subquery subquery = cq.subquery(Double.class);
Root subRoot = subquery.from(Product.class);
subquery.select(cb.avg(subRoot.get("price")));
cq.where(cb.greaterThan(root.get("price"), subquery));

4. Metamodel dependency for full type safety

True compile-time type safety requires the JPA static metamodel (Product_, Order_, etc.), which must be generated by an annotation processor. Without it, you fall back to string-based root.get("fieldName"), which is no safer than HQL.

5. Dynamic ORDER BY and GROUP BY are awkward

Building dynamic ORDER BY lists requires constructing Order objects programmatically, which is more cumbersome than appending strings in HQL.

When these restrictions matter least: The Criteria API shines for dynamic WHERE clause construction (optional filters). For anything with complex joins, subqueries, or DML, HQL is generally more practical.

↑ Back to top

Follow-up 1

Can you perform joins using Criteria Query?

Yes, you can perform joins using Criteria Query. However, the support for complex joins is limited compared to SQL queries. Simple joins can be achieved using the join() method of the Root or Join objects.

Follow-up 2

How do you handle subqueries in Criteria Query?

Handling subqueries in Criteria Query can be challenging due to its limited support for complex queries. One way to handle subqueries is by using the subquery() method of the CriteriaBuilder class. This method allows you to create a subquery and use it as a predicate in the main query.

Follow-up 3

Can you use native SQL in Criteria Query?

Yes, you can use native SQL in Criteria Query by using the createNativeQuery() method of the EntityManager class. This method allows you to execute native SQL queries and map the results to entities or DTOs. However, using native SQL in Criteria Query defeats the purpose of using a type-safe query API.

Live mock interview

Mock interview: Named Queries and Criteria Query

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.

Next lesson Criteria API →