Add support for from-space wide explicit join ordering and thus support entity/cross joins on all JpaFrom elements

This commit is contained in:
Christian Beikov 2021-10-23 18:16:17 +02:00
parent 1456a2dd7f
commit 6745f71f88
27 changed files with 255 additions and 209 deletions

View File

@ -421,9 +421,9 @@ public class LoaderSelectBuilder {
null, null,
SqlAstJoinType.LEFT, SqlAstJoinType.LEFT,
true, true,
false,
sqlAstCreationState sqlAstCreationState
); );
rootTableGroup.addTableGroupJoin( tableGroupJoin );
tableGroup = tableGroupJoin.getJoinedGroup(); tableGroup = tableGroupJoin.getJoinedGroup();
sqlAstCreationState.getFromClauseAccess().registerTableGroup( navigablePath, tableGroup ); sqlAstCreationState.getFromClauseAccess().registerTableGroup( navigablePath, tableGroup );
} }

View File

@ -171,7 +171,6 @@ public abstract class AbstractCompositeIdentifierMapping
String explicitSourceAlias, String explicitSourceAlias,
SqlAstJoinType sqlAstJoinType, SqlAstJoinType sqlAstJoinType,
boolean fetched, boolean fetched,
boolean nested,
SqlAliasBaseGenerator aliasBaseGenerator, SqlAliasBaseGenerator aliasBaseGenerator,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
@ -187,15 +186,7 @@ public abstract class AbstractCompositeIdentifierMapping
creationContext creationContext
); );
final TableGroupJoin join = new TableGroupJoin( navigablePath, SqlAstJoinType.LEFT, tableGroup, null ); return new TableGroupJoin( navigablePath, SqlAstJoinType.LEFT, tableGroup, null );
if ( nested ) {
lhs.addNestedTableGroupJoin( join );
}
else {
lhs.addTableGroupJoin( join );
}
return join;
} }
@Override @Override

View File

@ -284,7 +284,6 @@ public class EmbeddedAttributeMapping
String explicitSourceAlias, String explicitSourceAlias,
SqlAstJoinType sqlAstJoinType, SqlAstJoinType sqlAstJoinType,
boolean fetched, boolean fetched,
boolean nested,
SqlAliasBaseGenerator aliasBaseGenerator, SqlAliasBaseGenerator aliasBaseGenerator,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
@ -300,19 +299,7 @@ public class EmbeddedAttributeMapping
creationContext creationContext
); );
final TableGroupJoin join = new TableGroupJoin( return new TableGroupJoin( navigablePath, sqlAstJoinType, tableGroup );
navigablePath,
sqlAstJoinType,
tableGroup
);
if ( nested ) {
lhs.addNestedTableGroupJoin( join );
}
else {
lhs.addTableGroupJoin( join );
}
return join;
} }
@Override @Override

View File

@ -202,7 +202,6 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF
String explicitSourceAlias, String explicitSourceAlias,
SqlAstJoinType sqlAstJoinType, SqlAstJoinType sqlAstJoinType,
boolean fetched, boolean fetched,
boolean nested,
SqlAliasBaseGenerator aliasBaseGenerator, SqlAliasBaseGenerator aliasBaseGenerator,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
@ -218,19 +217,7 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF
creationContext creationContext
); );
final TableGroupJoin join = new TableGroupJoin( return new TableGroupJoin( navigablePath, sqlAstJoinType, tableGroup, null );
navigablePath,
sqlAstJoinType,
tableGroup,
null
);
if ( nested ) {
lhs.addNestedTableGroupJoin( join );
}
else {
lhs.addTableGroupJoin( join );
}
return join;
} }
@Override @Override

View File

@ -305,9 +305,9 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
null, null,
SqlAstJoinType.INNER, SqlAstJoinType.INNER,
true, true,
false,
creationState.getSqlAstCreationState() creationState.getSqlAstCreationState()
); );
tableGroup.addTableGroupJoin( tableGroupJoin );
return tableGroupJoin.getJoinedGroup(); return tableGroupJoin.getJoinedGroup();
} }
); );

View File

@ -273,7 +273,6 @@ public class EntityCollectionPart
String explicitSourceAlias, String explicitSourceAlias,
SqlAstJoinType sqlAstJoinType, SqlAstJoinType sqlAstJoinType,
boolean fetched, boolean fetched,
boolean nested,
SqlAliasBaseGenerator aliasBaseGenerator, SqlAliasBaseGenerator aliasBaseGenerator,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
@ -283,7 +282,6 @@ public class EntityCollectionPart
explicitSourceAlias, explicitSourceAlias,
sqlAstJoinType, sqlAstJoinType,
fetched, fetched,
nested,
aliasBaseGenerator, aliasBaseGenerator,
sqlExpressionResolver, sqlExpressionResolver,
creationContext creationContext

View File

@ -572,9 +572,9 @@ public class PluralAttributeMappingImpl
null, null,
SqlAstJoinType.LEFT, SqlAstJoinType.LEFT,
true, true,
false,
creationState.getSqlAstCreationState() creationState.getSqlAstCreationState()
); );
lhsTableGroup.addTableGroupJoin( tableGroupJoin );
return tableGroupJoin.getJoinedGroup(); return tableGroupJoin.getJoinedGroup();
} }
); );
@ -625,7 +625,6 @@ public class PluralAttributeMappingImpl
String explicitSourceAlias, String explicitSourceAlias,
SqlAstJoinType sqlAstJoinType, SqlAstJoinType sqlAstJoinType,
boolean fetched, boolean fetched,
boolean nested,
SqlAliasBaseGenerator aliasBaseGenerator, SqlAliasBaseGenerator aliasBaseGenerator,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
@ -640,7 +639,7 @@ public class PluralAttributeMappingImpl
sqlExpressionResolver, sqlExpressionResolver,
creationContext creationContext
); );
final TableGroupJoin join = new TableGroupJoin( return new TableGroupJoin(
navigablePath, navigablePath,
sqlAstJoinType, sqlAstJoinType,
tableGroup, tableGroup,
@ -652,15 +651,6 @@ public class PluralAttributeMappingImpl
creationContext creationContext
) )
); );
if ( nested ) {
lhs.addNestedTableGroupJoin( join );
}
else {
lhs.addTableGroupJoin( join );
}
return join;
} }
@Override @Override

View File

@ -716,27 +716,33 @@ public class ToOneAttributeMapping
final TableGroup tableGroup; final TableGroup tableGroup;
if ( fetchParent instanceof EntityResultJoinedSubclassImpl && if ( fetchParent instanceof EntityResultJoinedSubclassImpl &&
( (EntityPersister) fetchParent.getReferencedModePart() ).findDeclaredAttributeMapping( getPartName() ) == null ) { ( (EntityPersister) fetchParent.getReferencedModePart() ).findDeclaredAttributeMapping( getPartName() ) == null ) {
tableGroup = createTableGroupJoin( final TableGroupJoin tableGroupJoin = createTableGroupJoin(
fetchablePath, fetchablePath,
true, parentTableGroup,
getJoinType( fetchablePath, parentTableGroup ),
resultVariable, resultVariable,
creationState, getJoinType( fetchablePath, parentTableGroup ),
parentTableGroup true,
creationState.getSqlAstCreationState()
); );
parentTableGroup.addTableGroupJoin( tableGroupJoin );
tableGroup = tableGroupJoin.getJoinedGroup();
fromClauseAccess.registerTableGroup( fetchablePath, tableGroup ); fromClauseAccess.registerTableGroup( fetchablePath, tableGroup );
} }
else { else {
tableGroup = fromClauseAccess.resolveTableGroup( tableGroup = fromClauseAccess.resolveTableGroup(
fetchablePath, fetchablePath,
np -> np -> {
createTableGroupJoin( final TableGroupJoin tableGroupJoin = createTableGroupJoin(
fetchablePath, fetchablePath,
true, parentTableGroup,
resultVariable, resultVariable,
creationState, getDefaultSqlAstJoinType( parentTableGroup ),
parentTableGroup true,
) creationState.getSqlAstCreationState()
);
parentTableGroup.addTableGroupJoin( tableGroupJoin );
return tableGroupJoin.getJoinedGroup();
}
); );
} }
@ -839,9 +845,9 @@ public class ToOneAttributeMapping
null, null,
getDefaultSqlAstJoinType( tableGroup ), getDefaultSqlAstJoinType( tableGroup ),
true, true,
false,
creationState.getSqlAstCreationState() creationState.getSqlAstCreationState()
); );
tableGroup.addTableGroupJoin( tableGroupJoin );
creationState.getSqlAstCreationState().getFromClauseAccess().registerTableGroup( creationState.getSqlAstCreationState().getFromClauseAccess().registerTableGroup(
navigablePath, navigablePath,
@ -867,22 +873,6 @@ public class ToOneAttributeMapping
} }
} }
private TableGroup createTableGroupJoin(
NavigablePath fetchablePath,
boolean fetched,
String sourceAlias,
DomainResultCreationState creationState,
TableGroup parentTableGroup) {
return createTableGroupJoin(
fetchablePath,
fetched,
getDefaultSqlAstJoinType( parentTableGroup ),
sourceAlias,
creationState,
parentTableGroup
);
}
@Override @Override
public SqlAstJoinType getDefaultSqlAstJoinType(TableGroup parentTableGroup) { public SqlAstJoinType getDefaultSqlAstJoinType(TableGroup parentTableGroup) {
if ( isNullable ) { if ( isNullable ) {
@ -912,7 +902,6 @@ public class ToOneAttributeMapping
sourceAlias, sourceAlias,
sqlAstJoinType, sqlAstJoinType,
fetched, fetched,
false,
creationState.getSqlAstCreationState() creationState.getSqlAstCreationState()
); );
@ -931,7 +920,6 @@ public class ToOneAttributeMapping
String explicitSourceAlias, String explicitSourceAlias,
SqlAstJoinType sqlAstJoinType, SqlAstJoinType sqlAstJoinType,
boolean fetched, boolean fetched,
boolean nested,
SqlAliasBaseGenerator aliasBaseGenerator, SqlAliasBaseGenerator aliasBaseGenerator,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
@ -967,12 +955,6 @@ public class ToOneAttributeMapping
) )
) )
); );
if ( nested ) {
lhs.addNestedTableGroupJoin( join );
}
else {
lhs.addTableGroupJoin( join );
}
if ( sqlAstJoinType == SqlAstJoinType.INNER && isNullable ) { if ( sqlAstJoinType == SqlAstJoinType.INNER && isNullable ) {
// Force initialization of the underlying table group join to retain cardinality // Force initialization of the underlying table group join to retain cardinality

View File

@ -6,6 +6,9 @@
*/ */
package org.hibernate.query.criteria; package org.hibernate.query.criteria;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.sqm.tree.SqmJoinType;
import jakarta.persistence.criteria.From; import jakarta.persistence.criteria.From;
/** /**
@ -16,4 +19,12 @@ import jakarta.persistence.criteria.From;
public interface JpaFrom<O,T> extends JpaPath<T>, JpaFetchParent<O,T>, From<O,T> { public interface JpaFrom<O,T> extends JpaPath<T>, JpaFetchParent<O,T>, From<O,T> {
@Override @Override
JpaFrom<O,T> getCorrelationParent(); JpaFrom<O,T> getCorrelationParent();
<X> JpaEntityJoin<X> join(Class<X> entityJavaType);
<X> JpaEntityJoin<X> join(EntityDomainType<X> entity);
<X> JpaEntityJoin<X> join(Class<X> entityJavaType, SqmJoinType joinType);
<X> JpaEntityJoin<X> join(EntityDomainType<X> entity, SqmJoinType joinType);
} }

View File

@ -9,7 +9,6 @@ package org.hibernate.query.criteria;
import jakarta.persistence.criteria.Root; import jakarta.persistence.criteria.Root;
import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.sqm.tree.SqmJoinType;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
@ -19,12 +18,4 @@ public interface JpaRoot<T> extends JpaFrom<T,T>, Root<T> {
EntityDomainType<T> getModel(); EntityDomainType<T> getModel();
EntityDomainType<T> getManagedType(); EntityDomainType<T> getManagedType();
<X> JpaEntityJoin<X> join(Class<X> entityJavaType);
<X> JpaEntityJoin<X> join(EntityDomainType<X> entity);
<X> JpaEntityJoin<X> join(Class<X> entityJavaType, SqmJoinType joinType);
<X> JpaEntityJoin<X> join(EntityDomainType<X> entity, SqmJoinType joinType);
} }

View File

@ -1628,7 +1628,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
} }
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
protected void consumeQualifiedJoin(HqlParser.QualifiedJoinContext parserJoin, SqmRoot<?> sqmRoot) { protected <X> void consumeQualifiedJoin(HqlParser.QualifiedJoinContext parserJoin, SqmRoot<X> sqmRoot) {
final SqmJoinType joinType; final SqmJoinType joinType;
final int firstJoinTypeSymbolType; final int firstJoinTypeSymbolType;
if ( parserJoin.getChild( 0 ) instanceof HqlParser.JoinTypeQualifierContext if ( parserJoin.getChild( 0 ) instanceof HqlParser.JoinTypeQualifierContext
@ -1676,22 +1676,22 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
); );
try { try {
//noinspection rawtypes //noinspection unchecked
final SqmQualifiedJoin join = (SqmQualifiedJoin) qualifiedJoinRhsContext.getChild( 0 ).accept( this ); final SqmQualifiedJoin<X, ?> join = (SqmQualifiedJoin<X, ?>) qualifiedJoinRhsContext.getChild( 0 ).accept( this );
// we need to set the alias here because the path could be treated - the treat operator is // we need to set the alias here because the path could be treated - the treat operator is
// not consumed by the identifierConsumer // not consumed by the identifierConsumer
join.setExplicitAlias( alias ); join.setExplicitAlias( alias );
final HqlParser.QualifiedJoinPredicateContext qualifiedJoinPredicateContext = parserJoin.qualifiedJoinPredicate(); final HqlParser.QualifiedJoinPredicateContext qualifiedJoinPredicateContext = parserJoin.qualifiedJoinPredicate();
if ( join instanceof SqmEntityJoin ) { if ( join instanceof SqmEntityJoin<?> ) {
//noinspection unchecked
sqmRoot.addSqmJoin( join ); sqmRoot.addSqmJoin( join );
} }
else { else {
final SqmAttributeJoin<?, ?> attributeJoin = (SqmAttributeJoin<?, ?>) join;
if ( getCreationOptions().useStrictJpaCompliance() ) { if ( getCreationOptions().useStrictJpaCompliance() ) {
if ( join.getExplicitAlias() != null ) { if ( join.getExplicitAlias() != null ) {
if ( ( (SqmAttributeJoin<?, ?>) join ).isFetched() ) { if ( attributeJoin.isFetched() ) {
throw new StrictJpaComplianceViolation( throw new StrictJpaComplianceViolation(
"Encountered aliased fetch join, but strict JPQL compliance was requested", "Encountered aliased fetch join, but strict JPQL compliance was requested",
StrictJpaComplianceViolation.Type.ALIASED_FETCH_JOIN StrictJpaComplianceViolation.Type.ALIASED_FETCH_JOIN
@ -1699,7 +1699,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
} }
} }
} }
if ( qualifiedJoinPredicateContext != null && ( (SqmAttributeJoin<?, ?>) join ).isFetched() ) { if ( qualifiedJoinPredicateContext != null && attributeJoin.isFetched() ) {
throw new SemanticException( "with-clause not allowed on fetched associations; use filters" ); throw new SemanticException( "with-clause not allowed on fetched associations; use filters" );
} }
} }

View File

@ -91,10 +91,9 @@ public class TableGroupImpl implements TableGroup {
if ( tableGroupJoins == null ) { if ( tableGroupJoins == null ) {
tableGroupJoins = new ArrayList<>(); tableGroupJoins = new ArrayList<>();
} }
if ( !tableGroupJoins.contains( join ) ) { assert !tableGroupJoins.contains( join );
tableGroupJoins.add( join ); tableGroupJoins.add( join );
} }
}
@Override @Override
public void addNestedTableGroupJoin(TableGroupJoin join) { public void addNestedTableGroupJoin(TableGroupJoin join) {

View File

@ -118,11 +118,11 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue
tableAlias, tableAlias,
SqlAstJoinType.INNER, SqlAstJoinType.INNER,
true, true,
false,
s -> sqlAliasBase, s -> sqlAliasBase,
creationState.getSqlExpressionResolver(), creationState.getSqlExpressionResolver(),
creationState.getCreationContext() creationState.getCreationContext()
); );
ownerTableGroup.addTableGroupJoin( tableGroupJoin );
creationState.getFromClauseAccess().registerTableGroup( fetchPath, tableGroup = tableGroupJoin.getJoinedGroup() ); creationState.getFromClauseAccess().registerTableGroup( fetchPath, tableGroup = tableGroupJoin.getJoinedGroup() );
} }
else { else {

View File

@ -89,9 +89,9 @@ public class ImplicitFetchBuilderEmbeddable implements ImplicitFetchBuilder {
null, null,
SqlAstJoinType.INNER, SqlAstJoinType.INNER,
true, true,
false,
creationStateImpl creationStateImpl
); );
parentTableGroup.addTableGroupJoin( tableGroupJoin );
return tableGroupJoin.getJoinedGroup(); return tableGroupJoin.getJoinedGroup();
} }
); );

View File

@ -70,7 +70,6 @@ public class ImplicitModelPartResultBuilderEmbeddable
null, null,
SqlAstJoinType.INNER, SqlAstJoinType.INNER,
true, true,
false,
creationStateImpl creationStateImpl
); );

View File

@ -82,6 +82,9 @@ import org.hibernate.metamodel.model.domain.internal.CompositeSqmPathSource;
import org.hibernate.query.criteria.JpaPath; import org.hibernate.query.criteria.JpaPath;
import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression; import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression;
import org.hibernate.query.sqm.produce.function.internal.PatternRenderer; import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.domain.SqmCorrelatedRootJoin;
import org.hibernate.query.sqm.tree.domain.SqmCorrelation;
import org.hibernate.sql.exec.internal.VersionTypeSeedParameterSpecification; import org.hibernate.sql.exec.internal.VersionTypeSeedParameterSpecification;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
@ -1926,12 +1929,24 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final TableGroup tableGroup; final TableGroup tableGroup;
if ( sqmRoot.isCorrelated() ) { if ( sqmRoot.isCorrelated() ) {
final SessionFactoryImplementor sessionFactory = creationContext.getSessionFactory(); final SessionFactoryImplementor sessionFactory = creationContext.getSessionFactory();
final TableGroup parentTableGroup = fromClauseIndex.findTableGroup(
sqmRoot.getCorrelationParent().getNavigablePath()
);
final EntityPersister entityDescriptor = resolveEntityPersister( sqmRoot.getReferencedPathSource() ); final EntityPersister entityDescriptor = resolveEntityPersister( sqmRoot.getReferencedPathSource() );
if ( sqmRoot.containsOnlyInnerJoins() ) { if ( sqmRoot.containsOnlyInnerJoins() ) {
// If we have just inner joins against a correlated root, we can render the joins as references // If we have just inner joins against a correlated root, we can render the joins as references
final SqmFrom<?, ?> from;
// If we correlate a join, we have to create a special SqmRoot shell called SqmCorrelatedRootJoin.
// The only purpose of that is to serve as SqmRoot, which is needed for the FROM clause.
// It will always contain just a single correlated join though, which is what is actually correlated
if ( sqmRoot instanceof SqmCorrelatedRootJoin<?> ) {
assert sqmRoot.getSqmJoins().size() == 1;
assert sqmRoot.getSqmJoins().get( 0 ).isCorrelated();
from = sqmRoot.getSqmJoins().get( 0 );
}
else {
from = sqmRoot;
}
final TableGroup parentTableGroup = fromClauseIndex.findTableGroup(
from.getCorrelationParent().getNavigablePath()
);
final SqlAliasBase sqlAliasBase = sqlAliasBaseManager.createSqlAliasBase( parentTableGroup.getGroupAlias() ); final SqlAliasBase sqlAliasBase = sqlAliasBaseManager.createSqlAliasBase( parentTableGroup.getGroupAlias() );
tableGroup = new CorrelatedTableGroup( tableGroup = new CorrelatedTableGroup(
parentTableGroup, parentTableGroup,
@ -1945,11 +1960,13 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
); );
log.tracef( "Resolved SqmRoot [%s] to correlated TableGroup [%s]", sqmRoot, tableGroup ); log.tracef( "Resolved SqmRoot [%s] to correlated TableGroup [%s]", sqmRoot, tableGroup );
consumeExplicitJoins( from, tableGroup );
consumeExplicitJoins( sqmRoot, tableGroup );
return; return;
} }
else { else {
final TableGroup parentTableGroup = fromClauseIndex.findTableGroup(
sqmRoot.getCorrelationParent().getNavigablePath()
);
// If we have non-inner joins against a correlated root, we must render the root with a correlation predicate // If we have non-inner joins against a correlated root, we must render the root with a correlation predicate
tableGroup = entityDescriptor.createRootTableGroup( tableGroup = entityDescriptor.createRootTableGroup(
true, true,
@ -2042,8 +2059,34 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
fromClauseIndex.register( sqmRoot, tableGroup ); fromClauseIndex.register( sqmRoot, tableGroup );
currentQuerySpec.getFromClause().addRoot( tableGroup ); currentQuerySpec.getFromClause().addRoot( tableGroup );
if ( sqmRoot.getOrderedJoins() == null ) {
consumeExplicitJoins( sqmRoot, tableGroup ); consumeExplicitJoins( sqmRoot, tableGroup );
} }
else {
if ( log.isTraceEnabled() ) {
log.tracef( "Visiting explicit joins for `%s`", sqmRoot.getNavigablePath() );
}
TableGroup lastTableGroup = tableGroup;
for ( SqmJoin<?, ?> join : sqmRoot.getOrderedJoins() ) {
final TableGroup ownerTableGroup;
if ( join.getLhs() == null ) {
ownerTableGroup = tableGroup;
}
else {
if ( join.getLhs() instanceof SqmCorrelation<?, ?> ) {
ownerTableGroup = fromClauseIndex.findTableGroup(
( (SqmCorrelation<?, ?>) join.getLhs() ).getCorrelatedRoot().getNavigablePath()
);
}
else {
ownerTableGroup = fromClauseIndex.findTableGroup( join.getLhs().getNavigablePath() );
}
}
assert ownerTableGroup != null;
lastTableGroup = consumeExplicitJoin( join, lastTableGroup, ownerTableGroup, false );
}
}
}
private EntityPersister resolveEntityPersister(EntityDomainType<?> entityDomainType) { private EntityPersister resolveEntityPersister(EntityDomainType<?> entityDomainType) {
return creationContext.getDomainModel().getEntityDescriptor( entityDomainType.getHibernateEntityName() ); return creationContext.getDomainModel().getEntityDescriptor( entityDomainType.getHibernateEntityName() );
@ -2054,29 +2097,38 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
log.tracef( "Visiting explicit joins for `%s`", sqmFrom.getNavigablePath() ); log.tracef( "Visiting explicit joins for `%s`", sqmFrom.getNavigablePath() );
} }
sqmFrom.visitSqmJoins( sqmFrom.visitSqmJoins(
sqmJoin -> consumeExplicitJoin( sqmJoin, lhsTableGroup ) sqmJoin -> consumeExplicitJoin( sqmJoin, lhsTableGroup, lhsTableGroup, true )
); );
} }
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
protected void consumeExplicitJoin(SqmJoin<?, ?> sqmJoin, TableGroup lhsTableGroup) { protected TableGroup consumeExplicitJoin(
SqmJoin<?, ?> sqmJoin,
TableGroup lhsTableGroup,
TableGroup ownerTableGroup,
boolean transitive) {
if ( sqmJoin instanceof SqmAttributeJoin<?, ?> ) { if ( sqmJoin instanceof SqmAttributeJoin<?, ?> ) {
consumeAttributeJoin( ( (SqmAttributeJoin<?, ?>) sqmJoin ), lhsTableGroup ); return consumeAttributeJoin( ( (SqmAttributeJoin<?, ?>) sqmJoin ), lhsTableGroup, ownerTableGroup, transitive );
} }
else if ( sqmJoin instanceof SqmCrossJoin<?> ) { else if ( sqmJoin instanceof SqmCrossJoin<?> ) {
consumeCrossJoin( ( (SqmCrossJoin<?>) sqmJoin ), lhsTableGroup ); return consumeCrossJoin( ( (SqmCrossJoin<?>) sqmJoin ), lhsTableGroup, transitive );
} }
else if ( sqmJoin instanceof SqmEntityJoin<?> ) { else if ( sqmJoin instanceof SqmEntityJoin<?> ) {
consumeEntityJoin( ( (SqmEntityJoin<?>) sqmJoin ), lhsTableGroup ); return consumeEntityJoin( ( (SqmEntityJoin<?>) sqmJoin ), lhsTableGroup, transitive );
} }
else { else {
throw new InterpretationException( "Could not resolve SqmJoin [" + sqmJoin.getNavigablePath() + "] to TableGroupJoin" ); throw new InterpretationException( "Could not resolve SqmJoin [" + sqmJoin.getNavigablePath() + "] to TableGroupJoin" );
} }
} }
private void consumeAttributeJoin(SqmAttributeJoin<?, ?> sqmJoin, TableGroup lhsTableGroup) { private TableGroup consumeAttributeJoin(
SqmAttributeJoin<?, ?> sqmJoin,
TableGroup lhsTableGroup,
TableGroup ownerTableGroup,
boolean transitive) {
final SqmPathSource<?> pathSource = sqmJoin.getReferencedPathSource(); final SqmPathSource<?> pathSource = sqmJoin.getReferencedPathSource();
final SqmJoinType sqmJoinType = sqmJoin.getSqmJoinType();
final TableGroupJoin joinedTableGroupJoin; final TableGroupJoin joinedTableGroupJoin;
final TableGroup joinedTableGroup; final TableGroup joinedTableGroup;
@ -2084,7 +2136,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final NavigablePath sqmJoinNavigablePath = sqmJoin.getNavigablePath(); final NavigablePath sqmJoinNavigablePath = sqmJoin.getNavigablePath();
final NavigablePath parentNavigablePath = sqmJoinNavigablePath.getParent(); final NavigablePath parentNavigablePath = sqmJoinNavigablePath.getParent();
final ModelPart modelPart = lhsTableGroup.getModelPart().findSubPart( final ModelPart modelPart = ownerTableGroup.getModelPart().findSubPart(
pathSource.getPathName(), pathSource.getPathName(),
SqmMappingModelHelper.resolveExplicitTreatTarget( sqmJoin, this ) SqmMappingModelHelper.resolveExplicitTreatTarget( sqmJoin, this )
); );
@ -2108,11 +2160,10 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
joinedTableGroupJoin = pluralAttributeMapping.createTableGroupJoin( joinedTableGroupJoin = pluralAttributeMapping.createTableGroupJoin(
joinPath, joinPath,
lhsTableGroup, ownerTableGroup,
sqmJoin.getExplicitAlias(), sqmJoin.getExplicitAlias(),
sqmJoin.getSqmJoinType().getCorrespondingSqlJoinType(), sqmJoinType.getCorrespondingSqlJoinType(),
sqmJoin.isFetched(), sqmJoin.isFetched(),
false,
this this
); );
} }
@ -2123,13 +2174,19 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
joinedTableGroupJoin = ( (TableGroupJoinProducer) modelPart ).createTableGroupJoin( joinedTableGroupJoin = ( (TableGroupJoinProducer) modelPart ).createTableGroupJoin(
joinPath, joinPath,
lhsTableGroup, ownerTableGroup,
sqmJoin.getExplicitAlias(), sqmJoin.getExplicitAlias(),
sqmJoin.getSqmJoinType().getCorrespondingSqlJoinType(), sqmJoinType.getCorrespondingSqlJoinType(),
sqmJoin.isFetched(), sqmJoin.isFetched(),
false,
this this
); );
// Since this is an explicit join, we force the initialization of a possible lazy table group
// to retain the cardinality, but only if this is a non-trivial attribute join.
// Left or inner singular attribute joins without a predicate can be safely optimized away
if ( sqmJoin.getJoinPredicate() != null || sqmJoinType != SqmJoinType.INNER && sqmJoinType != SqmJoinType.LEFT ) {
joinedTableGroupJoin.getJoinedGroup().getPrimaryTableReference();
}
} }
joinedTableGroup = joinedTableGroupJoin.getJoinedGroup(); joinedTableGroup = joinedTableGroupJoin.getJoinedGroup();
@ -2153,8 +2210,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
currentlyProcessingJoin = oldJoin; currentlyProcessingJoin = oldJoin;
} }
if ( transitive ) {
consumeExplicitJoins( sqmJoin, joinedTableGroup ); consumeExplicitJoins( sqmJoin, joinedTableGroup );
} }
return joinedTableGroup;
}
private NavigablePath getJoinNavigablePath( private NavigablePath getJoinNavigablePath(
NavigablePath sqmJoinNavigablePath, NavigablePath sqmJoinNavigablePath,
@ -2173,7 +2233,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
} }
} }
private void consumeCrossJoin(SqmCrossJoin sqmJoin, TableGroup lhsTableGroup) { private TableGroup consumeCrossJoin(SqmCrossJoin<?> sqmJoin, TableGroup lhsTableGroup, boolean transitive) {
final EntityPersister entityDescriptor = resolveEntityPersister( sqmJoin.getReferencedPathSource() ); final EntityPersister entityDescriptor = resolveEntityPersister( sqmJoin.getReferencedPathSource() );
final TableGroup tableGroup = entityDescriptor.createRootTableGroup( final TableGroup tableGroup = entityDescriptor.createRootTableGroup(
@ -2198,10 +2258,13 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
getFromClauseIndex().register( sqmJoin, tableGroup ); getFromClauseIndex().register( sqmJoin, tableGroup );
if ( transitive ) {
consumeExplicitJoins( sqmJoin, tableGroupJoin.getJoinedGroup() ); consumeExplicitJoins( sqmJoin, tableGroupJoin.getJoinedGroup() );
} }
return tableGroup;
}
private void consumeEntityJoin(SqmEntityJoin sqmJoin, TableGroup lhsTableGroup) { private TableGroup consumeEntityJoin(SqmEntityJoin<?> sqmJoin, TableGroup lhsTableGroup, boolean transitive) {
final EntityPersister entityDescriptor = resolveEntityPersister( sqmJoin.getReferencedPathSource() ); final EntityPersister entityDescriptor = resolveEntityPersister( sqmJoin.getReferencedPathSource() );
final SqlAstJoinType correspondingSqlJoinType = sqmJoin.getSqmJoinType().getCorrespondingSqlJoinType(); final SqlAstJoinType correspondingSqlJoinType = sqmJoin.getSqmJoinType().getCorrespondingSqlJoinType();
@ -2236,9 +2299,11 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
// Note that we add the entity join after processing the predicate because implicit joins needed in there // 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 // can be just ordered right before the entity join without changing the semantics
lhsTableGroup.addTableGroupJoin( tableGroupJoin ); lhsTableGroup.addTableGroupJoin( tableGroupJoin );
if ( transitive ) {
consumeExplicitJoins( sqmJoin, tableGroupJoin.getJoinedGroup() ); consumeExplicitJoins( sqmJoin, tableGroupJoin.getJoinedGroup() );
} }
return tableGroup;
}
private <X> X prepareReusablePath(SqmPath<?> sqmPath, Supplier<X> supplier) { private <X> X prepareReusablePath(SqmPath<?> sqmPath, Supplier<X> supplier) {
final Consumer<TableGroup> implicitJoinChecker; final Consumer<TableGroup> implicitJoinChecker;
@ -2354,13 +2419,20 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
null, null,
defaultSqlAstJoinType, defaultSqlAstJoinType,
false, 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 this
); );
// Implicit joins in the ON clause of attribute joins need to be added as nested table group joins
// We don't have to do that for entity joins etc. as these do not have an inherent dependency on the lhs.
// We can just add the implicit join before the currently processing join
// See consumeEntityJoin for details
final boolean nested = currentClauseStack.getCurrent() == Clause.FROM
&& currentlyProcessingJoin instanceof SqmAttributeJoin<?, ?>;
if ( nested ) {
parentTableGroup.addNestedTableGroupJoin( tableGroupJoin );
}
else {
parentTableGroup.addTableGroupJoin( tableGroupJoin );
}
tableGroup = tableGroupJoin.getJoinedGroup(); tableGroup = tableGroupJoin.getJoinedGroup();
} }
@ -2822,12 +2894,12 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
null, null,
SqlAstJoinType.INNER, SqlAstJoinType.INNER,
false, false,
false,
sqlAliasBaseManager, sqlAliasBaseManager,
getSqlExpressionResolver(), getSqlExpressionResolver(),
creationContext creationContext
); );
parentTableGroup.addTableGroupJoin( tableGroupJoin );
return tableGroupJoin.getJoinedGroup(); return tableGroupJoin.getJoinedGroup();
} }
); );
@ -4657,16 +4729,17 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
).getExpressionType(); ).getExpressionType();
// The creation of the table group join against the correlated table group // The creation of the table group join against the correlated table group
// has the side effect that the from and where clause of the sub-query are set // has the side effect that the from and where clause of the sub-query are set
tableGroup.addTableGroupJoin(
pluralAttributeMapping.createTableGroupJoin( pluralAttributeMapping.createTableGroupJoin(
pluralPathNavPath, pluralPathNavPath,
tableGroup, tableGroup,
sqmPluralPath.getExplicitAlias(), sqmPluralPath.getExplicitAlias(),
SqlAstJoinType.INNER, SqlAstJoinType.INNER,
false, false,
false,
sqlAliasBaseManager, sqlAliasBaseManager,
subQueryState, subQueryState,
creationContext creationContext
)
); );
final ForeignKeyDescriptor collectionKeyDescriptor = pluralAttributeMapping.getKeyDescriptor(); final ForeignKeyDescriptor collectionKeyDescriptor = pluralAttributeMapping.getKeyDescriptor();
@ -5072,9 +5145,9 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
alias, alias,
tableGroupJoinProducer.getDefaultSqlAstJoinType( lhs ), tableGroupJoinProducer.getDefaultSqlAstJoinType( lhs ),
true, true,
false,
this this
); );
lhs.addTableGroupJoin( tableGroupJoin );
return tableGroupJoin.getJoinedGroup(); return tableGroupJoin.getJoinedGroup();
} }
); );

View File

@ -311,10 +311,9 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
null, null,
SqlAstJoinType.INNER, SqlAstJoinType.INNER,
false, false,
false,
sqlAstCreationState sqlAstCreationState
); );
parentTableGroup.addTableGroupJoin( tableGroupJoin );
return tableGroupJoin.getJoinedGroup(); return tableGroupJoin.getJoinedGroup();
} }
); );

View File

@ -32,6 +32,7 @@ import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.metamodel.model.domain.SetPersistentAttribute; import org.hibernate.metamodel.model.domain.SetPersistentAttribute;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute; import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.query.criteria.JpaEntityJoin;
import org.hibernate.query.criteria.JpaPath; import org.hibernate.query.criteria.JpaPath;
import org.hibernate.query.criteria.JpaSelection; import org.hibernate.query.criteria.JpaSelection;
import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.NodeBuilder;
@ -43,6 +44,7 @@ import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.spi.SqmCreationHelper; import org.hibernate.query.sqm.spi.SqmCreationHelper;
import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin; import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
import org.hibernate.query.sqm.tree.from.SqmFrom; import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.query.sqm.tree.from.SqmJoin; import org.hibernate.query.sqm.tree.from.SqmJoin;
import org.hibernate.query.sqm.tree.from.SqmRoot; import org.hibernate.query.sqm.tree.from.SqmRoot;
@ -142,7 +144,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
@Override @Override
public boolean hasJoins() { public boolean hasJoins() {
return ! (joins == null || joins.isEmpty() ); return !( joins == null || joins.isEmpty() );
} }
@Override @Override
@ -156,6 +158,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
joins = new ArrayList<>(); joins = new ArrayList<>();
} }
joins.add( join ); joins.add( join );
findRoot().addOrderedJoin( join );
} }
@Override @Override
@ -408,6 +411,29 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
); );
} }
@Override
public <X> JpaEntityJoin<X> join(Class<X> entityJavaType) {
return join( nodeBuilder().getDomainModel().entity( entityJavaType ) );
}
@Override
public <X> JpaEntityJoin<X> join(EntityDomainType<X> entity) {
return join( entity, SqmJoinType.INNER );
}
@Override
public <X> JpaEntityJoin<X> join(Class<X> entityJavaType, SqmJoinType joinType) {
return join( nodeBuilder().getDomainModel().entity( entityJavaType ), joinType );
}
@Override
public <X> JpaEntityJoin<X> join(EntityDomainType<X> entity, SqmJoinType joinType) {
final SqmEntityJoin<X> join = new SqmEntityJoin<>( entity, null, joinType, findRoot() );
//noinspection unchecked
addSqmJoin( (SqmJoin<T, ?>) join );
return join;
}
@Override @Override
public Set<Fetch<T, ?>> getFetches() { public Set<Fetch<T, ?>> getFetches() {
//noinspection unchecked //noinspection unchecked

View File

@ -6,6 +6,9 @@
*/ */
package org.hibernate.query.sqm.tree.from; package org.hibernate.query.sqm.tree.from;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
@ -31,6 +34,7 @@ import org.hibernate.sql.results.graph.DomainResultCreationState;
public class SqmRoot<E> extends AbstractSqmFrom<E,E> implements JpaRoot<E>, DomainResultProducer<E> { public class SqmRoot<E> extends AbstractSqmFrom<E,E> implements JpaRoot<E>, DomainResultProducer<E> {
private final boolean allowJoins; private final boolean allowJoins;
private List<SqmJoin<?, ?>> orderedJoins;
public SqmRoot( public SqmRoot(
EntityDomainType<E> entityType, EntityDomainType<E> entityType,
@ -68,6 +72,28 @@ public class SqmRoot<E> extends AbstractSqmFrom<E,E> implements JpaRoot<E>, Doma
return allowJoins; return allowJoins;
} }
public List<SqmJoin<?, ?>> getOrderedJoins() {
return orderedJoins;
}
public void addOrderedJoin(SqmJoin<?, ?> join) {
if ( orderedJoins == null ) {
// If we encounter anything but an attribute join, we need to order joins strictly
if ( !( join instanceof SqmAttributeJoin<?, ?> ) ) {
orderedJoins = new ArrayList<>();
visitSqmJoins( this::addOrderedJoinTransitive );
}
}
else {
orderedJoins.add( join );
}
}
private void addOrderedJoinTransitive(SqmJoin<?, ?> join) {
orderedJoins.add( join );
join.visitSqmJoins( this::addOrderedJoinTransitive );
}
@Override @Override
public void addSqmJoin(SqmJoin<E, ?> join) { public void addSqmJoin(SqmJoin<E, ?> join) {
if ( !allowJoins ) { if ( !allowJoins ) {
@ -127,29 +153,6 @@ public class SqmRoot<E> extends AbstractSqmFrom<E,E> implements JpaRoot<E>, Doma
return true; return true;
} }
@Override
public <X> JpaEntityJoin<X> join(Class<X> entityJavaType) {
return join( nodeBuilder().getDomainModel().entity( entityJavaType ) );
}
@Override
public <X> JpaEntityJoin<X> join(EntityDomainType<X> entity) {
return join( entity, SqmJoinType.INNER );
}
@Override
public <X> JpaEntityJoin<X> join(Class<X> entityJavaType, SqmJoinType joinType) {
return join( nodeBuilder().getDomainModel().entity( entityJavaType ), joinType );
}
@Override
public <X> JpaEntityJoin<X> join(EntityDomainType<X> entity, SqmJoinType joinType) {
final SqmEntityJoin<X> join = new SqmEntityJoin<>( entity, null, joinType, this );
//noinspection unchecked
addSqmJoin( (SqmJoin<E, ?>) join );
return join;
}
@Override @Override
public EntityDomainType<E> getModel() { public EntityDomainType<E> getModel() {
return getReferencedPathSource(); return getReferencedPathSource();

View File

@ -107,20 +107,18 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie
if ( tableGroupJoins == null ) { if ( tableGroupJoins == null ) {
tableGroupJoins = new ArrayList<>(); tableGroupJoins = new ArrayList<>();
} }
if ( !tableGroupJoins.contains( join ) ) { assert !tableGroupJoins.contains( join );
tableGroupJoins.add( join ); tableGroupJoins.add( join );
} }
}
@Override @Override
public void addNestedTableGroupJoin(TableGroupJoin join) { public void addNestedTableGroupJoin(TableGroupJoin join) {
if ( nestedTableGroupJoins == null ) { if ( nestedTableGroupJoins == null ) {
nestedTableGroupJoins = new ArrayList<>(); nestedTableGroupJoins = new ArrayList<>();
} }
if ( !nestedTableGroupJoins.contains( join ) ) { assert !nestedTableGroupJoins.contains( join );
nestedTableGroupJoins.add( join ); nestedTableGroupJoins.add( join );
} }
}
@Override @Override
public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) { public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) {

View File

@ -94,20 +94,18 @@ public class CompositeTableGroup implements VirtualTableGroup {
if ( tableGroupJoins == null ) { if ( tableGroupJoins == null ) {
tableGroupJoins = new ArrayList<>(); tableGroupJoins = new ArrayList<>();
} }
if ( !tableGroupJoins.contains( join ) ) { assert !tableGroupJoins.contains( join );
tableGroupJoins.add( join ); tableGroupJoins.add( join );
} }
}
@Override @Override
public void addNestedTableGroupJoin(TableGroupJoin join) { public void addNestedTableGroupJoin(TableGroupJoin join) {
if ( nestedTableGroupJoins == null ) { if ( nestedTableGroupJoins == null ) {
nestedTableGroupJoins = new ArrayList<>(); nestedTableGroupJoins = new ArrayList<>();
} }
if ( !nestedTableGroupJoins.contains( join ) ) { assert !nestedTableGroupJoins.contains( join );
nestedTableGroupJoins.add( join ); nestedTableGroupJoins.add( join );
} }
}
@Override @Override
public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) { public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) {

View File

@ -49,9 +49,7 @@ public class CorrelatedTableGroup extends AbstractTableGroup {
@Override @Override
public void addTableGroupJoin(TableGroupJoin join) { public void addTableGroupJoin(TableGroupJoin join) {
if ( getTableGroupJoins().contains( join ) ) { assert !getTableGroupJoins().contains( join );
return;
}
assert join.getJoinType() == SqlAstJoinType.INNER; assert join.getJoinType() == SqlAstJoinType.INNER;
querySpec.getFromClause().addRoot( join.getJoinedGroup() ); querySpec.getFromClause().addRoot( join.getJoinedGroup() );
joinPredicateConsumer.accept( join.getPredicate() ); joinPredicateConsumer.accept( join.getPredicate() );

View File

@ -32,7 +32,6 @@ public interface TableGroupJoinProducer extends TableGroupProducer {
String explicitSourceAlias, String explicitSourceAlias,
SqlAstJoinType sqlAstJoinType, SqlAstJoinType sqlAstJoinType,
boolean fetched, boolean fetched,
boolean nested,
SqlAstCreationState creationState) { SqlAstCreationState creationState) {
return createTableGroupJoin( return createTableGroupJoin(
navigablePath, navigablePath,
@ -40,7 +39,6 @@ public interface TableGroupJoinProducer extends TableGroupProducer {
explicitSourceAlias, explicitSourceAlias,
sqlAstJoinType, sqlAstJoinType,
fetched, fetched,
nested,
creationState.getSqlAliasBaseGenerator(), creationState.getSqlAliasBaseGenerator(),
creationState.getSqlExpressionResolver(), creationState.getSqlExpressionResolver(),
creationState.getCreationContext() creationState.getCreationContext()
@ -56,7 +54,6 @@ public interface TableGroupJoinProducer extends TableGroupProducer {
String explicitSourceAlias, String explicitSourceAlias,
SqlAstJoinType sqlAstJoinType, SqlAstJoinType sqlAstJoinType,
boolean fetched, boolean fetched,
boolean nested,
SqlAliasBaseGenerator aliasBaseGenerator, SqlAliasBaseGenerator aliasBaseGenerator,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext); SqlAstCreationContext creationContext);

View File

@ -92,20 +92,18 @@ public class UnionTableGroup implements VirtualTableGroup {
if ( tableGroupJoins == null ) { if ( tableGroupJoins == null ) {
tableGroupJoins = new ArrayList<>(); tableGroupJoins = new ArrayList<>();
} }
if ( !tableGroupJoins.contains( join ) ) { assert !tableGroupJoins.contains( join );
tableGroupJoins.add( join ); tableGroupJoins.add( join );
} }
}
@Override @Override
public void addNestedTableGroupJoin(TableGroupJoin join) { public void addNestedTableGroupJoin(TableGroupJoin join) {
if ( nestedTableGroupJoins == null ) { if ( nestedTableGroupJoins == null ) {
nestedTableGroupJoins = new ArrayList<>(); nestedTableGroupJoins = new ArrayList<>();
} }
if ( !nestedTableGroupJoins.contains( join ) ) { assert !nestedTableGroupJoins.contains( join );
nestedTableGroupJoins.add( join ); nestedTableGroupJoins.add( join );
} }
}
@Override @Override
public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) { public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) {

View File

@ -68,9 +68,9 @@ public class EmbeddableFetchImpl extends AbstractFetchParent implements Embeddab
null, null,
nullable ? SqlAstJoinType.LEFT : SqlAstJoinType.INNER, nullable ? SqlAstJoinType.LEFT : SqlAstJoinType.INNER,
true, true,
false,
creationState.getSqlAstCreationState() creationState.getSqlAstCreationState()
); );
lhsTableGroup.addTableGroupJoin( tableGroupJoin );
return tableGroupJoin.getJoinedGroup(); return tableGroupJoin.getJoinedGroup();
} }

View File

@ -54,10 +54,9 @@ public class EmbeddableResultImpl<T> extends AbstractFetchParent implements Embe
resultVariable, resultVariable,
SqlAstJoinType.INNER, SqlAstJoinType.INNER,
true, true,
false,
creationState.getSqlAstCreationState() creationState.getSqlAstCreationState()
); );
tableGroup.addTableGroupJoin( tableGroupJoin );
return tableGroupJoin.getJoinedGroup(); return tableGroupJoin.getJoinedGroup();
} }
); );

View File

@ -128,6 +128,28 @@ public class OuterJoinTest {
} ); } );
} }
@Test
public void testJoinOrderAttributeJoinDependsOnEntityJoin(EntityManagerFactoryScope scope) {
scope.inTransaction( em -> {
List<Tuple> resultList = em.createQuery(
"SELECT a.value, b.value, c.value " +
"FROM A a " +
"INNER JOIN a.cAssociationByKey c " +
"INNER JOIN B b ON a.key = b.key " +
"INNER JOIN c.association ca ON ca.key = b.key " +
"ORDER BY a.key, b.key, c.key",
Tuple.class
)
.getResultList();
assertEquals( 1, resultList.size() );
assertEquals( "a", resultList.get( 0 ).get( 0 ) );
assertEquals( "d", resultList.get( 0 ).get( 1 ) );
assertEquals( "g", resultList.get( 0 ).get( 2 ) );
} );
}
@Test @Test
public void testJoinOrderWithRightNormalJoin(EntityManagerFactoryScope scope) { public void testJoinOrderWithRightNormalJoin(EntityManagerFactoryScope scope) {
scope.inTransaction( em -> { scope.inTransaction( em -> {