Introduce special part name for FK target part to avoid issues with composite FK initializers

This commit is contained in:
Christian Beikov 2022-03-10 10:49:07 +01:00
parent 0ca38c8c87
commit c548a79f0b
7 changed files with 93 additions and 98 deletions

View File

@ -44,6 +44,7 @@ public interface ForeignKeyDescriptor extends VirtualModelPart, ValueMapping {
} }
String PART_NAME = "{fk}"; String PART_NAME = "{fk}";
String TARGET_PART_NAME = "{fk-target}";
String getKeyTable(); String getKeyTable();

View File

@ -306,26 +306,12 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
String columnContainingTable, String columnContainingTable,
EmbeddableValuedModelPart modelPart, EmbeddableValuedModelPart modelPart,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
final NavigablePath fkNavigablePath = navigablePath.append( getPartName() );
final NavigablePath resultNavigablePath; final NavigablePath resultNavigablePath;
if ( associationKey.getTable().equals( columnContainingTable ) ) { if ( modelPart == keySide.getModelPart() ) {
final ModelPartContainer parentModelPart = tableGroup.getModelPart(); resultNavigablePath = navigablePath.append( ForeignKeyDescriptor.PART_NAME );
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() );
}
} }
else { else {
resultNavigablePath = navigablePath.append( getPartName() ); resultNavigablePath = navigablePath.append( ForeignKeyDescriptor.TARGET_PART_NAME );
} }
final TableGroup fkTableGroup = creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup( final TableGroup fkTableGroup = creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup(
resultNavigablePath, resultNavigablePath,
@ -343,12 +329,6 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
return tableGroupJoin.getJoinedGroup(); return tableGroupJoin.getJoinedGroup();
} }
); );
if ( fkNavigablePath != resultNavigablePath ) {
creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup(
fkNavigablePath,
np -> fkTableGroup
);
}
final Nature currentForeignKeyResolvingKey = creationState.getCurrentlyResolvingForeignKeyPart(); final Nature currentForeignKeyResolvingKey = creationState.getCurrentlyResolvingForeignKeyPart();
try { try {

View File

@ -232,10 +232,17 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState(); final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver(); 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; final TableReference tableReference;
try { try {
tableReference = tableGroup.resolveTableReference( tableReference = tableGroup.resolveTableReference(
navigablePath.append( getTargetPart().getFetchableName() ), resultNavigablePath,
selectableMapping.getContainingTableExpression() selectableMapping.getContainingTableExpression()
); );
} }

View File

@ -748,7 +748,8 @@ public class ToOneAttributeMapping
private Key key; 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? // 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; return null;
} }
@ -1363,80 +1364,80 @@ public class ToOneAttributeMapping
// This is vital for the map key property check that comes next // This is vital for the map key property check that comes next
assert !( lhs instanceof PluralTableGroup ); 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 ); 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 // 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 // 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() ) if ( !addsPredicate && ( joinType == SqlAstJoinType.INNER || joinType == SqlAstJoinType.LEFT ) ) {
&& !addsPredicate && ( joinType == SqlAstJoinType.INNER || joinType == SqlAstJoinType.LEFT ) ) { TableGroup parentTableGroup = lhs;
final PluralTableGroup pluralTableGroup = (PluralTableGroup) fromClauseAccess.findTableGroup( ModelPartContainer parentContainer = lhs.getModelPart();
parentTableGroup.getNavigablePath().getParent() StringBuilder embeddablePathSb = null;
); // Traverse up embeddable table groups until we find a table group for a collection part
final String indexPropertyName = pluralTableGroup.getModelPart() while ( !( parentContainer instanceof CollectionPart ) ) {
.getIndexMetadata() if ( parentContainer instanceof EmbeddableValuedModelPart ) {
.getIndexPropertyName(); if ( embeddablePathSb == null ) {
final String pathName; embeddablePathSb = new StringBuilder();
if ( embeddablePathSb != null ) { }
pathName = embeddablePathSb.append( getAttributeName() ).toString(); embeddablePathSb.insert( 0, parentContainer.getPartName() + "." );
parentTableGroup = fromClauseAccess.findTableGroup( parentTableGroup.getNavigablePath().getParent() );
parentContainer = parentTableGroup.getModelPart();
}
else {
break;
}
} }
else { if ( CollectionPart.Nature.ELEMENT.getName().equals( parentTableGroup.getNavigablePath().getUnaliasedLocalName() ) ) {
pathName = getAttributeName(); final PluralTableGroup pluralTableGroup = (PluralTableGroup) fromClauseAccess.findTableGroup(
} parentTableGroup.getNavigablePath().getParent()
if ( pathName.equals( indexPropertyName ) ) { );
final TableGroup indexTableGroup = pluralTableGroup.getIndexTableGroup(); final String indexPropertyName = pluralTableGroup.getModelPart()
// If this is the map key property, we can reuse the index table group .getIndexMetadata()
initializeIfNeeded( lhs, requestedJoinType, indexTableGroup ); .getIndexPropertyName();
return new TableGroupJoin( final String pathName;
navigablePath, if ( embeddablePathSb != null ) {
joinType, pathName = embeddablePathSb.append( getAttributeName() ).toString();
new MappedByTableGroup( }
navigablePath, else {
this, pathName = getAttributeName();
indexTableGroup, }
fetched, if ( pathName.equals( indexPropertyName ) ) {
pluralTableGroup, final TableGroup indexTableGroup = pluralTableGroup.getIndexTableGroup();
(np, tableExpression) -> { // If this is the map key property, we can reuse the index table group
if ( !canUseParentTableGroup ) { initializeIfNeeded( lhs, requestedJoinType, indexTableGroup );
return false; return new TableGroupJoin(
} navigablePath,
NavigablePath path = np.getParent(); joinType,
// Fast path new MappedByTableGroup(
if ( navigablePath.equals( path ) ) { navigablePath,
return targetKeyPropertyNames.contains( np.getUnaliasedLocalName() ) 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 ); && identifyingColumnsTableExpression.equals( tableExpression );
} }
final StringBuilder sb = new StringBuilder( np.getFullPath().length() ); ),
sb.append( np.getUnaliasedLocalName() ); null
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
);
} }
} }

View File

@ -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 // We never want to create empty composites for the FK target or PK, otherwise collections would break
createEmptyCompositesEnabled = !ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getLocalName() ) createEmptyCompositesEnabled = !ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getUnaliasedLocalName() )
&& !EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( navigablePath.getLocalName() ) && !ForeignKeyDescriptor.TARGET_PART_NAME.equals( navigablePath.getUnaliasedLocalName() )
&& !EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( navigablePath.getUnaliasedLocalName() )
&& embeddableTypeDescriptor.isCreateEmptyCompositesEnabled(); && embeddableTypeDescriptor.isCreateEmptyCompositesEnabled();
sessionFactory = creationState.getSqlAstCreationContext().getSessionFactory(); 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. // so we can't use the fetch parent access in that case.
if ( fetchParentAccess != null && embedded instanceof VirtualModelPart if ( fetchParentAccess != null && embedded instanceof VirtualModelPart
&& !EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( embedded.getFetchableName() ) && !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 ); fetchParentAccess.resolveInstance( processingState );
compositeInstance = fetchParentAccess.getInitializedInstance(); compositeInstance = fetchParentAccess.getInitializedInstance();
} }
@ -253,7 +255,8 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA
private void extractRowState(RowProcessingState processingState) { private void extractRowState(RowProcessingState processingState) {
stateAllNull = true; 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() ); || EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( embedded.getFetchableName() );
for ( int i = 0; i < assemblers.size(); i++ ) { for ( int i = 0; i < assemblers.size(); i++ ) {
final DomainResultAssembler<?> assembler = assemblers.get( i ); final DomainResultAssembler<?> assembler = assemblers.get( i );

View File

@ -66,7 +66,8 @@ public class EmbeddableForeignKeyResultImpl<T>
final ToOneAttributeMapping toOne = (ToOneAttributeMapping) fetchable; final ToOneAttributeMapping toOne = (ToOneAttributeMapping) fetchable;
shouldSelect = selected && !creationState.isAssociationKeyVisited( shouldSelect = selected && !creationState.isAssociationKeyVisited(
toOne.getForeignKeyDescriptor().getAssociationKey() toOne.getForeignKeyDescriptor().getAssociationKey()
) && !ForeignKeyDescriptor.PART_NAME.equals( getNavigablePath().getLocalName() ); ) && !ForeignKeyDescriptor.PART_NAME.equals( getNavigablePath().getLocalName() )
&& !ForeignKeyDescriptor.TARGET_PART_NAME.equals( getNavigablePath().getLocalName() );
} }
else { else {
shouldSelect = selected; shouldSelect = selected;

View File

@ -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 // We only need to select in this phase if this is part of an identifier or foreign key
NavigablePath np = navigablePath.getParent(); NavigablePath np = navigablePath.getParent();
while ( np != null ) { 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 ); initializeInstance( rowProcessingState );
return; return;
} }