@@ -154,6 +154,102 @@ List results = hibernateQuery.list();
154154This returns a Hibernate ` org.hibernate.query.Query ` rather than a JPA
155155` jakarta.persistence.Query ` .
156156
157+ ## Common Table Expressions
158+
159+ ` HibernateQuery ` supports Common Table Expressions (CTEs) via the ` with() `
160+ method family. CTEs define named temporary result sets that can be referenced
161+ in the main query. This feature requires Hibernate 6.5 or later and is not
162+ available on ` JPAQuery ` .
163+
164+ | Method | Description |
165+ | ---| ---|
166+ | ` with(alias, subquery) ` | Define a CTE |
167+ | ` withMaterializedHint(alias, subquery) ` | Define a CTE with ` MATERIALIZED ` hint |
168+ | ` withNotMaterializedHint(alias, subquery) ` | Define a CTE with ` NOT MATERIALIZED ` hint |
169+
170+ ### Simple CTE with Join
171+
172+ Define a CTE that selects a specific cat's weight, then join against it to
173+ find lighter cats:
174+
175+ ``` java
176+ QCat cat = QCat . cat;
177+ QCat felix = new QCat (" felix" );
178+
179+ Cat result = new HibernateQuery<Cat > (session)
180+ .withNotMaterializedHint(felix,
181+ JPAExpressions . select(cat. bodyWeight. as(felix. bodyWeight))
182+ .from(cat)
183+ .where(cat. name. eq(" Felix" )))
184+ .select(cat)
185+ .from(felix)
186+ .join(cat). on(cat. bodyWeight. lt(felix. bodyWeight))
187+ .orderBy(cat. bodyWeight. desc())
188+ .limit(1 )
189+ .fetchOne();
190+ ```
191+
192+ Generated HQL:
193+
194+ ```
195+ with
196+ felix as not materialized (select cat.bodyWeight as bodyWeight
197+ from Cat cat
198+ where cat.name = ?1)
199+ select cat
200+ from Cat cat, felix felix
201+ where cat.bodyWeight > felix.bodyWeight
202+ ```
203+
204+ ### CTE with Custom Column
205+
206+ Use ` Expressions.numberPath() ` to create custom column references within a CTE:
207+
208+ ``` java
209+ QCat cat = QCat . cat;
210+ QCat avgCat = new QCat (" avgcat" );
211+ NumberPath<Double > avgWeight = Expressions . numberPath(Double . class, avgCat, " avgweight" );
212+
213+ List<Cat > results = new HibernateQuery<Cat > (session)
214+ .with(avgCat,
215+ JPAExpressions . select(cat. bodyWeight. avg(). as(avgWeight))
216+ .from(cat))
217+ .select(cat)
218+ .from(cat, avgCat)
219+ .orderBy(cat. bodyWeight. subtract(avgWeight). abs(). asc(), cat. id. asc())
220+ .fetch();
221+ ```
222+
223+ ### Multiple CTEs
224+
225+ Chain ` .with() ` calls to define multiple CTEs. Later CTEs can reference earlier
226+ ones:
227+
228+ ``` java
229+ QCat cat = QCat . cat;
230+ QCat felix = new QCat (" felix" );
231+ QCat felixMates = new QCat (" felixMates" );
232+
233+ List<Integer > results = new HibernateQuery<Integer > (session)
234+ .with(felix,
235+ JPAExpressions . select(cat. id. as(felix. id))
236+ .from(cat)
237+ .where(cat. name. eq(" Felix" )))
238+ .with(felixMates,
239+ JPAExpressions . select(cat. id. as(cat. id))
240+ .from(cat)
241+ .innerJoin(felix). on(cat. mate. id. eq(felix. id)))
242+ .select(felixMates. id)
243+ .from(felixMates)
244+ .fetch();
245+ ```
246+
247+ {: .note }
248+ CTE aliases reuse existing Q-types (e.g., ` new QCat("felix") ` ) to define CTE
249+ columns. Use ` as() ` in projections to map columns from the subquery to the CTE
250+ alias fields. The ` JPAExpressions ` factory is used for CTE subqueries, same as
251+ for standard JPQL subqueries.
252+
157253## Native SQL with Hibernate
158254
159255Use ` HibernateSQLQuery ` to run native SQL through a Hibernate ` Session ` :
@@ -173,6 +269,7 @@ for more details on native SQL query patterns.
173269| ---| ---| ---|
174270| Underlying API | JPA EntityManager | Hibernate Session |
175271| Query factory | ` JPAQueryFactory ` | ` HibernateQueryFactory ` |
272+ | Common Table Expressions | Not available | ` with() ` , ` withMaterializedHint() ` , ` withNotMaterializedHint() ` |
176273| Query caching | Not available | ` setCacheable() ` , ` setCacheRegion() ` |
177274| Read-only mode | Not available | ` setReadOnly() ` |
178275| SQL comments | Not available | ` setComment() ` |
0 commit comments