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 TARGET_PART_NAME = "{fk-target}";
String getKeyTable();

View File

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

View File

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

View File

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

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

View File

@ -66,7 +66,8 @@ public class EmbeddableForeignKeyResultImpl<T>
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;

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