From 1456a2dd7f601b90c1bee67e743a758ca9801b04 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Sat, 23 Oct 2021 15:34:39 +0200 Subject: [PATCH] Render implicit joins as nested table group joins instead of sub queries --- .../ast/internal/LoaderSelectBuilder.java | 1 + .../AbstractCompositeIdentifierMapping.java | 8 +- .../internal/EmbeddedAttributeMapping.java | 12 +- .../internal/EmbeddedCollectionPart.java | 12 +- .../EmbeddedForeignKeyDescriptor.java | 2 +- .../internal/EntityCollectionPart.java | 2 + .../internal/PluralAttributeMappingImpl.java | 13 +- .../internal/ToOneAttributeMapping.java | 20 +- .../query/results/TableGroupImpl.java | 17 +- .../dynamic/DynamicFetchBuilderLegacy.java | 1 + .../ImplicitFetchBuilderEmbeddable.java | 1 + ...licitModelPartResultBuilderEmbeddable.java | 1 + .../sqm/sql/BaseSqmToSqlAstConverter.java | 212 +++--------------- .../EntityValuedPathInterpretation.java | 1 + .../sql/ast/spi/AbstractSqlAstTranslator.java | 28 ++- .../sql/ast/tree/cte/CteTableGroup.java | 15 +- .../sql/ast/tree/from/AbstractTableGroup.java | 31 ++- .../ast/tree/from/CompositeTableGroup.java | 39 +++- .../ast/tree/from/CorrelatedTableGroup.java | 8 + .../sql/ast/tree/from/LazyTableGroup.java | 18 +- .../MutatingTableReferenceGroupWrapper.java | 15 +- .../sql/ast/tree/from/StandardTableGroup.java | 8 +- .../sql/ast/tree/from/TableGroup.java | 8 +- .../ast/tree/from/TableGroupJoinProducer.java | 4 +- .../sql/ast/tree/from/UnionTableGroup.java | 36 ++- .../EntityCollectionPartTableGroup.java | 18 +- .../internal/EmbeddableFetchImpl.java | 1 + .../internal/EmbeddableResultImpl.java | 1 + .../orm/test/hql/WithClauseTest.java | 2 - .../orm/test/sql/ast/SmokeTests.java | 4 +- .../function/OrderByFragmentFunction.java | 18 +- 31 files changed, 322 insertions(+), 235 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java index 7bfde93af6..f125e102f7 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java @@ -421,6 +421,7 @@ public class LoaderSelectBuilder { null, SqlAstJoinType.LEFT, true, + false, sqlAstCreationState ); tableGroup = tableGroupJoin.getJoinedGroup(); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java index d957db0281..42125d37c4 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java @@ -171,6 +171,7 @@ public abstract class AbstractCompositeIdentifierMapping String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, + boolean nested, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { @@ -187,7 +188,12 @@ public abstract class AbstractCompositeIdentifierMapping ); final TableGroupJoin join = new TableGroupJoin( navigablePath, SqlAstJoinType.LEFT, tableGroup, null ); - lhs.addTableGroupJoin( join ); + if ( nested ) { + lhs.addNestedTableGroupJoin( join ); + } + else { + lhs.addTableGroupJoin( join ); + } return join; } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java index 887e837baa..c8d33f2030 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java @@ -284,6 +284,7 @@ public class EmbeddedAttributeMapping String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, + boolean nested, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { @@ -299,14 +300,19 @@ public class EmbeddedAttributeMapping creationContext ); - final TableGroupJoin tableGroupJoin = new TableGroupJoin( + final TableGroupJoin join = new TableGroupJoin( navigablePath, sqlAstJoinType, tableGroup ); - lhs.addTableGroupJoin( tableGroupJoin ); + if ( nested ) { + lhs.addNestedTableGroupJoin( join ); + } + else { + lhs.addTableGroupJoin( join ); + } - return tableGroupJoin; + return join; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java index 86c15419e6..c22652250d 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java @@ -202,6 +202,7 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, + boolean nested, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { @@ -217,14 +218,19 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF creationContext ); - final TableGroupJoin tableGroupJoin = new TableGroupJoin( + final TableGroupJoin join = new TableGroupJoin( navigablePath, sqlAstJoinType, tableGroup, null ); - lhs.addTableGroupJoin( tableGroupJoin ); - return tableGroupJoin; + if ( nested ) { + lhs.addNestedTableGroupJoin( join ); + } + else { + lhs.addTableGroupJoin( join ); + } + return join; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedForeignKeyDescriptor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedForeignKeyDescriptor.java index 5d31285bd4..b1288d9b42 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedForeignKeyDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedForeignKeyDescriptor.java @@ -37,7 +37,6 @@ import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableReference; -import org.hibernate.sql.ast.tree.from.TableReferenceJoin; import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; import org.hibernate.sql.ast.tree.predicate.Junction; import org.hibernate.sql.ast.tree.predicate.Predicate; @@ -306,6 +305,7 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { null, SqlAstJoinType.INNER, true, + false, creationState.getSqlAstCreationState() ); return tableGroupJoin.getJoinedGroup(); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java index e0cf748cd3..701a2d9fbc 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java @@ -273,6 +273,7 @@ public class EntityCollectionPart String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, + boolean nested, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { @@ -282,6 +283,7 @@ public class EntityCollectionPart explicitSourceAlias, sqlAstJoinType, fetched, + nested, aliasBaseGenerator, sqlExpressionResolver, creationContext diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java index 53abaad457..3c495d21f8 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java @@ -572,6 +572,7 @@ public class PluralAttributeMappingImpl null, SqlAstJoinType.LEFT, true, + false, creationState.getSqlAstCreationState() ); return tableGroupJoin.getJoinedGroup(); @@ -624,6 +625,7 @@ public class PluralAttributeMappingImpl String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, + boolean nested, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { @@ -638,7 +640,7 @@ public class PluralAttributeMappingImpl sqlExpressionResolver, creationContext ); - final TableGroupJoin tableGroupJoin = new TableGroupJoin( + final TableGroupJoin join = new TableGroupJoin( navigablePath, sqlAstJoinType, tableGroup, @@ -651,9 +653,14 @@ public class PluralAttributeMappingImpl ) ); - lhs.addTableGroupJoin( tableGroupJoin ); + if ( nested ) { + lhs.addNestedTableGroupJoin( join ); + } + else { + lhs.addTableGroupJoin( join ); + } - return tableGroupJoin; + return join; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java index e5f6b3a3fa..adffa957de 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java @@ -839,6 +839,7 @@ public class ToOneAttributeMapping null, getDefaultSqlAstJoinType( tableGroup ), true, + false, creationState.getSqlAstCreationState() ); @@ -911,6 +912,7 @@ public class ToOneAttributeMapping sourceAlias, sqlAstJoinType, fetched, + false, creationState.getSqlAstCreationState() ); @@ -929,6 +931,7 @@ public class ToOneAttributeMapping String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, + boolean nested, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { @@ -944,7 +947,7 @@ public class ToOneAttributeMapping sqlExpressionResolver, creationContext ); - final TableGroupJoin tableGroupJoin = new TableGroupJoin( + final TableGroupJoin join = new TableGroupJoin( navigablePath, sqlAstJoinType, lazyTableGroup, @@ -954,7 +957,7 @@ public class ToOneAttributeMapping final TableReference lhsTableReference = lhs.resolveTableReference( navigablePath, identifyingColumnsTableExpression ); lazyTableGroup.setTableGroupInitializerCallback( - tableGroup -> tableGroupJoin.applyPredicate( + tableGroup -> join.applyPredicate( foreignKeyDescriptor.generateJoinPredicate( sideNature == ForeignKeyDescriptor.Nature.TARGET ? lhsTableReference : tableGroup.getPrimaryTableReference(), sideNature == ForeignKeyDescriptor.Nature.TARGET ? tableGroup.getPrimaryTableReference() : lhsTableReference, @@ -964,14 +967,19 @@ public class ToOneAttributeMapping ) ) ); - lhs.addTableGroupJoin( tableGroupJoin ); + if ( nested ) { + lhs.addNestedTableGroupJoin( join ); + } + else { + lhs.addTableGroupJoin( join ); + } if ( sqlAstJoinType == SqlAstJoinType.INNER && isNullable ) { // Force initialization of the underlying table group join to retain cardinality lazyTableGroup.getPrimaryTableReference(); } - return tableGroupJoin; + return join; } @Override @@ -991,7 +999,7 @@ public class ToOneAttributeMapping canUseInnerJoin, navigablePath, fetched, - () -> createTableGroupJoinInternal( + () -> createTableGroupInternal( canUseInnerJoin, navigablePath, fetched, @@ -1064,7 +1072,7 @@ public class ToOneAttributeMapping return getDefaultSqlAstJoinType( tableGroup ); } - public TableGroup createTableGroupJoinInternal( + public TableGroup createTableGroupInternal( boolean canUseInnerJoins, NavigablePath navigablePath, boolean fetched, diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/TableGroupImpl.java b/hibernate-core/src/main/java/org/hibernate/query/results/TableGroupImpl.java index 3b07a24d9a..ce57e0a5de 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/TableGroupImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/TableGroupImpl.java @@ -77,13 +77,13 @@ public class TableGroupImpl implements TableGroup { } @Override - public boolean canUseInnerJoins() { - return false; + public List getNestedTableGroupJoins() { + return Collections.emptyList(); } @Override - public boolean hasTableGroupJoins() { - return tableGroupJoins != null && !tableGroupJoins.isEmpty(); + public boolean canUseInnerJoins() { + return false; } @Override @@ -96,6 +96,11 @@ public class TableGroupImpl implements TableGroup { } } + @Override + public void addNestedTableGroupJoin(TableGroupJoin join) { + addTableGroupJoin( join ); + } + @Override public void visitTableGroupJoins(Consumer consumer) { if ( tableGroupJoins != null ) { @@ -103,6 +108,10 @@ public class TableGroupImpl implements TableGroup { } } + @Override + public void visitNestedTableGroupJoins(Consumer consumer) { + } + @Override public void applyAffectedTableNames(Consumer nameCollector) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderLegacy.java b/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderLegacy.java index fb0fc41346..bf64e4b14b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderLegacy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderLegacy.java @@ -118,6 +118,7 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue tableAlias, SqlAstJoinType.INNER, true, + false, s -> sqlAliasBase, creationState.getSqlExpressionResolver(), creationState.getCreationContext() diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderEmbeddable.java b/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderEmbeddable.java index 235ca5c81c..d41c775d24 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderEmbeddable.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderEmbeddable.java @@ -89,6 +89,7 @@ public class ImplicitFetchBuilderEmbeddable implements ImplicitFetchBuilder { null, SqlAstJoinType.INNER, true, + false, creationStateImpl ); return tableGroupJoin.getJoinedGroup(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitModelPartResultBuilderEmbeddable.java b/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitModelPartResultBuilderEmbeddable.java index 38396f5457..996b825718 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitModelPartResultBuilderEmbeddable.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitModelPartResultBuilderEmbeddable.java @@ -70,6 +70,7 @@ public class ImplicitModelPartResultBuilderEmbeddable null, SqlAstJoinType.INNER, true, + false, creationStateImpl ); 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 4d2ae37bb5..5f66ada55b 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 @@ -23,7 +23,6 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import jakarta.persistence.TemporalType; -import jakarta.persistence.metamodel.EmbeddableType; import org.hibernate.HibernateException; import org.hibernate.Internal; @@ -373,8 +372,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base private final Stack fromClauseIndexStack = new StandardStack<>(); private SqlAstProcessingState lastPoppedProcessingState; private FromClauseIndex lastPoppedFromClauseIndex; - private SqlAstQueryPartProcessingStateImpl joinPredicateWrapper; - private SqmJoin currentJoin; + private SqmJoin currentlyProcessingJoin; private final Stack currentClauseStack = new StandardStack<>(); @@ -2114,6 +2112,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base sqmJoin.getExplicitAlias(), sqmJoin.getSqmJoinType().getCorrespondingSqlJoinType(), sqmJoin.isFetched(), + false, this ); } @@ -2128,6 +2127,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base sqmJoin.getExplicitAlias(), sqmJoin.getSqmJoinType().getCorrespondingSqlJoinType(), sqmJoin.isFetched(), + false, this ); } @@ -2147,22 +2147,10 @@ public abstract class BaseSqmToSqlAstConverter extends Base throw new IllegalStateException(); } - final SqmJoin oldJoin = currentJoin; - currentJoin = sqmJoin; - final Predicate predicate = (Predicate) sqmJoin.getJoinPredicate().accept( this ); - if ( joinPredicateWrapper == null ) { - joinedTableGroupJoin.applyPredicate( predicate ); - } - else { - // See #prepareReusablePath for an explanation why this is necessary - popProcessingStateStack(); - - final QuerySpec querySpec = joinPredicateWrapper.getInflightQueryPart().getFirstQuerySpec(); - querySpec.applyPredicate( predicate ); - joinedTableGroupJoin.applyPredicate( new ExistsPredicate( querySpec, getBooleanType() ) ); - joinPredicateWrapper = null; - } - currentJoin = oldJoin; + final SqmJoin oldJoin = currentlyProcessingJoin; + currentlyProcessingJoin = sqmJoin; + joinedTableGroupJoin.applyPredicate( (Predicate) sqmJoin.getJoinPredicate().accept( this ) ); + currentlyProcessingJoin = oldJoin; } consumeExplicitJoins( sqmJoin, joinedTableGroup ); @@ -2236,29 +2224,20 @@ public abstract class BaseSqmToSqlAstConverter extends Base tableGroup, null ); - lhsTableGroup.addTableGroupJoin( tableGroupJoin ); - - consumeExplicitJoins( sqmJoin, tableGroupJoin.getJoinedGroup() ); // add any additional join restrictions if ( sqmJoin.getJoinPredicate() != null ) { - final SqmJoin oldJoin = currentJoin; - currentJoin = sqmJoin; - final Predicate predicate = (Predicate) sqmJoin.getJoinPredicate().accept( this ); - if ( joinPredicateWrapper == null ) { - tableGroupJoin.applyPredicate( predicate ); - } - else { - // See #prepareReusablePath for an explanation why this is necessary - popProcessingStateStack(); - - final QuerySpec querySpec = joinPredicateWrapper.getInflightQueryPart().getFirstQuerySpec(); - querySpec.applyPredicate( predicate ); - tableGroupJoin.applyPredicate( new ExistsPredicate( querySpec, getBooleanType() ) ); - joinPredicateWrapper = null; - } - currentJoin = oldJoin; + final SqmJoin oldJoin = currentlyProcessingJoin; + currentlyProcessingJoin = sqmJoin; + tableGroupJoin.applyPredicate( (Predicate) sqmJoin.getJoinPredicate().accept( this ) ); + currentlyProcessingJoin = oldJoin; } + + // Note that we add the entity join after processing the predicate because implicit joins needed in there + // can be just ordered right before the entity join without changing the semantics + lhsTableGroup.addTableGroupJoin( tableGroupJoin ); + + consumeExplicitJoins( sqmJoin, tableGroupJoin.getJoinedGroup() ); } private X prepareReusablePath(SqmPath sqmPath, Supplier supplier) { @@ -2271,41 +2250,15 @@ public abstract class BaseSqmToSqlAstConverter extends Base } final FromClauseIndex fromClauseIndex = fromClauseIndexStack.getCurrent(); final boolean useInnerJoin = currentClauseStack.getCurrent() == Clause.SELECT; - if ( currentClauseStack.getCurrent() == Clause.FROM && currentJoin instanceof SqmAttributeJoin - && joinPredicateWrapper == null && needsJoinForPath( fromClauseIndex, sqmPath ) ) { - // If we encounter a reusable path that requires a join in the ON clause of an attribute join - // we have to transform the predicate into `.. on exists (select 1 from ... where ..)` - // Note that we don't need this for e.g. entity joins because the implicit joins needed in such ON clauses - // can be just ordered right before the entity join without changing the semantics - final QuerySpec querySpec = new QuerySpec( false ); - final JdbcLiteral jdbcLiteral = new JdbcLiteral<>( 1, basicType( Integer.class ) ); - querySpec.getSelectClause().addSqlSelection( - new SqlSelectionImpl( 1, 0, jdbcLiteral ) - ); - joinPredicateWrapper = new SqlAstQueryPartProcessingStateImpl( - querySpec, - getCurrentProcessingState(), - this, - currentClauseStack::getCurrent - ); - pushProcessingState( joinPredicateWrapper ); - // Note that the pop must happen where the join predicate is applied - // This is necessary as we want to retain the FromClauseIndex of this newly created sub query - // while we are still processing expressions of the predicate - currentClauseStack.push( Clause.WHERE ); - prepareReusablePath( fromClauseIndex, sqmPath, useInnerJoin, implicitJoinChecker ); - currentClauseStack.pop(); - } - else { - prepareReusablePath( fromClauseIndex, sqmPath, useInnerJoin, implicitJoinChecker ); - } + prepareReusablePath( fromClauseIndex, sqmPath, useInnerJoin, implicitJoinChecker ); return supplier.get(); } private TableGroup prepareReusablePath( FromClauseIndex fromClauseIndex, JpaPath sqmPath, - boolean useInnerJoin, Consumer implicitJoinChecker) { + boolean useInnerJoin, + Consumer implicitJoinChecker) { final JpaPath parentPath = sqmPath.getParentPath(); final TableGroup tableGroup = fromClauseIndex.findTableGroup( parentPath.getNavigablePath() ); if ( tableGroup == null ) { @@ -2354,105 +2307,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base } } - private boolean needsJoinForPath(FromClauseIndex fromClauseIndex, SqmPath sqmPath) { - final JpaPath parentPath = sqmPath.getParentPath(); - final TableGroup parentTableGroup = fromClauseIndex.findTableGroup( parentPath.getNavigablePath() ); - // First, check if we can find the table group for the direct parent - if ( parentTableGroup != null ) { - return needsJoinForPath( fromClauseIndex, parentTableGroup, sqmPath.getReferencedPathSource().getPathName() ); - } - JpaPath realParentPath = parentPath; - int realParentDistance = 0; - // Next, check if we can find the table group for the parent, after traversing up embeddable paths - if ( realParentPath.getModel() instanceof EmbeddableType ) { - do { - realParentPath = realParentPath.getParentPath(); - realParentDistance++; - } while ( realParentPath.getModel() instanceof EmbeddableType ); - final TableGroup assumedToOneTableGroup = fromClauseIndex.findTableGroup( realParentPath.getNavigablePath() ); - if ( assumedToOneTableGroup != null ) { - if ( assumedToOneTableGroup.getModelPart() instanceof ToOneAttributeMapping ) { - realParentPath = parentPath.getParentPath(); - final StringBuilder sb = new StringBuilder(); - sb.append( sqmPath.getReferencedPathSource().getPathName() ); - for ( int i = realParentDistance - 1; i >= 0; i-- ) { - sb.insert( 0, realParentPath.getNavigablePath().getUnaliasedLocalName() + '.' ); - realParentPath = realParentPath.getParentPath(); - } - return needsJoinForPath( fromClauseIndex, assumedToOneTableGroup, sb.toString() ); - } - return false; - } - } - // Finally, check if we can find the grandparent table group - final TableGroup grandparentTableGroup = fromClauseIndex.findTableGroup( realParentPath.getParentPath().getNavigablePath() ); - if ( grandparentTableGroup == null ) { - // without a grandparent, we always need a join - return true; - } - - // With the grandparent available, we can determine if the model part is a to-one mapping - final ModelPart realParentModelPart = grandparentTableGroup.getModelPart().findSubPart( - realParentPath.getNavigablePath().getUnaliasedLocalName(), - null - ); - // This must be a to-one otherwise we assume to always need a join - if ( !( realParentModelPart instanceof ToOneAttributeMapping ) ) { - return true; - } - final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) realParentModelPart; - final String path; - if ( realParentDistance == 0 ) { - path = sqmPath.getReferencedPathSource().getPathName(); - } - else { - realParentPath = parentPath.getParentPath(); - final StringBuilder sb = new StringBuilder(); - sb.append( sqmPath.getReferencedPathSource().getPathName() ); - for ( int i = realParentDistance - 1; i >= 0; i-- ) { - sb.insert( 0, realParentPath.getNavigablePath().getUnaliasedLocalName() + '.' ); - realParentPath = realParentPath.getParentPath(); - } - path = sb.toString(); - } - - return !toOneAttributeMapping.isTargetKeyPropertyPath( path ); - } - - private boolean needsJoinForPath( - FromClauseIndex fromClauseIndex, - TableGroup tableGroup, - String pathName) { - TableGroup parentTableGroup = tableGroup; - ToOneAttributeMapping toOneAttributeMapping; - ModelPartContainer modelPart = tableGroup.getModelPart(); - String path; - if ( modelPart instanceof ToOneAttributeMapping ) { - toOneAttributeMapping = (ToOneAttributeMapping) modelPart; - path = pathName; - } - else { - if ( !( modelPart instanceof EmbeddableValuedModelPart ) ) { - return false; - } - StringBuilder sb = new StringBuilder(); - sb.append( pathName ); - do { - sb.insert( 0, modelPart.getPartName() + '.' ); - parentTableGroup = fromClauseIndex.findTableGroup( parentTableGroup.getNavigablePath().getParent() ); - modelPart = parentTableGroup.getModelPart(); - } while ( modelPart instanceof EmbeddableValuedModelPart ); - if ( !( modelPart instanceof ToOneAttributeMapping ) ) { - return false; - } - toOneAttributeMapping = (ToOneAttributeMapping) modelPart; - path = sb.toString(); - } - final LazyTableGroup lazyTableGroup = (LazyTableGroup) parentTableGroup; - // Unless the lazy table group is not initialized and the requested sub part is not the FK - return !lazyTableGroup.isInitialized() && !toOneAttributeMapping.isTargetKeyPropertyPath( path ); - } - private TableGroup createTableGroup(TableGroup parentTableGroup, SqmPath joinedPath, boolean useInnerJoin) { final SqmPath lhsPath = joinedPath.getLhs(); final FromClauseIndex fromClauseIndex = getFromClauseIndex(); @@ -2500,6 +2354,11 @@ public abstract class BaseSqmToSqlAstConverter extends Base null, defaultSqlAstJoinType, false, + // Implicit joins in the ON clause need to be added as nested table group joins + currentClauseStack.getCurrent() == Clause.FROM + // Except for entity joins, as we can order the implicit joins before the entity join + // See consumeEntityJoin for details + && !( currentlyProcessingJoin instanceof SqmEntityJoin ), this ); tableGroup = tableGroupJoin.getJoinedGroup(); @@ -2963,6 +2822,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base null, SqlAstJoinType.INNER, false, + false, sqlAliasBaseManager, getSqlExpressionResolver(), creationContext @@ -4340,17 +4200,13 @@ public abstract class BaseSqmToSqlAstConverter extends Base @Override public QueryPart visitSubQueryExpression(SqmSubQuery sqmSubQuery) { - return visitQueryPart( sqmSubQuery.getQueryPart() ); -// -// final ExpressableType expressableType = determineExpressableType( sqmSubQuery ); -// -// return new SubQuery( -// subQuerySpec, -// expressableType instanceof BasicValuedExpressableType -// ? ( (BasicValuedExpressableType) expressableType ).getSqlExpressableType( getTypeConfiguration() ) -// : null, -// expressableType -// ); + // The only purpose for tracking the current join is to + // Reset the current join for sub queries because in there, we won't add nested joins + final SqmJoin oldJoin = currentlyProcessingJoin; + currentlyProcessingJoin = null; + final QueryPart queryPart = visitQueryPart( sqmSubQuery.getQueryPart() ); + currentlyProcessingJoin = oldJoin; + return queryPart; } @Override @@ -4807,6 +4663,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base sqmPluralPath.getExplicitAlias(), SqlAstJoinType.INNER, false, + false, sqlAliasBaseManager, subQueryState, creationContext @@ -5215,6 +5072,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base alias, tableGroupJoinProducer.getDefaultSqlAstJoinType( lhs ), true, + false, this ); return tableGroupJoin.getJoinedGroup(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java index cea21b0429..455341db1c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java @@ -311,6 +311,7 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta null, SqlAstJoinType.INNER, false, + false, sqlAstCreationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index a9bf280459..bfdc9d165d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -3403,9 +3403,10 @@ public abstract class AbstractSqlAstTranslator implemen } protected void renderTableGroup(TableGroup tableGroup, Predicate predicate) { - // Without reference joins, even a real table group does not need parenthesis + // Without reference joins or nested join groups, even a real table group does not need parenthesis final boolean realTableGroup = tableGroup.isRealTableGroup() - && CollectionHelper.isNotEmpty( tableGroup.getTableReferenceJoins() ); + && ( CollectionHelper.isNotEmpty( tableGroup.getTableReferenceJoins() ) + || hasTableGroupsToRender( tableGroup.getNestedTableGroupJoins() ) ); if ( realTableGroup ) { appendSql( OPEN_PARENTHESIS ); } @@ -3415,6 +3416,7 @@ public abstract class AbstractSqlAstTranslator implemen if ( realTableGroup ) { renderTableReferenceJoins( tableGroup ); + processNestedTableGroupJoins( tableGroup ); appendSql( CLOSE_PARENTHESIS ); } @@ -3444,6 +3446,17 @@ public abstract class AbstractSqlAstTranslator implemen } } + private boolean hasTableGroupsToRender(List nestedTableGroupJoins) { + for ( TableGroupJoin nestedTableGroupJoin : nestedTableGroupJoins ) { + final TableGroup joinedGroup = nestedTableGroupJoin.getJoinedGroup(); + if ( !( joinedGroup instanceof LazyTableGroup ) || ( (LazyTableGroup) joinedGroup ).isInitialized() ) { + return true; + } + } + + return false; + } + @SuppressWarnings("WeakerAccess") protected boolean renderTableReference(TableReference tableReference, LockMode lockMode) { appendSql( tableReference.getTableExpression() ); @@ -3503,6 +3516,11 @@ public abstract class AbstractSqlAstTranslator implemen source.visitTableGroupJoins( this::processTableGroupJoin ); } + @SuppressWarnings("WeakerAccess") + protected void processNestedTableGroupJoins(TableGroup source) { + source.visitNestedTableGroupJoins( this::processTableGroupJoin ); + } + @SuppressWarnings("WeakerAccess") protected void processTableGroupJoin(TableGroupJoin tableGroupJoin) { final TableGroup joinedGroup = tableGroupJoin.getJoinedGroup(); @@ -3513,6 +3531,12 @@ public abstract class AbstractSqlAstTranslator implemen else if ( !( joinedGroup instanceof LazyTableGroup ) || ( (LazyTableGroup) joinedGroup ).getUnderlyingTableGroup() != null ) { appendSql( WHITESPACE ); SqlAstJoinType joinType = tableGroupJoin.getJoinType(); + // todo (6.0): no exactly sure why this is necessary and IMO this should ideally go away. + // I realized that inverse one-to-one associations with join tables are considered "non-nullable" in the boot model. + // Due to that, we use an inner join for the table group join of that association which the following "works around" + // IMO such an association should be considered nullable. It's also odd that the association + // has the FK Side KEY, although it is clearly TARGET + // See org.hibernate.orm.test.annotations.manytoone.ManyToOneJoinTest.testOneToOneJoinTable2 if ( !joinedGroup.isRealTableGroup() && joinType == SqlAstJoinType.INNER && !joinedGroup.getTableReferenceJoins().isEmpty() ) { joinType = SqlAstJoinType.LEFT; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java index 1c739115a8..300b62813f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java @@ -59,6 +59,11 @@ public class CteTableGroup implements TableGroup { return Collections.emptyList(); } + @Override + public List getNestedTableGroupJoins() { + return Collections.emptyList(); + } + @Override public boolean canUseInnerJoins() { return false; @@ -87,18 +92,22 @@ public class CteTableGroup implements TableGroup { public void visitTableGroupJoins(Consumer consumer) { } + @Override + public void visitNestedTableGroupJoins(Consumer consumer) { + } + @Override public String getGroupAlias() { return null; } @Override - public boolean hasTableGroupJoins() { - return false; + public void addTableGroupJoin(TableGroupJoin join) { + throw new UnsupportedOperationException( ); } @Override - public void addTableGroupJoin(TableGroupJoin join) { + public void addNestedTableGroupJoin(TableGroupJoin join) { throw new UnsupportedOperationException( ); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractTableGroup.java index 4f15ba2d2f..5eee2aa503 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractTableGroup.java @@ -27,6 +27,7 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie private final SqlAliasBase sqlAliasBase; private List tableGroupJoins; + private List nestedTableGroupJoins; private final SessionFactoryImplementor sessionFactory; @@ -87,13 +88,18 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie } @Override - public boolean canUseInnerJoins() { - return canUseInnerJoins; + public List getNestedTableGroupJoins() { + return nestedTableGroupJoins == null ? Collections.emptyList() : Collections.unmodifiableList( nestedTableGroupJoins ); } @Override - public boolean hasTableGroupJoins() { - return tableGroupJoins != null && !tableGroupJoins.isEmpty(); + public boolean isRealTableGroup() { + return nestedTableGroupJoins != null && !nestedTableGroupJoins.isEmpty(); + } + + @Override + public boolean canUseInnerJoins() { + return canUseInnerJoins; } @Override @@ -106,6 +112,16 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie } } + @Override + public void addNestedTableGroupJoin(TableGroupJoin join) { + if ( nestedTableGroupJoins == null ) { + nestedTableGroupJoins = new ArrayList<>(); + } + if ( !nestedTableGroupJoins.contains( join ) ) { + nestedTableGroupJoins.add( join ); + } + } + @Override public void visitTableGroupJoins(Consumer consumer) { if ( tableGroupJoins != null ) { @@ -113,6 +129,13 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie } } + @Override + public void visitNestedTableGroupJoins(Consumer consumer) { + if ( nestedTableGroupJoins != null ) { + nestedTableGroupJoins.forEach( consumer ); + } + } + @Override public String toString() { return getClass().getSimpleName() + '(' + getNavigablePath() + ')'; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CompositeTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CompositeTableGroup.java index 5eed0a2dd8..860abd2796 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CompositeTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CompositeTableGroup.java @@ -25,6 +25,7 @@ public class CompositeTableGroup implements VirtualTableGroup { private final boolean fetched; private List tableGroupJoins; + private List nestedTableGroupJoins; public CompositeTableGroup( NavigablePath navigablePath, @@ -74,13 +75,18 @@ public class CompositeTableGroup implements VirtualTableGroup { } @Override - public boolean canUseInnerJoins() { - return underlyingTableGroup.canUseInnerJoins(); + public List getNestedTableGroupJoins() { + return nestedTableGroupJoins == null ? Collections.emptyList() : Collections.unmodifiableList( nestedTableGroupJoins ); } @Override - public boolean hasTableGroupJoins() { - return tableGroupJoins != null && !tableGroupJoins.isEmpty(); + public boolean isRealTableGroup() { + return nestedTableGroupJoins != null && !nestedTableGroupJoins.isEmpty(); + } + + @Override + public boolean canUseInnerJoins() { + return underlyingTableGroup.canUseInnerJoins(); } @Override @@ -93,6 +99,16 @@ public class CompositeTableGroup implements VirtualTableGroup { } } + @Override + public void addNestedTableGroupJoin(TableGroupJoin join) { + if ( nestedTableGroupJoins == null ) { + nestedTableGroupJoins = new ArrayList<>(); + } + if ( !nestedTableGroupJoins.contains( join ) ) { + nestedTableGroupJoins.add( join ); + } + } + @Override public void visitTableGroupJoins(Consumer consumer) { if ( tableGroupJoins != null ) { @@ -100,6 +116,13 @@ public class CompositeTableGroup implements VirtualTableGroup { } } + @Override + public void visitNestedTableGroupJoins(Consumer consumer) { + if ( nestedTableGroupJoins != null ) { + nestedTableGroupJoins.forEach( consumer ); + } + } + @Override public void applyAffectedTableNames(Consumer nameCollector) { underlyingTableGroup.applyAffectedTableNames( nameCollector ); @@ -136,6 +159,14 @@ public class CompositeTableGroup implements VirtualTableGroup { if ( tableReference != null ) { return tableReference; } + for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { + final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup() + .getPrimaryTableReference() + .getTableReference( navigablePath, tableExpression, allowFkOptimization ); + if ( primaryTableReference != null ) { + return primaryTableReference; + } + } for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup() .getPrimaryTableReference() diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedTableGroup.java index b5ed81f7b9..cd295ce4ab 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedTableGroup.java @@ -71,6 +71,14 @@ public class CorrelatedTableGroup extends AbstractTableGroup { if ( primaryTableReference != null ) { return primaryTableReference; } + for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { + final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() + .getPrimaryTableReference() + .getTableReference( navigablePath, tableExpression, allowFkOptimization ); + if ( groupTableReference != null ) { + return groupTableReference; + } + } for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() .getPrimaryTableReference() diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java index 82b8befe25..d300b767bb 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java @@ -106,8 +106,8 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements } @Override - public boolean hasTableGroupJoins() { - return tableGroup != null && tableGroup.hasTableGroupJoins(); + public List getNestedTableGroupJoins() { + return tableGroup == null ? Collections.emptyList() : tableGroup.getNestedTableGroupJoins(); } @Override @@ -115,6 +115,11 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements getTableGroup().addTableGroupJoin( join ); } + @Override + public void addNestedTableGroupJoin(TableGroupJoin join) { + getTableGroup().addNestedTableGroupJoin( join ); + } + @Override public void visitTableGroupJoins(Consumer consumer) { if ( tableGroup != null ) { @@ -122,6 +127,13 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements } } + @Override + public void visitNestedTableGroupJoins(Consumer consumer) { + if ( tableGroup != null ) { + tableGroup.visitNestedTableGroupJoins( consumer ); + } + } + @Override public boolean canUseInnerJoins() { return canUseInnerJoins; @@ -159,7 +171,7 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements @Override public boolean isRealTableGroup() { - return false; + return tableGroup != null && tableGroup.isRealTableGroup(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MutatingTableReferenceGroupWrapper.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MutatingTableReferenceGroupWrapper.java index 3142da0490..60ead12a3f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MutatingTableReferenceGroupWrapper.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MutatingTableReferenceGroupWrapper.java @@ -93,12 +93,12 @@ public class MutatingTableReferenceGroupWrapper implements VirtualTableGroup { } @Override - public boolean canUseInnerJoins() { - return false; + public List getNestedTableGroupJoins() { + return Collections.emptyList(); } @Override - public boolean hasTableGroupJoins() { + public boolean canUseInnerJoins() { return false; } @@ -107,10 +107,19 @@ public class MutatingTableReferenceGroupWrapper implements VirtualTableGroup { throw new UnsupportedOperationException(); } + @Override + public void addNestedTableGroupJoin(TableGroupJoin join) { + throw new UnsupportedOperationException(); + } + @Override public void visitTableGroupJoins(Consumer consumer) { } + @Override + public void visitNestedTableGroupJoins(Consumer consumer) { + } + @Override public List getTableReferenceJoins() { return Collections.emptyList(); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardTableGroup.java index 3dd5351a0a..fb1a7c4c0d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardTableGroup.java @@ -115,7 +115,7 @@ public class StandardTableGroup extends AbstractTableGroup { @Override public boolean isRealTableGroup() { - return realTableGroup; + return realTableGroup || super.isRealTableGroup(); } @Override @@ -159,6 +159,12 @@ public class StandardTableGroup extends AbstractTableGroup { return potentiallyCreateTableReference( tableExpression ); } + for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { + final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup().getPrimaryTableReference(); + if ( primaryTableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization ) != null ) { + return primaryTableReference; + } + } for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup().getPrimaryTableReference(); if ( primaryTableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization ) != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroup.java index b77b2adde2..4c12e1aef0 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroup.java @@ -41,15 +41,19 @@ public interface TableGroup extends SqlAstNode, ColumnReferenceQualifier, SqmPat String getSourceAlias(); List getTableGroupJoins(); + + List getNestedTableGroupJoins(); boolean canUseInnerJoins(); - boolean hasTableGroupJoins(); - void addTableGroupJoin(TableGroupJoin join); + void addNestedTableGroupJoin(TableGroupJoin join); + void visitTableGroupJoins(Consumer consumer); + void visitNestedTableGroupJoins(Consumer consumer); + void applyAffectedTableNames(Consumer nameCollector); TableReference getPrimaryTableReference(); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupJoinProducer.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupJoinProducer.java index 0d94ebe49a..b78d498c15 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupJoinProducer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupJoinProducer.java @@ -8,7 +8,6 @@ package org.hibernate.sql.ast.tree.from; import java.util.function.Consumer; -import org.hibernate.LockMode; import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator; @@ -33,6 +32,7 @@ public interface TableGroupJoinProducer extends TableGroupProducer { String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, + boolean nested, SqlAstCreationState creationState) { return createTableGroupJoin( navigablePath, @@ -40,6 +40,7 @@ public interface TableGroupJoinProducer extends TableGroupProducer { explicitSourceAlias, sqlAstJoinType, fetched, + nested, creationState.getSqlAliasBaseGenerator(), creationState.getSqlExpressionResolver(), creationState.getCreationContext() @@ -55,6 +56,7 @@ public interface TableGroupJoinProducer extends TableGroupProducer { String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, + boolean nested, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableGroup.java index e36504ecdb..7c8cf9c529 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableGroup.java @@ -23,6 +23,7 @@ public class UnionTableGroup implements VirtualTableGroup { private final boolean canUseInnerJoins; private final NavigablePath navigablePath; private List tableGroupJoins; + private List nestedTableGroupJoins; private final UnionSubclassEntityPersister modelPart; private final String sourceAlias; @@ -72,8 +73,13 @@ public class UnionTableGroup implements VirtualTableGroup { } @Override - public boolean hasTableGroupJoins() { - return tableGroupJoins != null && !tableGroupJoins.isEmpty(); + public List getNestedTableGroupJoins() { + return nestedTableGroupJoins == null ? Collections.emptyList() : Collections.unmodifiableList( nestedTableGroupJoins ); + } + + @Override + public boolean isRealTableGroup() { + return nestedTableGroupJoins != null && !nestedTableGroupJoins.isEmpty(); } @Override @@ -91,6 +97,16 @@ public class UnionTableGroup implements VirtualTableGroup { } } + @Override + public void addNestedTableGroupJoin(TableGroupJoin join) { + if ( nestedTableGroupJoins == null ) { + nestedTableGroupJoins = new ArrayList<>(); + } + if ( !nestedTableGroupJoins.contains( join ) ) { + nestedTableGroupJoins.add( join ); + } + } + @Override public void visitTableGroupJoins(Consumer consumer) { if ( tableGroupJoins != null ) { @@ -98,6 +114,13 @@ public class UnionTableGroup implements VirtualTableGroup { } } + @Override + public void visitNestedTableGroupJoins(Consumer consumer) { + if ( nestedTableGroupJoins != null ) { + nestedTableGroupJoins.forEach( consumer ); + } + } + @Override public void applyAffectedTableNames(Consumer nameCollector) { } @@ -128,6 +151,15 @@ public class UnionTableGroup implements VirtualTableGroup { if ( tableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization ) != null ) { return tableReference; } + if ( nestedTableGroupJoins != null ) { + for ( TableGroupJoin tableGroupJoin : nestedTableGroupJoins ) { + final TableReference tableReference = tableGroupJoin.getJoinedGroup() + .resolveTableReference( navigablePath, tableExpression, allowFkOptimization ); + if ( tableReference != null ) { + return tableReference; + } + } + } if ( tableGroupJoins != null ) { for ( TableGroupJoin tableGroupJoin : tableGroupJoins ) { final TableReference tableReference = tableGroupJoin.getJoinedGroup() diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EntityCollectionPartTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EntityCollectionPartTableGroup.java index 2860e8dcba..4b88781cbe 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EntityCollectionPartTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EntityCollectionPartTableGroup.java @@ -66,13 +66,13 @@ public class EntityCollectionPartTableGroup implements TableGroup { } @Override - public boolean canUseInnerJoins() { - return collectionTableGroup.canUseInnerJoins(); + public List getNestedTableGroupJoins() { + return collectionTableGroup.getNestedTableGroupJoins(); } @Override - public boolean hasTableGroupJoins() { - return collectionTableGroup.hasTableGroupJoins(); + public boolean canUseInnerJoins() { + return collectionTableGroup.canUseInnerJoins(); } @Override @@ -80,11 +80,21 @@ public class EntityCollectionPartTableGroup implements TableGroup { collectionTableGroup.addTableGroupJoin( join ); } + @Override + public void addNestedTableGroupJoin(TableGroupJoin join) { + collectionTableGroup.addNestedTableGroupJoin( join ); + } + @Override public void visitTableGroupJoins(Consumer consumer) { collectionTableGroup.visitTableGroupJoins( consumer ); } + @Override + public void visitNestedTableGroupJoins(Consumer consumer) { + collectionTableGroup.visitNestedTableGroupJoins( consumer ); + } + @Override public void applyAffectedTableNames(Consumer nameCollector) { collectionTableGroup.applyAffectedTableNames( nameCollector ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java index d917a95188..47ee4a181a 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java @@ -68,6 +68,7 @@ public class EmbeddableFetchImpl extends AbstractFetchParent implements Embeddab null, nullable ? SqlAstJoinType.LEFT : SqlAstJoinType.INNER, true, + false, creationState.getSqlAstCreationState() ); return tableGroupJoin.getJoinedGroup(); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableResultImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableResultImpl.java index 9e8a8bb6b4..431c007a63 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableResultImpl.java @@ -54,6 +54,7 @@ public class EmbeddableResultImpl extends AbstractFetchParent implements Embe resultVariable, SqlAstJoinType.INNER, true, + false, creationState.getSqlAstCreationState() ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/WithClauseTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/WithClauseTest.java index a9c17e6505..c40d0bbdc4 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/WithClauseTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/WithClauseTest.java @@ -13,7 +13,6 @@ import java.util.List; import org.hibernate.HibernateException; import org.hibernate.QueryException; import org.hibernate.dialect.DerbyDialect; -import org.hibernate.dialect.TiDBDialect; import org.hibernate.query.Query; import org.hibernate.testing.TestForIssue; @@ -115,7 +114,6 @@ public class WithClauseTest { } @Test - @SkipForDialect(dialectClass = TiDBDialect.class, reason = "TiDB db does not support subqueries for ON condition") public void testWithClauseWithImplicitJoin(SessionFactoryScope scope) { scope.inTransaction( (session) -> { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java index 37d3809a3f..cab8e7acd9 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/ast/SmokeTests.java @@ -100,7 +100,7 @@ public class SmokeTests { assertThat( rootTableGroup.getTableReferenceJoins().size(), is( 0 ) ); - assertThat( rootTableGroup.hasTableGroupJoins(), is( false ) ); + assertThat( rootTableGroup.getTableGroupJoins().isEmpty(), is( true ) ); // `s` is the "alias stem" for `SimpleEntity` and as it is the first entity with that stem in @@ -159,7 +159,7 @@ public class SmokeTests { assertThat( rootTableGroup.getTableReferenceJoins().size(), is( 0 ) ); - assertThat( rootTableGroup.hasTableGroupJoins(), is( false ) ); + assertThat( rootTableGroup.getTableGroupJoins().isEmpty(), is( true ) ); // `s` is the "alias stem" for `SimpleEntity` and as it is the first entity with that stem in diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/function/OrderByFragmentFunction.java b/hibernate-envers/src/main/java/org/hibernate/envers/function/OrderByFragmentFunction.java index 75b7d5e4b8..34acfeff9e 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/function/OrderByFragmentFunction.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/function/OrderByFragmentFunction.java @@ -174,13 +174,13 @@ public class OrderByFragmentFunction extends AbstractSqmFunctionDescriptor { } @Override - public boolean canUseInnerJoins() { - return delegate.canUseInnerJoins(); + public List getNestedTableGroupJoins() { + return delegate.getNestedTableGroupJoins(); } @Override - public boolean hasTableGroupJoins() { - return delegate.hasTableGroupJoins(); + public boolean canUseInnerJoins() { + return delegate.canUseInnerJoins(); } @Override @@ -188,11 +188,21 @@ public class OrderByFragmentFunction extends AbstractSqmFunctionDescriptor { delegate.addTableGroupJoin( join ); } + @Override + public void addNestedTableGroupJoin(TableGroupJoin join) { + delegate.addNestedTableGroupJoin( join ); + } + @Override public void visitTableGroupJoins(Consumer consumer) { delegate.visitTableGroupJoins( consumer ); } + @Override + public void visitNestedTableGroupJoins(Consumer consumer) { + delegate.visitNestedTableGroupJoins( consumer ); + } + @Override public void applyAffectedTableNames(Consumer nameCollector) { delegate.applyAffectedTableNames( nameCollector );