Render implicit joins as nested table group joins instead of sub queries

This commit is contained in:
Christian Beikov 2021-10-23 15:34:39 +02:00
parent 756afb8788
commit 1456a2dd7f
31 changed files with 322 additions and 235 deletions

View File

@ -421,6 +421,7 @@ public class LoaderSelectBuilder {
null,
SqlAstJoinType.LEFT,
true,
false,
sqlAstCreationState
);
tableGroup = tableGroupJoin.getJoinedGroup();

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -77,13 +77,13 @@ public class TableGroupImpl implements TableGroup {
}
@Override
public boolean canUseInnerJoins() {
return false;
public List<TableGroupJoin> 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<TableGroupJoin> consumer) {
if ( tableGroupJoins != null ) {
@ -103,6 +108,10 @@ public class TableGroupImpl implements TableGroup {
}
}
@Override
public void visitNestedTableGroupJoins(Consumer<TableGroupJoin> consumer) {
}
@Override
public void applyAffectedTableNames(Consumer<String> nameCollector) {

View File

@ -118,6 +118,7 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue
tableAlias,
SqlAstJoinType.INNER,
true,
false,
s -> sqlAliasBase,
creationState.getSqlExpressionResolver(),
creationState.getCreationContext()

View File

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

View File

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

View File

@ -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<T extends Statement> extends Base
private final Stack<FromClauseIndex> fromClauseIndexStack = new StandardStack<>();
private SqlAstProcessingState lastPoppedProcessingState;
private FromClauseIndex lastPoppedFromClauseIndex;
private SqlAstQueryPartProcessingStateImpl joinPredicateWrapper;
private SqmJoin<?, ?> currentJoin;
private SqmJoin<?, ?> currentlyProcessingJoin;
private final Stack<Clause> currentClauseStack = new StandardStack<>();
@ -2114,6 +2112,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
sqmJoin.getExplicitAlias(),
sqmJoin.getSqmJoinType().getCorrespondingSqlJoinType(),
sqmJoin.isFetched(),
false,
this
);
}
@ -2128,6 +2127,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
sqmJoin.getExplicitAlias(),
sqmJoin.getSqmJoinType().getCorrespondingSqlJoinType(),
sqmJoin.isFetched(),
false,
this
);
}
@ -2147,22 +2147,10 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> 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<T extends Statement> 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> X prepareReusablePath(SqmPath<?> sqmPath, Supplier<X> supplier) {
@ -2271,41 +2250,15 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> 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<Integer> 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<TableGroup> implicitJoinChecker) {
boolean useInnerJoin,
Consumer<TableGroup> implicitJoinChecker) {
final JpaPath<?> parentPath = sqmPath.getParentPath();
final TableGroup tableGroup = fromClauseIndex.findTableGroup( parentPath.getNavigablePath() );
if ( tableGroup == null ) {
@ -2354,105 +2307,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> 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<T extends Statement> 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<T extends Statement> extends Base
null,
SqlAstJoinType.INNER,
false,
false,
sqlAliasBaseManager,
getSqlExpressionResolver(),
creationContext
@ -4340,17 +4200,13 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> 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<T extends Statement> extends Base
sqmPluralPath.getExplicitAlias(),
SqlAstJoinType.INNER,
false,
false,
sqlAliasBaseManager,
subQueryState,
creationContext
@ -5215,6 +5072,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
alias,
tableGroupJoinProducer.getDefaultSqlAstJoinType( lhs ),
true,
false,
this
);
return tableGroupJoin.getJoinedGroup();

View File

@ -311,6 +311,7 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
null,
SqlAstJoinType.INNER,
false,
false,
sqlAstCreationState
);

View File

@ -3403,9 +3403,10 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> 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<T extends JdbcOperation> implemen
if ( realTableGroup ) {
renderTableReferenceJoins( tableGroup );
processNestedTableGroupJoins( tableGroup );
appendSql( CLOSE_PARENTHESIS );
}
@ -3444,6 +3446,17 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
}
}
private boolean hasTableGroupsToRender(List<TableGroupJoin> 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<T extends JdbcOperation> 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<T extends JdbcOperation> 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;
}

View File

@ -59,6 +59,11 @@ public class CteTableGroup implements TableGroup {
return Collections.emptyList();
}
@Override
public List<TableGroupJoin> getNestedTableGroupJoins() {
return Collections.emptyList();
}
@Override
public boolean canUseInnerJoins() {
return false;
@ -87,18 +92,22 @@ public class CteTableGroup implements TableGroup {
public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) {
}
@Override
public void visitNestedTableGroupJoins(Consumer<TableGroupJoin> 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( );
}

View File

@ -27,6 +27,7 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie
private final SqlAliasBase sqlAliasBase;
private List<TableGroupJoin> tableGroupJoins;
private List<TableGroupJoin> nestedTableGroupJoins;
private final SessionFactoryImplementor sessionFactory;
@ -87,13 +88,18 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie
}
@Override
public boolean canUseInnerJoins() {
return canUseInnerJoins;
public List<TableGroupJoin> 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<TableGroupJoin> consumer) {
if ( tableGroupJoins != null ) {
@ -113,6 +129,13 @@ public abstract class AbstractTableGroup extends AbstractColumnReferenceQualifie
}
}
@Override
public void visitNestedTableGroupJoins(Consumer<TableGroupJoin> consumer) {
if ( nestedTableGroupJoins != null ) {
nestedTableGroupJoins.forEach( consumer );
}
}
@Override
public String toString() {
return getClass().getSimpleName() + '(' + getNavigablePath() + ')';

View File

@ -25,6 +25,7 @@ public class CompositeTableGroup implements VirtualTableGroup {
private final boolean fetched;
private List<TableGroupJoin> tableGroupJoins;
private List<TableGroupJoin> nestedTableGroupJoins;
public CompositeTableGroup(
NavigablePath navigablePath,
@ -74,13 +75,18 @@ public class CompositeTableGroup implements VirtualTableGroup {
}
@Override
public boolean canUseInnerJoins() {
return underlyingTableGroup.canUseInnerJoins();
public List<TableGroupJoin> 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<TableGroupJoin> consumer) {
if ( tableGroupJoins != null ) {
@ -100,6 +116,13 @@ public class CompositeTableGroup implements VirtualTableGroup {
}
}
@Override
public void visitNestedTableGroupJoins(Consumer<TableGroupJoin> consumer) {
if ( nestedTableGroupJoins != null ) {
nestedTableGroupJoins.forEach( consumer );
}
}
@Override
public void applyAffectedTableNames(Consumer<String> 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()

View File

@ -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()

View File

@ -106,8 +106,8 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
}
@Override
public boolean hasTableGroupJoins() {
return tableGroup != null && tableGroup.hasTableGroupJoins();
public List<TableGroupJoin> 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<TableGroupJoin> consumer) {
if ( tableGroup != null ) {
@ -122,6 +127,13 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
}
}
@Override
public void visitNestedTableGroupJoins(Consumer<TableGroupJoin> 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

View File

@ -93,12 +93,12 @@ public class MutatingTableReferenceGroupWrapper implements VirtualTableGroup {
}
@Override
public boolean canUseInnerJoins() {
return false;
public List<TableGroupJoin> 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<TableGroupJoin> consumer) {
}
@Override
public void visitNestedTableGroupJoins(Consumer<TableGroupJoin> consumer) {
}
@Override
public List<TableReferenceJoin> getTableReferenceJoins() {
return Collections.emptyList();

View File

@ -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 ) {

View File

@ -41,15 +41,19 @@ public interface TableGroup extends SqlAstNode, ColumnReferenceQualifier, SqmPat
String getSourceAlias();
List<TableGroupJoin> getTableGroupJoins();
List<TableGroupJoin> getNestedTableGroupJoins();
boolean canUseInnerJoins();
boolean hasTableGroupJoins();
void addTableGroupJoin(TableGroupJoin join);
void addNestedTableGroupJoin(TableGroupJoin join);
void visitTableGroupJoins(Consumer<TableGroupJoin> consumer);
void visitNestedTableGroupJoins(Consumer<TableGroupJoin> consumer);
void applyAffectedTableNames(Consumer<String> nameCollector);
TableReference getPrimaryTableReference();

View File

@ -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);

View File

@ -23,6 +23,7 @@ public class UnionTableGroup implements VirtualTableGroup {
private final boolean canUseInnerJoins;
private final NavigablePath navigablePath;
private List<TableGroupJoin> tableGroupJoins;
private List<TableGroupJoin> 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<TableGroupJoin> 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<TableGroupJoin> consumer) {
if ( tableGroupJoins != null ) {
@ -98,6 +114,13 @@ public class UnionTableGroup implements VirtualTableGroup {
}
}
@Override
public void visitNestedTableGroupJoins(Consumer<TableGroupJoin> consumer) {
if ( nestedTableGroupJoins != null ) {
nestedTableGroupJoins.forEach( consumer );
}
}
@Override
public void applyAffectedTableNames(Consumer<String> 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()

View File

@ -66,13 +66,13 @@ public class EntityCollectionPartTableGroup implements TableGroup {
}
@Override
public boolean canUseInnerJoins() {
return collectionTableGroup.canUseInnerJoins();
public List<TableGroupJoin> 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<TableGroupJoin> consumer) {
collectionTableGroup.visitTableGroupJoins( consumer );
}
@Override
public void visitNestedTableGroupJoins(Consumer<TableGroupJoin> consumer) {
collectionTableGroup.visitNestedTableGroupJoins( consumer );
}
@Override
public void applyAffectedTableNames(Consumer<String> nameCollector) {
collectionTableGroup.applyAffectedTableNames( nameCollector );

View File

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

View File

@ -54,6 +54,7 @@ public class EmbeddableResultImpl<T> extends AbstractFetchParent implements Embe
resultVariable,
SqlAstJoinType.INNER,
true,
false,
creationState.getSqlAstCreationState()
);

View File

@ -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) -> {

View File

@ -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

View File

@ -174,13 +174,13 @@ public class OrderByFragmentFunction extends AbstractSqmFunctionDescriptor {
}
@Override
public boolean canUseInnerJoins() {
return delegate.canUseInnerJoins();
public List<TableGroupJoin> 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<TableGroupJoin> consumer) {
delegate.visitTableGroupJoins( consumer );
}
@Override
public void visitNestedTableGroupJoins(Consumer<TableGroupJoin> consumer) {
delegate.visitNestedTableGroupJoins( consumer );
}
@Override
public void applyAffectedTableNames(Consumer<String> nameCollector) {
delegate.applyAffectedTableNames( nameCollector );