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 7a8fde1362..33a7264d3f 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 @@ -44,6 +44,7 @@ public interface ForeignKeyDescriptor extends VirtualModelPart, ValueMapping { } String PART_NAME = "{fk}"; + String TARGET_PART_NAME = "{fk-target}"; String getKeyTable(); 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 81ced6ba38..799553d97c 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 @@ -306,26 +306,12 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { String columnContainingTable, EmbeddableValuedModelPart modelPart, DomainResultCreationState creationState) { - final NavigablePath fkNavigablePath = navigablePath.append( getPartName() ); final NavigablePath resultNavigablePath; - if ( associationKey.getTable().equals( columnContainingTable ) ) { - final ModelPartContainer parentModelPart = tableGroup.getModelPart(); - if ( parentModelPart instanceof PluralAttributeMapping ) { - if ( ( (PluralAttributeMapping) parentModelPart ).getIndexDescriptor() == null ) { - resultNavigablePath = navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ) - .append( getPartName() ); - } - else { - resultNavigablePath = navigablePath.append( CollectionPart.Nature.INDEX.getName() ) - .append( getPartName() ); - } - } - else { - resultNavigablePath = navigablePath.append( getPartName() ); - } + if ( modelPart == keySide.getModelPart() ) { + resultNavigablePath = navigablePath.append( ForeignKeyDescriptor.PART_NAME ); } else { - resultNavigablePath = navigablePath.append( getPartName() ); + resultNavigablePath = navigablePath.append( ForeignKeyDescriptor.TARGET_PART_NAME ); } final TableGroup fkTableGroup = creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup( resultNavigablePath, @@ -343,12 +329,6 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { return tableGroupJoin.getJoinedGroup(); } ); - if ( fkNavigablePath != resultNavigablePath ) { - creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup( - fkNavigablePath, - np -> fkTableGroup - ); - } final Nature currentForeignKeyResolvingKey = creationState.getCurrentlyResolvingForeignKeyPart(); try { 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 9d1936c27f..3866ac40b8 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 @@ -232,10 +232,17 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState(); final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver(); + final NavigablePath resultNavigablePath; + if ( selectableMapping == keySide.getModelPart() ) { + resultNavigablePath = navigablePath.append( ForeignKeyDescriptor.PART_NAME ); + } + else { + resultNavigablePath = navigablePath.append( ForeignKeyDescriptor.TARGET_PART_NAME ); + } final TableReference tableReference; try { tableReference = tableGroup.resolveTableReference( - navigablePath.append( getTargetPart().getFetchableName() ), + resultNavigablePath, selectableMapping.getContainingTableExpression() ); } 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 517cfe9ad2..cfb4923f15 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 @@ -748,7 +748,8 @@ public class ToOneAttributeMapping private Key key; } */ - if ( parentNavigablePath.getLocalName().equals( ForeignKeyDescriptor.PART_NAME ) ) { + if ( parentNavigablePath.getUnaliasedLocalName().equals( ForeignKeyDescriptor.PART_NAME ) + || parentNavigablePath.getUnaliasedLocalName().equals( ForeignKeyDescriptor.TARGET_PART_NAME ) ) { // todo (6.0): maybe it's better to have a flag in creation state that marks if we are building a circular fetch domain result already to skip this? return null; } @@ -1363,80 +1364,80 @@ public class ToOneAttributeMapping // This is vital for the map key property check that comes next assert !( lhs instanceof PluralTableGroup ); - TableGroup parentTableGroup = lhs; - ModelPartContainer parentContainer = lhs.getModelPart(); - StringBuilder embeddablePathSb = null; - // Traverse up embeddable table groups until we find a table group for a collection part - while ( !( parentContainer instanceof CollectionPart ) ) { - if ( parentContainer instanceof EmbeddableValuedModelPart ) { - if ( embeddablePathSb == null ) { - embeddablePathSb = new StringBuilder(); - } - embeddablePathSb.insert( 0, parentContainer.getPartName() + "." ); - parentTableGroup = fromClauseAccess.findTableGroup( parentTableGroup.getNavigablePath().getParent() ); - parentContainer = parentTableGroup.getModelPart(); - } - else { - break; - } - } - final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER ); // If a parent is a collection part, there is no custom predicate and the join is INNER or LEFT // we check if this attribute is the map key property to reuse the existing index table group - if ( CollectionPart.Nature.ELEMENT.getName().equals( parentTableGroup.getNavigablePath().getUnaliasedLocalName() ) - && !addsPredicate && ( joinType == SqlAstJoinType.INNER || joinType == SqlAstJoinType.LEFT ) ) { - final PluralTableGroup pluralTableGroup = (PluralTableGroup) fromClauseAccess.findTableGroup( - parentTableGroup.getNavigablePath().getParent() - ); - final String indexPropertyName = pluralTableGroup.getModelPart() - .getIndexMetadata() - .getIndexPropertyName(); - final String pathName; - if ( embeddablePathSb != null ) { - pathName = embeddablePathSb.append( getAttributeName() ).toString(); + if ( !addsPredicate && ( joinType == SqlAstJoinType.INNER || joinType == SqlAstJoinType.LEFT ) ) { + TableGroup parentTableGroup = lhs; + ModelPartContainer parentContainer = lhs.getModelPart(); + StringBuilder embeddablePathSb = null; + // Traverse up embeddable table groups until we find a table group for a collection part + while ( !( parentContainer instanceof CollectionPart ) ) { + if ( parentContainer instanceof EmbeddableValuedModelPart ) { + if ( embeddablePathSb == null ) { + embeddablePathSb = new StringBuilder(); + } + embeddablePathSb.insert( 0, parentContainer.getPartName() + "." ); + parentTableGroup = fromClauseAccess.findTableGroup( parentTableGroup.getNavigablePath().getParent() ); + parentContainer = parentTableGroup.getModelPart(); + } + else { + break; + } } - else { - pathName = getAttributeName(); - } - if ( pathName.equals( indexPropertyName ) ) { - final TableGroup indexTableGroup = pluralTableGroup.getIndexTableGroup(); - // If this is the map key property, we can reuse the index table group - initializeIfNeeded( lhs, requestedJoinType, indexTableGroup ); - return new TableGroupJoin( - navigablePath, - joinType, - new MappedByTableGroup( - navigablePath, - this, - indexTableGroup, - fetched, - pluralTableGroup, - (np, tableExpression) -> { - if ( !canUseParentTableGroup ) { - return false; - } - NavigablePath path = np.getParent(); - // Fast path - if ( navigablePath.equals( path ) ) { - return targetKeyPropertyNames.contains( np.getUnaliasedLocalName() ) + if ( CollectionPart.Nature.ELEMENT.getName().equals( parentTableGroup.getNavigablePath().getUnaliasedLocalName() ) ) { + final PluralTableGroup pluralTableGroup = (PluralTableGroup) fromClauseAccess.findTableGroup( + parentTableGroup.getNavigablePath().getParent() + ); + final String indexPropertyName = pluralTableGroup.getModelPart() + .getIndexMetadata() + .getIndexPropertyName(); + final String pathName; + if ( embeddablePathSb != null ) { + pathName = embeddablePathSb.append( getAttributeName() ).toString(); + } + else { + pathName = getAttributeName(); + } + if ( pathName.equals( indexPropertyName ) ) { + final TableGroup indexTableGroup = pluralTableGroup.getIndexTableGroup(); + // If this is the map key property, we can reuse the index table group + initializeIfNeeded( lhs, requestedJoinType, indexTableGroup ); + return new TableGroupJoin( + navigablePath, + joinType, + new MappedByTableGroup( + navigablePath, + this, + indexTableGroup, + fetched, + pluralTableGroup, + (np, tableExpression) -> { + if ( !canUseParentTableGroup ) { + return false; + } + NavigablePath path = np.getParent(); + // Fast path + if ( navigablePath.equals( path ) ) { + return targetKeyPropertyNames.contains( np.getUnaliasedLocalName() ) + && identifyingColumnsTableExpression.equals( tableExpression ); + } + final StringBuilder sb = new StringBuilder( np.getFullPath().length() ); + sb.append( np.getUnaliasedLocalName() ); + while ( path != null && !navigablePath.equals( path ) ) { + sb.insert( 0, '.' ); + sb.insert( 0, path.getUnaliasedLocalName() ); + path = path.getParent(); + } + return navigablePath.equals( path ) + && targetKeyPropertyNames.contains( sb.toString() ) && identifyingColumnsTableExpression.equals( tableExpression ); } - final StringBuilder sb = new StringBuilder( np.getFullPath().length() ); - sb.append( np.getUnaliasedLocalName() ); - while ( path != null && !navigablePath.equals( path ) ) { - sb.insert( 0, '.' ); - sb.insert( 0, path.getUnaliasedLocalName() ); - path = path.getParent(); - } - return navigablePath.equals( path ) - && targetKeyPropertyNames.contains( sb.toString() ) - && identifyingColumnsTableExpression.equals( tableExpression ); - } - ), - null - ); + ), + null + ); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java index 4f81562e54..fb8c13fc8c 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java @@ -108,8 +108,9 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA ); // We never want to create empty composites for the FK target or PK, otherwise collections would break - createEmptyCompositesEnabled = !ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getLocalName() ) - && !EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( navigablePath.getLocalName() ) + createEmptyCompositesEnabled = !ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getUnaliasedLocalName() ) + && !ForeignKeyDescriptor.TARGET_PART_NAME.equals( navigablePath.getUnaliasedLocalName() ) + && !EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( navigablePath.getUnaliasedLocalName() ) && embeddableTypeDescriptor.isCreateEmptyCompositesEnabled(); sessionFactory = creationState.getSqlAstCreationContext().getSessionFactory(); @@ -231,7 +232,8 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA // so we can't use the fetch parent access in that case. if ( fetchParentAccess != null && embedded instanceof VirtualModelPart && !EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( embedded.getFetchableName() ) - && !ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getUnaliasedLocalName() ) ) { + && !ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getUnaliasedLocalName() ) + && !ForeignKeyDescriptor.TARGET_PART_NAME.equals( navigablePath.getUnaliasedLocalName() ) ) { fetchParentAccess.resolveInstance( processingState ); compositeInstance = fetchParentAccess.getInitializedInstance(); } @@ -253,7 +255,8 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA private void extractRowState(RowProcessingState processingState) { stateAllNull = true; - final boolean isKey = ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getLocalName() ) + final boolean isKey = ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getUnaliasedLocalName() ) + || ForeignKeyDescriptor.TARGET_PART_NAME.equals( navigablePath.getUnaliasedLocalName() ) || EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( embedded.getFetchableName() ); for ( int i = 0; i < assemblers.size(); i++ ) { final DomainResultAssembler assembler = assemblers.get( i ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableForeignKeyResultImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableForeignKeyResultImpl.java index 6b14551a36..d9a7df393d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableForeignKeyResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableForeignKeyResultImpl.java @@ -66,7 +66,8 @@ public class EmbeddableForeignKeyResultImpl final ToOneAttributeMapping toOne = (ToOneAttributeMapping) fetchable; shouldSelect = selected && !creationState.isAssociationKeyVisited( toOne.getForeignKeyDescriptor().getAssociationKey() - ) && !ForeignKeyDescriptor.PART_NAME.equals( getNavigablePath().getLocalName() ); + ) && !ForeignKeyDescriptor.PART_NAME.equals( getNavigablePath().getLocalName() ) + && !ForeignKeyDescriptor.TARGET_PART_NAME.equals( getNavigablePath().getLocalName() ); } else { shouldSelect = selected; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java index 7dcd879621..cb18e1e25e 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java @@ -87,7 +87,9 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl // We only need to select in this phase if this is part of an identifier or foreign key NavigablePath np = navigablePath.getParent(); while ( np != null ) { - if ( np instanceof EntityIdentifierNavigablePath || ForeignKeyDescriptor.PART_NAME.equals( np.getUnaliasedLocalName() ) ) { + if ( np instanceof EntityIdentifierNavigablePath + || ForeignKeyDescriptor.PART_NAME.equals( np.getUnaliasedLocalName() ) + || ForeignKeyDescriptor.TARGET_PART_NAME.equals( np.getUnaliasedLocalName() )) { initializeInstance( rowProcessingState ); return; }