Hibernate 6.2 introduced CTE support but it's unclear how to use multiple CTEs in the single HQL query. So let's say I have City entity and the following CTE query:
TypedQuery<Integer> typedQuery = em.createQuery(
"""
with max_cities as (
SELECT id id from City c ORDER BY population DESC
LIMIT 20
), min_cities as (
SELECT id id from City c ORDER BY population ASC
LIMIT 20
)
SELECT m1.id from max_cities m1 join min_cities m2
on m1.id = m2.id
""",
Integer.class);
It contains two CTEs (min_cities and max_cities). And Hibernate doesn't complain about that. But the error message is pretty clear here:
Caused by: java.lang.IllegalArgumentException: Could not resolve entity reference: min_cities
at org.hibernate.metamodel.model.domain.internal.JpaMetamodelImpl.resolveHqlEntityReference(JpaMetamodelImpl.java:166)
So Hibernate allows to use first CTE in FROM clause but expects entity (and not CTE) in the JOIN clause. However MySQL documentation states that joining two CTEs is pretty legal:
WITH
cte1 AS (SELECT a, b FROM table1),
cte2 AS (SELECT c, d FROM table2)
SELECT b, d FROM cte1 JOIN cte2
WHERE cte1.a = cte2.c;
So is joining CTEs supported in Hibernate ?
That looks like a bug in ExpectingEntityJoinDelegate#consumeIdentifier:
@Override
public void consumeIdentifier(String identifier, boolean isTerminal, boolean allowReuse) {
if ( path.length() != 0 ) {
path.append( '.' );
}
path.append( identifier );
if ( isTerminal ) {
final String fullPath = path.toString();
final EntityDomainType<?> joinedEntityType = creationState.getCreationContext()
.getJpaMetamodel()
.resolveHqlEntityReference( fullPath );
// here impl assumes that resolveHqlEntityReference may return nulls,
// however it never does that. Most probably getHqlEntityReference
// should be used instead
if ( joinedEntityType == null ) {
final SqmCteStatement<?> cteStatement = creationState.findCteStatement( fullPath );
if ( cteStatement != null ) {
join = new SqmCteJoin<>( cteStatement, alias, joinType, sqmRoot );
creationState.getCurrentProcessingState().getPathRegistry().register( join );
return;
}
throw new SemanticException( "Could not resolve join path - " + fullPath );
}
BTW, cross join does work as expected, i.e.:
"""
with max_cities as (
SELECT id id from City c ORDER BY population DESC
LIMIT 20
), min_cities as (
SELECT id id from City c ORDER BY population ASC
LIMIT 20
)
SELECT m1.id from max_cities m1, min_cities m2
where m1.id = m2.id
"""
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With