From 4da2659292f140d290b51b2b178039cf09c1c4c2 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Mon, 29 Jul 2024 15:11:18 +0200 Subject: [PATCH] HHH-18378 Check where clause before reusing existing joins for fetch --- .../hibernate/query/sqm/internal/SqmUtil.java | 13 ++++++- .../sqm/sql/BaseSqmToSqlAstConverter.java | 5 ++- .../query/sqm/tree/select/SqmQuerySpec.java | 38 ++++++++++++++----- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java index 1f02cb1229..d6a63c5cb3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java @@ -74,6 +74,7 @@ import org.hibernate.query.sqm.tree.from.SqmFrom; import org.hibernate.query.sqm.tree.from.SqmJoin; import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin; import org.hibernate.query.sqm.tree.from.SqmRoot; +import org.hibernate.query.sqm.tree.predicate.SqmWhereClause; import org.hibernate.query.sqm.tree.select.SqmOrderByClause; import org.hibernate.query.sqm.tree.select.SqmQueryGroup; import org.hibernate.query.sqm.tree.select.SqmQueryPart; @@ -105,6 +106,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import static java.util.stream.Collectors.toList; import static org.hibernate.internal.util.NullnessUtil.castNonNull; +import static org.hibernate.internal.util.collections.CollectionHelper.arrayList; import static org.hibernate.query.sqm.tree.jpa.ParameterCollector.collectParameters; /** @@ -232,6 +234,15 @@ public class SqmUtil { return false; } + public static List getWhereClauseNavigablePaths(SqmQuerySpec querySpec) { + final SqmWhereClause where = querySpec.getWhereClause(); + if ( where == null || where.getPredicate() == null ) { + return Collections.emptyList(); + } + + return collectNavigablePaths( List.of( where.getPredicate() ) ); + } + public static List getGroupByNavigablePaths(SqmQuerySpec querySpec) { final List> expressions = querySpec.getGroupByClauseExpressions(); if ( expressions.isEmpty() ) { @@ -255,7 +266,7 @@ public class SqmUtil { } private static List collectNavigablePaths(final List> expressions) { - final List navigablePaths = new ArrayList<>( expressions.size() ); + final List navigablePaths = arrayList( expressions.size() ); final SqmPathVisitor pathVisitor = new SqmPathVisitor( path -> navigablePaths.add( path.getNavigablePath() ) ); for ( final SqmExpression expression : expressions ) { if ( expression instanceof SqmAliasedNodeRef ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index da3f5833c5..d52065febb 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -8406,7 +8406,10 @@ public abstract class BaseSqmToSqlAstConverter extends Base joinProducer, joinProducer.determineSqlJoinType( lhs, null, true ) ); - if ( compatibleTableGroup == null ) { + final SqmQueryPart queryPart = getCurrentSqmQueryPart(); + if ( compatibleTableGroup == null + // If the compatible table group is used in the where clause it cannot be reused for fetching + || ( queryPart != null && queryPart.getFirstQuerySpec().whereClauseContains( compatibleTableGroup.getNavigablePath(), this ) ) ) { final TableGroupJoin tableGroupJoin = joinProducer.createTableGroupJoin( fetchablePath, lhs, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java index 35d6a47f70..724702ec79 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmQuerySpec.java @@ -629,15 +629,26 @@ public class SqmQuerySpec extends SqmQueryPart super.appendHqlString( sb ); } + @Internal + public boolean whereClauseContains(NavigablePath navigablePath, SqmToSqlAstConverter sqlAstConverter) { + if ( whereClause == null ) { + return false; + } + return isSameOrParent( + navigablePath, + sqlAstConverter.resolveMetadata( this, SqmUtil::getWhereClauseNavigablePaths ) + ); + } + @Internal public boolean groupByClauseContains(NavigablePath navigablePath, SqmToSqlAstConverter sqlAstConverter) { if ( groupByClauseExpressions.isEmpty() ) { return false; } - return navigablePathsContain( sqlAstConverter.resolveMetadata( - this, - SqmUtil::getGroupByNavigablePaths - ), navigablePath ); + return isSameOrChildren( + navigablePath, + sqlAstConverter.resolveMetadata( this, SqmUtil::getGroupByNavigablePaths ) + ); } @Internal @@ -646,13 +657,13 @@ public class SqmQuerySpec extends SqmQueryPart if ( orderByClause == null || orderByClause.getSortSpecifications().isEmpty() ) { return false; } - return navigablePathsContain( sqlAstConverter.resolveMetadata( - this, - SqmUtil::getOrderByNavigablePaths - ), navigablePath ); + return isSameOrChildren( + navigablePath, + sqlAstConverter.resolveMetadata( this, SqmUtil::getOrderByNavigablePaths ) + ); } - private boolean navigablePathsContain(List navigablePaths, NavigablePath navigablePath) { + private boolean isSameOrChildren(NavigablePath navigablePath, List navigablePaths) { for ( NavigablePath path : navigablePaths ) { if ( path.isParentOrEqual( navigablePath ) ) { return true; @@ -660,4 +671,13 @@ public class SqmQuerySpec extends SqmQueryPart } return false; } + + private boolean isSameOrParent(NavigablePath navigablePath, List navigablePaths) { + for ( NavigablePath path : navigablePaths ) { + if ( navigablePath.isParentOrEqual( path ) ) { + return true; + } + } + return false; + } }