From 3be99c1c73d24df2c1e26bd672177f425463d714 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Fri, 7 May 2021 17:32:50 +0200 Subject: [PATCH] Introduce enum for describing KEY and TARGET of FK and use/expose that to describe the FK direction of to-one associations --- .../internal/LoaderSqlAstCreationState.java | 12 ++ .../metamodel/mapping/Association.java | 1 + .../mapping/ForeignKeyDescriptor.java | 27 ++-- .../EmbeddedForeignKeyDescriptor.java | 128 ++++++++---------- .../internal/EntityCollectionPart.java | 5 + .../internal/PluralAttributeMappingImpl.java | 19 +-- .../internal/SimpleForeignKeyDescriptor.java | 42 ++---- .../internal/ToOneAttributeMapping.java | 57 ++++---- .../DomainResultCreationStateImpl.java | 11 ++ .../sqm/sql/BaseSqmToSqlAstConverter.java | 13 +- .../EntityValuedPathInterpretation.java | 47 +++++-- .../graph/DomainResultCreationState.java | 9 ++ .../internal/CollectionDomainResult.java | 2 +- .../entity/AbstractEntityResultGraphNode.java | 14 +- .../internal/EntityDelayedResultImpl.java | 12 +- .../CircularBiDirectionalFetchImpl.java | 5 + .../internal/domain/CircularFetchImpl.java | 7 +- 17 files changed, 219 insertions(+), 192 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSqlAstCreationState.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSqlAstCreationState.java index 78a0ab7d5f..505de74185 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSqlAstCreationState.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSqlAstCreationState.java @@ -17,6 +17,7 @@ import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.graph.spi.AppliedGraph; import org.hibernate.metamodel.mapping.AssociationKey; +import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.query.Limit; import org.hibernate.query.NavigablePath; @@ -56,6 +57,7 @@ public class LoaderSqlAstCreationState private final FetchProcessor fetchProcessor; private boolean resolvingCircularFetch; + private ForeignKeyDescriptor.Side currentlyResolvingForeignKeySide; private Set visitedAssociationKeys = new HashSet<>(); public LoaderSqlAstCreationState( @@ -125,6 +127,16 @@ public class LoaderSqlAstCreationState this.resolvingCircularFetch = resolvingCircularFetch; } + @Override + public ForeignKeyDescriptor.Side getCurrentlyResolvingForeignKeyPart() { + return currentlyResolvingForeignKeySide; + } + + @Override + public void setCurrentlyResolvingForeignKeyPart(ForeignKeyDescriptor.Side currentlyResolvingForeignKeySide) { + this.currentlyResolvingForeignKeySide = currentlyResolvingForeignKeySide; + } + @Override public boolean forceIdentifierSelection() { return forceIdentifierSelection; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/Association.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/Association.java index 9de82f2057..1826d27bec 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/Association.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/Association.java @@ -13,4 +13,5 @@ import org.hibernate.sql.results.graph.Fetchable; */ public interface Association extends Fetchable { ForeignKeyDescriptor getForeignKeyDescriptor(); + ForeignKeyDescriptor.Side getSide(); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ForeignKeyDescriptor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ForeignKeyDescriptor.java index 2c5a50c749..105255ec4a 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ForeignKeyDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ForeignKeyDescriptor.java @@ -24,6 +24,12 @@ import org.hibernate.sql.results.graph.DomainResultCreationState; * Descriptor for foreign-keys */ public interface ForeignKeyDescriptor extends VirtualModelPart { + + enum Side { + KEY, + TARGET; + } + String PART_NAME = "{fk}"; String getKeyTable(); @@ -38,7 +44,7 @@ public interface ForeignKeyDescriptor extends VirtualModelPart { * Create a DomainResult for the referring-side of the fk */ DomainResult createKeyDomainResult( - NavigablePath collectionPath, + NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState); @@ -46,24 +52,19 @@ public interface ForeignKeyDescriptor extends VirtualModelPart { * Create a DomainResult for the target-side of the fk */ DomainResult createTargetDomainResult( - NavigablePath collectionPath, - TableGroup tableGroup, - DomainResultCreationState creationState); - - DomainResult createCollectionFetchDomainResult( - NavigablePath collectionPath, - TableGroup tableGroup, - DomainResultCreationState creationState); - - DomainResult createDomainResult( NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState); - DomainResult createDomainResult( + DomainResult createCollectionFetchDomainResult( + NavigablePath collectionPath, + TableGroup tableGroup, + DomainResultCreationState creationState); + + DomainResult createDomainResult( NavigablePath navigablePath, TableGroup tableGroup, - boolean isKeyReferringSide, + Side side, DomainResultCreationState creationState); Predicate generateJoinPredicate( 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 04ce00d8e6..ba1c5484a6 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 @@ -58,7 +58,7 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { private final SelectableMappings keySelectableMappings; private final String targetTable; private final SelectableMappings targetSelectableMappings; - private AssociationKey associationKey; + private final AssociationKey associationKey; public EmbeddedForeignKeyDescriptor( EmbeddableValuedModelPart keyMappingType, @@ -74,6 +74,13 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { this.targetSelectableMappings = targetSelectableMappings; this.targetMappingType = targetMappingType; this.keyMappingType = keyMappingType; + final List columns = new ArrayList<>( keySelectableMappings.getJdbcTypeCount() ); + keySelectableMappings.forEachSelectable( + (columnIndex, selection) -> { + columns.add( selection.getSelectionExpression() ); + } + ); + this.associationKey = new AssociationKey( keyTable, columns ); creationProcess.registerInitializationCallback( "Embedded (composite) FK descriptor " + targetMappingType.getNavigableRole(), @@ -105,6 +112,13 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { keySelectableMappings, creationProcess ); + final List columns = new ArrayList<>( keySelectableMappings.getJdbcTypeCount() ); + keySelectableMappings.forEachSelectable( + (columnIndex, selection) -> { + columns.add( selection.getSelectionExpression() ); + } + ); + this.associationKey = new AssociationKey( keyTable, columns ); } @Override @@ -135,14 +149,13 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { @Override public DomainResult createKeyDomainResult( - NavigablePath collectionPath, + NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) { - assert tableGroup.getTableReference( collectionPath, keyTable ) != null; - return createDomainResult( - collectionPath, + navigablePath, tableGroup, + null, keyTable, keyMappingType, creationState @@ -151,14 +164,15 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { @Override public DomainResult createTargetDomainResult( - NavigablePath collectionPath, + NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) { - assert tableGroup.getTableReference( collectionPath, targetTable ) != null; + assert tableGroup.getTableReference( navigablePath, targetTable ) != null; return createDomainResult( - collectionPath, + navigablePath, tableGroup, + null, targetTable, targetMappingType, creationState @@ -166,7 +180,7 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { } @Override - public DomainResult createCollectionFetchDomainResult( + public DomainResult createCollectionFetchDomainResult( NavigablePath collectionPath, TableGroup tableGroup, DomainResultCreationState creationState) { @@ -174,6 +188,7 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { return createDomainResult( collectionPath, tableGroup, + null, targetTable, targetMappingType, creationState @@ -183,6 +198,7 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { return createDomainResult( collectionPath, tableGroup, + null, keyTable, keyMappingType, creationState @@ -191,29 +207,16 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { } @Override - public DomainResult createDomainResult( + public DomainResult createDomainResult( NavigablePath navigablePath, TableGroup tableGroup, + Side side, DomainResultCreationState creationState) { - return createDomainResult( - navigablePath, - tableGroup, - keyTable, - keyMappingType, - creationState - ); - } - - @Override - public DomainResult createDomainResult( - NavigablePath navigablePath, - TableGroup tableGroup, - boolean isKeyReferringSide, - DomainResultCreationState creationState) { - if ( isKeyReferringSide ) { + if ( side == Side.KEY ) { return createDomainResult( navigablePath, tableGroup, + null, keyTable, keyMappingType, creationState @@ -223,6 +226,7 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { return createDomainResult( navigablePath, tableGroup, + null, targetTable, targetMappingType, creationState @@ -230,9 +234,19 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { } } - private DomainResult createDomainResult( + @Override + public DomainResult createDomainResult( NavigablePath navigablePath, TableGroup tableGroup, + String resultVariable, + DomainResultCreationState creationState) { + return createDomainResult( navigablePath, tableGroup, resultVariable, keyTable, keyMappingType, creationState ); + } + + private DomainResult createDomainResult( + NavigablePath navigablePath, + TableGroup tableGroup, + String resultVariable, String columnContainingTable, EmbeddableValuedModelPart modelPart, DomainResultCreationState creationState) { @@ -279,12 +293,19 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { ); } - return new EmbeddableForeignKeyResultImpl<>( - resultNavigablePath, - modelPart, - null, - creationState - ); + final Side currentForeignKeyResolvingKey = creationState.getCurrentlyResolvingForeignKeyPart(); + try { + creationState.setCurrentlyResolvingForeignKeyPart( keyMappingType == modelPart ? Side.KEY : Side.TARGET ); + return new EmbeddableForeignKeyResultImpl<>( + resultNavigablePath, + modelPart, + resultVariable, + creationState + ); + } + finally { + creationState.setCurrentlyResolvingForeignKeyPart( currentForeignKeyResolvingKey ); + } } @Override @@ -420,15 +441,6 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { @Override public AssociationKey getAssociationKey() { - if ( associationKey == null ) { - final List columns = new ArrayList<>(); - keySelectableMappings.forEachSelectable( - (columnIndex, selection) -> { - columns.add( selection.getSelectionExpression() ); - } - ); - associationKey = new AssociationKey( keyTable, columns ); - } return associationKey; } @@ -457,38 +469,6 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { return targetMappingType.getNavigableRole(); } - @Override - public DomainResult createDomainResult( - NavigablePath navigablePath, - TableGroup tableGroup, - String resultVariable, - DomainResultCreationState creationState) { - final NavigablePath fkNavigablePath = navigablePath.append( getPartName() ); - creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup( - fkNavigablePath, - np -> { - final TableGroupJoin tableGroupJoin = keyMappingType.createTableGroupJoin( - fkNavigablePath, - tableGroup, - null, - null, - true, - LockMode.NONE, - creationState.getSqlAstCreationState() - ); - return tableGroupJoin.getJoinedGroup(); - } - - ); - - return new EmbeddableForeignKeyResultImpl<>( - fkNavigablePath, - keyMappingType, - resultVariable, - creationState - ); - } - @Override public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) { assert domainValue instanceof Object[]; 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 919ff716f5..e8891707d7 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 @@ -331,6 +331,11 @@ public class EntityCollectionPart return collectionDescriptor.getAttributeMapping().getKeyDescriptor(); } + @Override + public ForeignKeyDescriptor.Side getSide() { + return ForeignKeyDescriptor.Side.TARGET; + } + @Override public FetchStyle getStyle() { return FetchStyle.JOIN; 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 bee20ac857..cec54d2708 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 @@ -532,20 +532,11 @@ public class PluralAttributeMappingImpl NavigablePath fetchablePath, DomainResultCreationState creationState, SqlAstCreationState sqlAstCreationState) { - final DomainResult foreignKeyDomainResult; - assert !creationState.isResolvingCircularFetch(); - try { - creationState.setResolvingCircularFetch( true ); - foreignKeyDomainResult = getKeyDescriptor().createDomainResult( - fetchablePath, - sqlAstCreationState.getFromClauseAccess().getTableGroup( fetchParent.getNavigablePath() ), - false, - creationState - ); - } - finally { - creationState.setResolvingCircularFetch( false ); - } + final DomainResult foreignKeyDomainResult = getKeyDescriptor().createTargetDomainResult( + fetchablePath, + sqlAstCreationState.getFromClauseAccess().getTableGroup( fetchParent.getNavigablePath() ), + creationState + ); return new DelayedCollectionFetch( fetchablePath, this, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java index 3f9e48918c..1d5d30ae40 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java @@ -8,7 +8,6 @@ package org.hibernate.metamodel.mapping.internal; import java.util.Collections; import java.util.List; -import java.util.Locale; import java.util.function.Function; import java.util.function.IntFunction; @@ -41,7 +40,6 @@ 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.Predicate; -import org.hibernate.sql.results.DomainResultCreationException; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.Fetch; @@ -121,13 +119,13 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa @Override public DomainResult createKeyDomainResult( - NavigablePath collectionPath, + NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) { - assert tableGroup.getTableReference( collectionPath, keySide.getContainingTableExpression() ) != null; + assert tableGroup.getTableReference( navigablePath, keySide.getContainingTableExpression() ) != null; return createDomainResult( - collectionPath, + navigablePath, tableGroup, keySide, creationState @@ -135,11 +133,11 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa } @Override - public DomainResult createTargetDomainResult(NavigablePath collectionPath, TableGroup tableGroup, DomainResultCreationState creationState) { - assert tableGroup.getTableReference( collectionPath, targetSide.getContainingTableExpression() ) != null; + public DomainResult createTargetDomainResult(NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) { + assert tableGroup.getTableReference( navigablePath, targetSide.getContainingTableExpression() ) != null; return createDomainResult( - collectionPath, + navigablePath, tableGroup, targetSide, creationState @@ -158,36 +156,16 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa public DomainResult createDomainResult( NavigablePath navigablePath, TableGroup tableGroup, + Side side, DomainResultCreationState creationState) { - try { + if ( side == Side.KEY ) { return createDomainResult( navigablePath, tableGroup, keySide, creationState ); } - catch (Exception e) { - throw new DomainResultCreationException( - String.format( - Locale.ROOT, - "Unable to create fk key domain-result `%s.%s` relative to `%s`", - keySide.getContainingTableExpression(), - keySide.getSelectionExpression(), - tableGroup - ), - e - ); + else { + return createDomainResult( navigablePath, tableGroup, targetSide, creationState ); } } - @Override - public DomainResult createDomainResult( - NavigablePath navigablePath, - TableGroup tableGroup, - boolean isKeyReferringSide, - DomainResultCreationState creationState) { - if ( isKeyReferringSide ) { - return createDomainResult( navigablePath, tableGroup, keySide, creationState ); - } - return createDomainResult( navigablePath, tableGroup, targetSide, creationState ); - } - @Override public DomainResult createDomainResult( NavigablePath navigablePath, 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 2d05573085..bf452c8a59 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 @@ -100,8 +100,8 @@ public class ToOneAttributeMapping private final TableGroupProducer declaringTableGroupProducer; private ForeignKeyDescriptor foreignKeyDescriptor; + private ForeignKeyDescriptor.Side lhsSide; private String identifyingColumnsTableExpression; - private boolean isKeyReferringSide; public ToOneAttributeMapping( String name, @@ -284,9 +284,11 @@ public class ToOneAttributeMapping @Override public void setForeignKeyDescriptor(ForeignKeyDescriptor foreignKeyDescriptor) { - isKeyReferringSide = foreignKeyDescriptor.getAssociationKey().getTable().equals( identifyingColumnsTableExpression ); assert identifyingColumnsTableExpression != null; this.foreignKeyDescriptor = foreignKeyDescriptor; + this.lhsSide = foreignKeyDescriptor.getAssociationKey().getTable().equals( identifyingColumnsTableExpression ) + ? ForeignKeyDescriptor.Side.KEY + : ForeignKeyDescriptor.Side.TARGET; } public void setIdentifyingColumnsTableExpression(String tableExpression) { @@ -298,8 +300,13 @@ public class ToOneAttributeMapping return this.foreignKeyDescriptor; } + @Override + public ForeignKeyDescriptor.Side getSide() { + return lhsSide; + } + public boolean canJoinForeignKey(EntityIdentifierMapping identifierMapping) { - return isKeyReferringSide && identifierMapping == getForeignKeyDescriptor().getTargetPart() && !isNullable; + return lhsSide == ForeignKeyDescriptor.Side.KEY && identifierMapping == getForeignKeyDescriptor().getTargetPart() && !isNullable; } public String getReferencedPropertyName() { @@ -467,7 +474,7 @@ public class ToOneAttributeMapping We have a cirularity but it is not bidirectional */ - if ( isKeyReferringSide ) { + if ( lhsSide == ForeignKeyDescriptor.Side.KEY ) { final TableGroup parentTableGroup = creationState .getSqlAstCreationState() .getFromClauseAccess() @@ -476,7 +483,7 @@ public class ToOneAttributeMapping assert !creationState.isResolvingCircularFetch(); try { creationState.setResolvingCircularFetch( true ); - foreignKeyDomainResult = foreignKeyDescriptor.createDomainResult( + foreignKeyDomainResult = foreignKeyDescriptor.createKeyDomainResult( fetchablePath, parentTableGroup, creationState @@ -634,16 +641,24 @@ public class ToOneAttributeMapping */ - final boolean isKeyReferringSide; - if ( isFetchingForeignKey( fetchParent.getNavigablePath() ) ) { - isKeyReferringSide = !this.isKeyReferringSide; + final ForeignKeyDescriptor.Side resolvingKeySideOfForeignKey = creationState.getCurrentlyResolvingForeignKeyPart(); + final ForeignKeyDescriptor.Side side; + if ( resolvingKeySideOfForeignKey == ForeignKeyDescriptor.Side.KEY && this.lhsSide == ForeignKeyDescriptor.Side.TARGET ) { + // If we are currently resolving the key part of a foreign key we do not want to add joins. + // So if the lhs of this association is the target of the FK, we have to use the KEY part to avoid a join + side = ForeignKeyDescriptor.Side.KEY; } - else{ - isKeyReferringSide = this.isKeyReferringSide; + else { + side = this.lhsSide; } - final DomainResult keyResult = foreignKeyDescriptor.createDomainResult( fetchablePath, parentTableGroup, isKeyReferringSide, creationState ); + final DomainResult keyResult = foreignKeyDescriptor.createDomainResult( + fetchablePath, + parentTableGroup, + side, + creationState + ); boolean selectByUniqueKey; - if ( isKeyReferringSide ) { + if ( side == ForeignKeyDescriptor.Side.KEY ) { // case 1.2 selectByUniqueKey = false; } @@ -672,16 +687,6 @@ public class ToOneAttributeMapping ); } - private boolean isFetchingForeignKey(NavigablePath p) { - while ( p != null ) { - if ( ForeignKeyDescriptor.PART_NAME.equals( p.getLocalName() ) ) { - return true; - } - p = p.getParent(); - } - return false; - } - @Override public DomainResult createDelayedDomainResult( NavigablePath navigablePath, @@ -690,7 +695,7 @@ public class ToOneAttributeMapping DomainResultCreationState creationState) { // We only need a join if the key is on the referring side i.e. this is an inverse to-one // and if the FK refers to a non-PK, in which case we must load the whole entity - if ( !isKeyReferringSide || referencedPropertyName != null ) { + if ( lhsSide == ForeignKeyDescriptor.Side.TARGET || referencedPropertyName != null ) { final TableGroupJoin tableGroupJoin = createTableGroupJoin( navigablePath, tableGroup, @@ -776,8 +781,8 @@ public class ToOneAttributeMapping final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( aliasRoot ); // We can only use the parent table group if the FK is located there // If this is false, the FK is on a join table - final boolean canUseParentTableGroup = isKeyReferringSide && declaringTableGroupProducer.containsTableReference( - identifyingColumnsTableExpression ); + final boolean canUseParentTableGroup = lhsSide == ForeignKeyDescriptor.Side.KEY + && declaringTableGroupProducer.containsTableReference( identifyingColumnsTableExpression ); final LazyTableGroup lazyTableGroup = new LazyTableGroup( navigablePath, () -> createTableGroupJoinInternal( @@ -916,7 +921,7 @@ public class ToOneAttributeMapping @Override public int forEachSelectable(int offset, SelectableConsumer consumer) { - if ( isKeyReferringSide ) { + if ( lhsSide == ForeignKeyDescriptor.Side.KEY ) { return foreignKeyDescriptor.visitKeySelectables( offset, consumer ); } else { diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/DomainResultCreationStateImpl.java b/hibernate-core/src/main/java/org/hibernate/query/results/DomainResultCreationStateImpl.java index f425755c27..14e2e626a0 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/DomainResultCreationStateImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/DomainResultCreationStateImpl.java @@ -77,6 +77,7 @@ public class DomainResultCreationStateImpl private final Stack relativePathStack = new StandardStack<>(); private boolean processingKeyFetches = false; private boolean resolvingCircularFetch; + private ForeignKeyDescriptor.Side currentlyResolvingForeignKeySide; public DomainResultCreationStateImpl( String stateIdentifier, @@ -420,4 +421,14 @@ public class DomainResultCreationStateImpl this.resolvingCircularFetch = resolvingCircularFetch; } + @Override + public ForeignKeyDescriptor.Side getCurrentlyResolvingForeignKeyPart() { + return currentlyResolvingForeignKeySide; + } + + @Override + public void setCurrentlyResolvingForeignKeyPart(ForeignKeyDescriptor.Side currentlyResolvingForeignKeySide) { + this.currentlyResolvingForeignKeySide = currentlyResolvingForeignKeySide; + } + } 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 44e8a182b6..6064825f80 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 @@ -339,6 +339,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base private int fetchDepth; private boolean resolvingCircularFetch; + private ForeignKeyDescriptor.Side currentlyResolvingForeignKeySide; private Map collectionFilterPredicates; private OrderByFragmentConsumer orderByFragmentConsumer; @@ -2718,7 +2719,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base final ForeignKeyDescriptor keyDescriptor = mapDescriptor.getKeyDescriptor(); final NavigablePath keyNavigablePath = mapNavigablePath.append( keyDescriptor.getPartName() ); - final DomainResult keyResult = keyDescriptor.createDomainResult( + final DomainResult keyResult = keyDescriptor.createKeyDomainResult( keyNavigablePath, tableGroup, this @@ -4699,6 +4700,16 @@ public abstract class BaseSqmToSqlAstConverter extends Base this.resolvingCircularFetch = resolvingCircularFetch; } + @Override + public ForeignKeyDescriptor.Side getCurrentlyResolvingForeignKeyPart() { + return currentlyResolvingForeignKeySide; + } + + @Override + public void setCurrentlyResolvingForeignKeyPart(ForeignKeyDescriptor.Side currentlyResolvingForeignKeySide) { + this.currentlyResolvingForeignKeySide = currentlyResolvingForeignKeySide; + } + @Internal public interface SqmAliasedNodeCollector { void next(); 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 89f1ec6678..c3cd0e59f8 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 @@ -12,12 +12,14 @@ import java.util.List; import org.hibernate.LockMode; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; +import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.EntityAssociationMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; +import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor; import org.hibernate.query.NavigablePath; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; @@ -61,37 +63,52 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta if ( mapping instanceof EntityAssociationMapping ) { final EntityAssociationMapping associationMapping = (EntityAssociationMapping) mapping; - final ForeignKeyDescriptor keyDescriptor = associationMapping.getForeignKeyDescriptor(); + final ForeignKeyDescriptor fkDescriptor = associationMapping.getForeignKeyDescriptor(); + final String lhsTable; + final ModelPart lhsPart; + if ( associationMapping.getSide() == ForeignKeyDescriptor.Side.KEY ) { + lhsTable = fkDescriptor.getKeyTable(); + lhsPart = fkDescriptor.getKeyPart(); + } + else { + lhsTable = fkDescriptor.getTargetTable(); + lhsPart = fkDescriptor.getTargetPart(); + } final TableReference tableReference = tableGroup.resolveTableReference( sqmPath.getNavigablePath(), - keyDescriptor.getKeyTable() + lhsTable ); - if ( keyDescriptor instanceof SimpleForeignKeyDescriptor ) { - final SimpleForeignKeyDescriptor simpleKeyDescriptor = (SimpleForeignKeyDescriptor) keyDescriptor; + if ( fkDescriptor instanceof SimpleForeignKeyDescriptor ) { + final BasicValuedModelPart basicValuedModelPart = (BasicValuedModelPart) lhsPart; sqlExpression = sqlExprResolver.resolveSqlExpression( - createColumnReferenceKey( tableReference, simpleKeyDescriptor.getSelectionExpression() ), + createColumnReferenceKey( tableReference, basicValuedModelPart.getSelectionExpression() ), processingState -> new ColumnReference( tableReference, - simpleKeyDescriptor, + basicValuedModelPart, sessionFactory ) ); } else { - final List expressions = new ArrayList<>(); - keyDescriptor.forEachSelectable( - (selectionIndex, selectableMapping) -> sqlExprResolver.resolveSqlExpression( - createColumnReferenceKey( tableReference, selectableMapping.getSelectionExpression() ), - processingState -> new ColumnReference( - tableReference, - selectableMapping, - sessionFactory + final List expressions = new ArrayList<>( fkDescriptor.getJdbcTypeCount() ); + lhsPart.forEachSelectable( + (selectionIndex, selectableMapping) -> expressions.add( + sqlExprResolver.resolveSqlExpression( + createColumnReferenceKey( + tableReference, + selectableMapping.getSelectionExpression() + ), + processingState -> new ColumnReference( + tableReference, + selectableMapping, + sessionFactory + ) ) ) ); - sqlExpression = new SqlTuple( expressions, keyDescriptor ); + sqlExpression = new SqlTuple( expressions, lhsPart ); } } else { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/DomainResultCreationState.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/DomainResultCreationState.java index e0f49b964f..92a4f0e449 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/DomainResultCreationState.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/DomainResultCreationState.java @@ -10,6 +10,7 @@ import java.util.List; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.metamodel.mapping.AssociationKey; +import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.spi.SqlAliasBaseManager; @@ -81,4 +82,12 @@ public interface DomainResultCreationState { boolean isResolvingCircularFetch(); void setResolvingCircularFetch(boolean resolvingCircularFetch); + + /** + * Returns the part of the foreign key that is currently being resolved, + * or null if no foreign key is currently being resolved. + */ + ForeignKeyDescriptor.Side getCurrentlyResolvingForeignKeyPart(); + + void setCurrentlyResolvingForeignKeyPart(ForeignKeyDescriptor.Side currentlyResolvingForeignKeySide); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/CollectionDomainResult.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/CollectionDomainResult.java index e2ef70c5df..e6c1151755 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/CollectionDomainResult.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/CollectionDomainResult.java @@ -50,7 +50,7 @@ public class CollectionDomainResult implements DomainResult, CollectionResultGra this.loadingAttribute = loadingAttribute; this.resultVariable = resultVariable; - fkResult = loadingAttribute.getKeyDescriptor().createDomainResult( + fkResult = loadingAttribute.getKeyDescriptor().createKeyDomainResult( loadingPath, tableGroup, creationState diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityResultGraphNode.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityResultGraphNode.java index 42eba13aae..e018f92955 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityResultGraphNode.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityResultGraphNode.java @@ -78,7 +78,7 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent // If we don't do this here, LazyTableGroup#getTableReferenceInternal would have to use the target table in case {id} is encountered if ( ( (ToOneAttributeMapping) referencedModelPart ).canJoinForeignKey( identifierMapping ) ) { identifierResult = ( (ToOneAttributeMapping) referencedModelPart ).getForeignKeyDescriptor() - .createDomainResult( + .createKeyDomainResult( navigablePath, creationState.getSqlAstCreationState() .getFromClauseAccess() @@ -156,12 +156,12 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent ( (ManagedMappingType) mappingType ).visitAttributeMappings( attributeMapping -> { if ( attributeMapping instanceof ToOneAttributeMapping ) { - ( (ToOneAttributeMapping) attributeMapping ).getForeignKeyDescriptor().createDomainResult( - navigablePath.getParent(), - entityTableGroup, - null, - creationState - ); + ( (ToOneAttributeMapping) attributeMapping ).getForeignKeyDescriptor() + .createKeyDomainResult( + navigablePath.getParent(), + entityTableGroup, + creationState + ); } else { attributeMapping.createDomainResult( diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedResultImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedResultImpl.java index 527484de03..9245c14458 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedResultImpl.java @@ -43,13 +43,11 @@ public class EntityDelayedResultImpl implements DomainResult { DomainResultCreationState creationState) { this.navigablePath = navigablePath; this.entityValuedModelPart = entityValuedModelPart; - this.identifierResult = entityValuedModelPart.getForeignKeyDescriptor() - .createDomainResult( - navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ), - rootTableGroup, - null, - creationState - ); + this.identifierResult = entityValuedModelPart.getForeignKeyDescriptor().createKeyDomainResult( + navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ), + rootTableGroup, + creationState + ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularBiDirectionalFetchImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularBiDirectionalFetchImpl.java index 5d9d8ff4b4..66ee61f193 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularBiDirectionalFetchImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularBiDirectionalFetchImpl.java @@ -150,6 +150,11 @@ public class CircularBiDirectionalFetchImpl implements BiDirectionalFetch, Assoc return ( (Association) fetchParent ).getForeignKeyDescriptor(); } + @Override + public ForeignKeyDescriptor.Side getSide() { + return ( (Association) fetchParent ).getSide(); + } + @Override public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) { fetchable.breakDownJdbcValues( domainValue, valueConsumer, session ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularFetchImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularFetchImpl.java index 6dca813a4d..5801688428 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularFetchImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularFetchImpl.java @@ -10,14 +10,12 @@ import org.hibernate.LockMode; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.mapping.Association; -import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.metamodel.model.domain.NavigableRole; -import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.BiDirectionalFetch; @@ -181,6 +179,11 @@ public class CircularFetchImpl implements BiDirectionalFetch, Association { return ( (Association) fetchParent ).getForeignKeyDescriptor(); } + @Override + public ForeignKeyDescriptor.Side getSide() { + return ( (Association) fetchParent ).getSide(); + } + @Override public void breakDownJdbcValues(Object domainValue, JdbcValueConsumer valueConsumer, SharedSessionContractImplementor session) { fetchable.breakDownJdbcValues( domainValue, valueConsumer, session );