Tutorials Logic, IN info@tutorialslogic.com

Hibernate HQL Query Language

What is HQL?

HQL (Hibernate Query Language) is an object-oriented query language similar to SQL, but instead of operating on tables and columns, it operates on entity classes and their properties. HQL is database-independent - Hibernate translates it to the appropriate SQL dialect.

Key differences from SQL:

  • Use class names instead of table names: FROM User not FROM users
  • Use property names instead of column names: u.firstName not u.first_name
  • Case-sensitive for class/property names, case-insensitive for keywords

HQL SELECT, FROM, WHERE

HQL SELECT, FROM, WHERE
Session session = sessionFactory.openSession();

// SELECT all users
List<User> users = session.createQuery("FROM User", User.class).list();

// SELECT with alias
List<User> users2 = session.createQuery("FROM User u", User.class).list();

// SELECT specific columns (returns Object[])
List<Object[]> names = session.createQuery(
    "SELECT u.firstName, u.email FROM User u", Object[].class).list();

// WHERE clause
List<User> admins = session.createQuery(
    "FROM User u WHERE u.role = 'ADMIN'", User.class).list();

// WHERE with AND/OR
List<User> result = session.createQuery(
    "FROM User u WHERE u.age >= 18 AND u.active = true", User.class).list();

// ORDER BY
List<User> sorted = session.createQuery(
    "FROM User u ORDER BY u.lastName ASC, u.firstName ASC", User.class).list();

// LIKE
List<User> matching = session.createQuery(
    "FROM User u WHERE u.email LIKE '%@gmail.com'", User.class).list();

// IN
List<User> inList = session.createQuery(
    "FROM User u WHERE u.role IN ('ADMIN', 'MODERATOR')", User.class).list();

// BETWEEN
List<User> ageRange = session.createQuery(
    "FROM User u WHERE u.age BETWEEN 20 AND 30", User.class).list();

// IS NULL / IS NOT NULL
List<User> noEmail = session.createQuery(
    "FROM User u WHERE u.email IS NULL", User.class).list();

session.close();

Parameters, Aggregates, and Named Queries

Parameters, Aggregates, and Named Queries
Session session = sessionFactory.openSession();

// Named parameters (preferred - prevents SQL injection)
List<User> users = session.createQuery(
    "FROM User u WHERE u.email = :email AND u.role = :role", User.class)
    .setParameter("email", "alice@example.com")
    .setParameter("role", "ADMIN")
    .list();

// Positional parameters (legacy)
List<User> users2 = session.createQuery(
    "FROM User u WHERE u.email = ?1", User.class)
    .setParameter(1, "alice@example.com")
    .list();

// Aggregate functions
Long count = session.createQuery(
    "SELECT COUNT(u) FROM User u", Long.class).uniqueResult();

Double avgAge = session.createQuery(
    "SELECT AVG(u.age) FROM User u", Double.class).uniqueResult();

// GROUP BY
List<Object[]> roleCount = session.createQuery(
    "SELECT u.role, COUNT(u) FROM User u GROUP BY u.role", Object[].class).list();

// Pagination
List<User> page = session.createQuery("FROM User u ORDER BY u.id", User.class)
    .setFirstResult(0)   // Offset (0-based)
    .setMaxResults(10)   // Limit
    .list();

// UPDATE query
int updated = session.createMutationQuery(
    "UPDATE User u SET u.active = false WHERE u.lastLogin < :date")
    .setParameter("date", java.time.LocalDate.now().minusYears(1))
    .executeUpdate();

// DELETE query
int deleted = session.createMutationQuery(
    "DELETE FROM User u WHERE u.active = false")
    .executeUpdate();

session.close();

Named Queries

@NamedQuery and @NamedNativeQuery

@NamedQuery and @NamedNativeQuery
// Define named queries on the entity class
@Entity
@NamedQueries({
    @NamedQuery(
        name = "User.findByEmail",
        query = "FROM User u WHERE u.email = :email"
    ),
    @NamedQuery(
        name = "User.findActiveUsers",
        query = "FROM User u WHERE u.active = true ORDER BY u.username"
    ),
    @NamedQuery(
        name = "User.countByRole",
        query = "SELECT COUNT(u) FROM User u WHERE u.role = :role"
    )
})
@NamedNativeQuery(
    name = "User.findByEmailNative",
    query = "SELECT * FROM users WHERE email = :email",
    resultClass = User.class
)
public class User { ... }

// Use named queries
Session session = sessionFactory.openSession();

User user = session.createNamedQuery("User.findByEmail", User.class)
        .setParameter("email", "alice@example.com")
        .uniqueResult();

List<User> activeUsers = session.createNamedQuery("User.findActiveUsers", User.class)
        .list();

Long adminCount = session.createNamedQuery("User.countByRole", Long.class)
        .setParameter("role", "ADMIN")
        .uniqueResult();

session.close();

When HQL Is the Clearest Query Tool

HQL expresses reads and bulk writes with entity names, associations, and mapped properties while Hibernate translates the query to SQL.

Use it when the query shape is known and remains readable as text. Dynamic search forms with many optional predicates may be clearer with Criteria, while native SQL is justified only when database-specific behavior is required.

  • Use constructor projections for DTO read models.
  • Use named parameters for safe values.
  • Use join fetch intentionally to solve known lazy loading needs.
  • Avoid bulk updates unless persistence context effects are understood.
  • Keep database-specific functions out unless portability is not required.

Fetch Plans, Projections, and Pagination

Select only the data the use case needs. Constructor projections can create read models without loading full entities, while join fetch can solve a known lazy-loading problem. Fetching a collection multiplies rows and can make pagination incorrect or expensive, so page root identifiers first when necessary.

Apply a stable order before setFirstResult and setMaxResults. Inspect generated SQL and query counts with representative data, and avoid solving every N+1 issue by fetching an entire object graph. A deliberate fetch plan should match one request, not become a global mapping default.

Before you move on

Hibernate HQL Query Language Mastery Check

4 checks
  • Write HQL with entity and property names rather than table and column names.
  • Bind every external value and apply deterministic ordering before pagination.
  • Use JOIN FETCH only when its row multiplication and pagination effects are understood.
  • Clear or refresh the persistence context after a bulk update that bypasses managed entity state.

Hibernate Questions Learners Ask

HQL queries the mapped object model. Hibernate translates entity names, fields, and relationships into database SQL using mapping metadata.

Parameters prevent injection, handle escaping and types correctly, and let the database reuse query plans. Concatenating a name, status, or date into HQL is unsafe and error-prone. Dynamic query structure should be assembled from whitelisted clauses, while all user values remain bound parameters.

Joining a parent to a collection produces one SQL row per matching child. Hibernate may therefore return repeated parent references unless the query uses distinct semantics appropriately. Fetching multiple bag collections can create even larger Cartesian products.

Next Step
Next Practice

Finish the concept here, then reinforce it with hands-on coding, interview prep, or a tool that matches the topic.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.