Querying Hibernate

Querydsl provides Hibernate-specific extensions on top of the standard JPA support. For common features such as basic querying, joins, subqueries, and DML operations, see the JPA tutorial.

This page covers features exclusive to the Hibernate API.

Maven Integration

The Hibernate integration uses the same querydsl-jpa artifact:

<dependency>
  <groupId>io.github.openfeign.querydsl</groupId>
  <artifactId>querydsl-jpa</artifactId>
  <version>7.1</version>
</dependency>

If your domain model uses Hibernate-specific annotations, configure the annotation processor with HibernateAnnotationProcessor:

<plugin>
  <artifactId>maven-compiler-plugin</artifactId>
  <configuration>
    <generatedSourcesDirectory>target/generated-sources/java</generatedSourcesDirectory>
  </configuration>
  <dependencies>
    <dependency>
      <groupId>io.github.openfeign.querydsl</groupId>
      <artifactId>querydsl-apt</artifactId>
      <version>7.1</version>
      <classifier>jpa</classifier>
    </dependency>
    <dependency>
      <groupId>jakarta.persistence</groupId>
      <artifactId>jakarta.persistence-api</artifactId>
      <version>3.1.0</version>
    </dependency>
  </dependencies>
</plugin>

For standard JPA annotations, JPAAnnotationProcessor works as well. See the JPA tutorial for details.

Creating Queries

Use HibernateQuery with a Hibernate Session:

HibernateQuery<?> query = new HibernateQuery<Void>(session);

Or use HibernateQueryFactory as the recommended approach:

HibernateQueryFactory queryFactory = new HibernateQueryFactory(session);

All standard JPQL query operations (from, where, join, groupBy, orderBy, etc.) work the same way as described in the JPA tutorial.

HibernateQuery also supports StatelessSession:

HibernateQuery<?> query = new HibernateQuery<Void>(statelessSession);

Hibernate-Specific Query Options

HibernateQuery provides several options not available on JPAQuery.

Query Caching

List<Cat> cats = queryFactory.selectFrom(cat)
    .where(cat.name.startsWith("A"))
    .setCacheable(true)
    .setCacheRegion("catCache")
    .fetch();

Read-Only Mode

Entities loaded in read-only mode are never dirty-checked:

List<Cat> cats = queryFactory.selectFrom(cat)
    .setReadOnly(true)
    .fetch();

SQL Comments

List<Cat> cats = queryFactory.selectFrom(cat)
    .setComment("load cats by name")
    .fetch();

Lock Modes

List<Cat> cats = queryFactory.selectFrom(cat)
    .setLockMode(cat, LockMode.PESSIMISTIC_WRITE)
    .fetch();

Flush Mode

List<Cat> cats = queryFactory.selectFrom(cat)
    .setFlushMode(FlushMode.AUTO)
    .fetch();

Fetch Size and Timeout

List<Cat> cats = queryFactory.selectFrom(cat)
    .setFetchSize(50)
    .setTimeout(30)
    .fetch();

Scrollable Results

ScrollableResults results = queryFactory.selectFrom(cat)
    .createQuery()
    .scroll(ScrollMode.FORWARD_ONLY);

Exposing the Original Query

To access the underlying Hibernate Query directly:

Query hibernateQuery = queryFactory.selectFrom(cat).createQuery();
List results = hibernateQuery.list();

This returns a Hibernate org.hibernate.query.Query rather than a JPA jakarta.persistence.Query.

Common Table Expressions

HibernateQuery supports Common Table Expressions (CTEs) via the with() method family. CTEs define named temporary result sets that can be referenced in the main query. This feature requires Hibernate 6.5 or later and is not available on JPAQuery.

Method Description
with(alias, subquery) Define a CTE
withMaterializedHint(alias, subquery) Define a CTE with MATERIALIZED hint
withNotMaterializedHint(alias, subquery) Define a CTE with NOT MATERIALIZED hint

Simple CTE with Join

Define a CTE that selects a specific cat’s weight, then join against it to find lighter cats:

QCat cat = QCat.cat;
QCat felix = new QCat("felix");

Cat result = new HibernateQuery<Cat>(session)
    .withNotMaterializedHint(felix,
        JPAExpressions.select(cat.bodyWeight.as(felix.bodyWeight))
            .from(cat)
            .where(cat.name.eq("Felix")))
    .select(cat)
    .from(felix)
    .join(cat).on(cat.bodyWeight.lt(felix.bodyWeight))
    .orderBy(cat.bodyWeight.desc())
    .limit(1)
    .fetchOne();

Generated HQL:

with
felix as not materialized (select cat.bodyWeight as bodyWeight
from Cat cat
where cat.name = ?1)
select cat
from Cat cat, felix felix
where cat.bodyWeight > felix.bodyWeight

CTE with Custom Column

Use Expressions.numberPath() to create custom column references within a CTE:

QCat cat = QCat.cat;
QCat avgCat = new QCat("avgcat");
NumberPath<Double> avgWeight = Expressions.numberPath(Double.class, avgCat, "avgweight");

List<Cat> results = new HibernateQuery<Cat>(session)
    .with(avgCat,
        JPAExpressions.select(cat.bodyWeight.avg().as(avgWeight))
            .from(cat))
    .select(cat)
    .from(cat, avgCat)
    .orderBy(cat.bodyWeight.subtract(avgWeight).abs().asc(), cat.id.asc())
    .fetch();

Multiple CTEs

Chain .with() calls to define multiple CTEs. Later CTEs can reference earlier ones:

QCat cat = QCat.cat;
QCat felix = new QCat("felix");
QCat felixMates = new QCat("felixMates");

List<Integer> results = new HibernateQuery<Integer>(session)
    .with(felix,
        JPAExpressions.select(cat.id.as(felix.id))
            .from(cat)
            .where(cat.name.eq("Felix")))
    .with(felixMates,
        JPAExpressions.select(cat.id.as(cat.id))
            .from(cat)
            .innerJoin(felix).on(cat.mate.id.eq(felix.id)))
    .select(felixMates.id)
    .from(felixMates)
    .fetch();

CTE aliases reuse existing Q-types (e.g., new QCat("felix")) to define CTE columns. Use as() in projections to map columns from the subquery to the CTE alias fields. The JPAExpressions factory is used for CTE subqueries, same as for standard JPQL subqueries.

Native SQL with Hibernate

Use HibernateSQLQuery to run native SQL through a Hibernate Session:

SQLTemplates templates = new H2Templates();
HibernateSQLQuery<?> query = new HibernateSQLQuery<Void>(session, templates);
List<String> names = query.select(cat.name).from(cat).fetch();

See the JPA tutorial for more details on native SQL query patterns.

Comparison with JPAQuery

Feature JPAQuery HibernateQuery
Underlying API JPA EntityManager Hibernate Session
Query factory JPAQueryFactory HibernateQueryFactory
Common Table Expressions Not available with(), withMaterializedHint(), withNotMaterializedHint()
Query caching Not available setCacheable(), setCacheRegion()
Read-only mode Not available setReadOnly()
SQL comments Not available setComment()
Lock modes JPA lock modes Hibernate LockMode per path
Flush mode Not available setFlushMode()
Scrollable results Not available scroll(ScrollMode)
StatelessSession Not available Supported
Native SQL class JPASQLQuery HibernateSQLQuery