Fix bidirectional fetching issues

This commit is contained in:
Christian Beikov 2021-09-22 06:56:48 +02:00
parent 905227d2ed
commit e8d337828b
10 changed files with 149 additions and 139 deletions

View File

@ -729,7 +729,7 @@ public class LoaderSelectBuilder {
FetchTiming fetchTiming = fetchable.getMappedFetchOptions().getTiming(); FetchTiming fetchTiming = fetchable.getMappedFetchOptions().getTiming();
boolean joined = fetchable.getMappedFetchOptions().getStyle() == FetchStyle.JOIN; boolean joined = fetchable.getMappedFetchOptions().getStyle() == FetchStyle.JOIN;
boolean explicitFetch = false;
EntityGraphTraversalState.TraversalResult traversalResult = null; EntityGraphTraversalState.TraversalResult traversalResult = null;
if ( !( fetchable instanceof CollectionPart ) ) { if ( !( fetchable instanceof CollectionPart ) ) {
@ -738,6 +738,7 @@ public class LoaderSelectBuilder {
traversalResult = entityGraphTraversalState.traverse( fetchParent, fetchable, isKeyFetchable ); traversalResult = entityGraphTraversalState.traverse( fetchParent, fetchable, isKeyFetchable );
fetchTiming = traversalResult.getFetchTiming(); fetchTiming = traversalResult.getFetchTiming();
joined = traversalResult.isJoined(); joined = traversalResult.isJoined();
explicitFetch = true;
} }
else if ( loadQueryInfluencers.hasEnabledFetchProfiles() ) { else if ( loadQueryInfluencers.hasEnabledFetchProfiles() ) {
// There is no point in checking the fetch profile if it can't affect this fetchable // There is no point in checking the fetch profile if it can't affect this fetchable
@ -753,6 +754,7 @@ public class LoaderSelectBuilder {
if ( profileFetch != null ) { if ( profileFetch != null ) {
fetchTiming = FetchTiming.IMMEDIATE; fetchTiming = FetchTiming.IMMEDIATE;
joined = joined || profileFetch.getStyle() == org.hibernate.engine.profile.Fetch.Style.JOIN; joined = joined || profileFetch.getStyle() == org.hibernate.engine.profile.Fetch.Style.JOIN;
explicitFetch = true;
} }
} }
} }
@ -796,7 +798,8 @@ public class LoaderSelectBuilder {
fetchDepth++; fetchDepth++;
} }
if ( !creationState.isResolvingCircularFetch() ) { // There is no need to check for circular fetches if this is an explicit fetch
if ( !explicitFetch && !creationState.isResolvingCircularFetch() ) {
final Fetch biDirectionalFetch = fetchable.resolveCircularFetch( final Fetch biDirectionalFetch = fetchable.resolveCircularFetch(
fetchablePath, fetchablePath,
fetchParent, fetchParent,

View File

@ -13,7 +13,6 @@ import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchOptions; import org.hibernate.sql.results.graph.FetchOptions;
import org.hibernate.sql.results.graph.FetchParent; import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.basic.BasicFetch; import org.hibernate.sql.results.graph.basic.BasicFetch;
@ -77,13 +76,4 @@ public interface EntityDiscriminatorMapping extends VirtualModelPart, BasicValue
return FetchTiming.IMMEDIATE; return FetchTiming.IMMEDIATE;
} }
@Override
default Fetch resolveCircularFetch(
NavigablePath fetchablePath,
FetchParent fetchParent,
FetchTiming fetchTiming,
DomainResultCreationState creationState) {
// can never be circular
return null;
}
} }

View File

@ -9,6 +9,7 @@ package org.hibernate.metamodel.mapping;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.hibernate.loader.ast.spi.Loadable; import org.hibernate.loader.ast.spi.Loadable;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.metamodel.mapping.ordering.OrderByFragment; import org.hibernate.metamodel.mapping.ordering.OrderByFragment;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
@ -60,7 +61,7 @@ public interface PluralAttributeMapping
String getSeparateCollectionTable(); String getSeparateCollectionTable();
boolean isBidirectionalAttributeName(NavigablePath fetchablePath); boolean isBidirectionalAttributeName(NavigablePath fetchablePath, ToOneAttributeMapping modelPart);
@Override @Override
default boolean incrementFetchDepth(){ default boolean incrementFetchDepth(){

View File

@ -196,16 +196,6 @@ public class EmbeddedAttributeMapping
return navigableRole; return navigableRole;
} }
@Override
public Fetch resolveCircularFetch(
NavigablePath fetchablePath,
FetchParent fetchParent,
FetchTiming fetchTiming,
DomainResultCreationState creationState) {
// an embeddable can never be circular
return null;
}
@Override @Override
public Fetch generateFetch( public Fetch generateFetch(
FetchParent fetchParent, FetchParent fetchParent,

View File

@ -134,15 +134,6 @@ public class EntityCollectionPart
return this; return this;
} }
@Override
public Fetch resolveCircularFetch(
NavigablePath fetchablePath,
FetchParent fetchParent,
FetchTiming fetchTiming,
DomainResultCreationState creationState) {
return null;
}
@Override @Override
public EntityFetch generateFetch( public EntityFetch generateFetch(
FetchParent fetchParent, FetchParent fetchParent,

View File

@ -104,7 +104,6 @@ public class PluralAttributeMappingImpl
private final FetchStyle fetchStyle; private final FetchStyle fetchStyle;
private final String bidirectionalAttributeName; private final String bidirectionalAttributeName;
private final Boolean isInverse;
private final CollectionPersister collectionDescriptor; private final CollectionPersister collectionDescriptor;
private final String separateCollectionTable; private final String separateCollectionTable;
@ -184,8 +183,6 @@ public class PluralAttributeMappingImpl
this.bidirectionalAttributeName = StringHelper.subStringNullIfEmpty( bootDescriptor.getMappedByProperty(), '.'); this.bidirectionalAttributeName = StringHelper.subStringNullIfEmpty( bootDescriptor.getMappedByProperty(), '.');
this.isInverse = bootDescriptor.isInverse();
this.sqlAliasStem = SqlAliasStemHelper.INSTANCE.generateStemFromAttributeName( attributeName ); this.sqlAliasStem = SqlAliasStemHelper.INSTANCE.generateStemFromAttributeName( attributeName );
if ( bootDescriptor.isOneToMany() ) { if ( bootDescriptor.isOneToMany() ) {
@ -232,14 +229,13 @@ public class PluralAttributeMappingImpl
} }
@Override @Override
public boolean isBidirectionalAttributeName(NavigablePath fetchablePath) { public boolean isBidirectionalAttributeName(NavigablePath fetchablePath, ToOneAttributeMapping modelPart) {
if ( isInverse ) {
return true;
}
if ( bidirectionalAttributeName == null ) { if ( bidirectionalAttributeName == null ) {
return false; // If the FK-target of the to-one mapping is the same as the FK-target of this plural mapping,
// then we say this is bidirectional, given that this is only invoked for model parts of the collection elements
return fkDescriptor.getTargetPart() == modelPart.getForeignKeyDescriptor().getTargetPart();
} }
return fetchablePath.getFullPath().endsWith( bidirectionalAttributeName ); return fetchablePath.getUnaliasedLocalName().endsWith( bidirectionalAttributeName );
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")

View File

@ -24,7 +24,6 @@ import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.ToOne; import org.hibernate.mapping.ToOne;
import org.hibernate.metamodel.mapping.AssociationKey; import org.hibernate.metamodel.mapping.AssociationKey;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.EntityAssociationMapping; import org.hibernate.metamodel.mapping.EntityAssociationMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
@ -474,34 +473,21 @@ public class ToOneAttributeMapping
return null; return null;
} }
ModelPart modelPart = creationState.resolveModelPart( parentNavigablePath ); ModelPart parentModelPart = creationState.resolveModelPart( parentNavigablePath );
if ( modelPart instanceof EmbeddedIdentifierMappingImpl ) { if ( parentModelPart instanceof EmbeddedIdentifierMappingImpl ) {
while ( parentNavigablePath instanceof EntityIdentifierNavigablePath ) { while ( parentNavigablePath instanceof EntityIdentifierNavigablePath ) {
parentNavigablePath = parentNavigablePath.getParent(); parentNavigablePath = parentNavigablePath.getParent();
assert parentNavigablePath != null;
parentModelPart = creationState.resolveModelPart( parentNavigablePath );
} }
} }
while ( modelPart instanceof EmbeddableValuedFetchable ) { while ( parentModelPart instanceof EmbeddableValuedFetchable ) {
parentNavigablePath = parentNavigablePath.getParent(); parentNavigablePath = parentNavigablePath.getParent();
assert parentNavigablePath != null; assert parentNavigablePath != null;
modelPart = creationState.resolveModelPart( parentNavigablePath ); parentModelPart = creationState.resolveModelPart( parentNavigablePath );
} }
if ( isBidirectionalAttributeName( parentNavigablePath ) ) { if ( isBidirectionalAttributeName( parentNavigablePath, parentModelPart, fetchablePath, creationState ) ) {
/*
class Child {
@OneToOne(mappedBy = "biologicalChild")
private Mother mother;
}
class Mother {
@OneToOne
private Child biologicalChild;
}
fetchablePath= Mother.biologicalChild.mother
this.mappedBy = "biologicalChild"
parent.getFullPath() = "Mother.biologicalChild"
*/
return createCircularBiDirectionalFetch( return createCircularBiDirectionalFetch(
fetchablePath, fetchablePath,
fetchParent, fetchParent,
@ -510,39 +496,6 @@ public class ToOneAttributeMapping
); );
} }
/*
check if mappedBy is on the other side of the association
*/
final boolean isBiDirectional = isBidirectional(
modelPart,
parentNavigablePath.getParent(),
fetchablePath,
creationState
);
if ( isBiDirectional ) {
/*
class Child {
@OneToOne(mappedBy = "biologicalChild")
private Mother mother;
}
class Mother {
@OneToOne
private Child biologicalChild;
}
fetchablePath = "Child.mother.biologicalChild"
otherSideAssociationModelPart = ToOneAttributeMapping("Child.mother")
otherSideMappedBy = "biologicalChild"
*/
return createCircularBiDirectionalFetch(
fetchablePath,
fetchParent,
parentNavigablePath,
LockMode.READ
);
}
/* /*
class Child { class Child {
@OneToOne @OneToOne
@ -554,7 +507,7 @@ public class ToOneAttributeMapping
private Child stepMother; private Child stepMother;
} }
We have a cirularity but it is not bidirectional We have a circularity but it is not bidirectional
*/ */
if ( sideNature == ForeignKeyDescriptor.Nature.KEY ) { if ( sideNature == ForeignKeyDescriptor.Nature.KEY ) {
final TableGroup parentTableGroup = creationState final TableGroup parentTableGroup = creationState
@ -581,6 +534,7 @@ public class ToOneAttributeMapping
fetchablePath, fetchablePath,
fetchParent, fetchParent,
this, this,
isSelectByUniqueKey( sideNature ),
fetchablePath, fetchablePath,
foreignKeyDomainResult foreignKeyDomainResult
); );
@ -589,35 +543,95 @@ public class ToOneAttributeMapping
return null; return null;
} }
private boolean isBidirectional( protected boolean isBidirectionalAttributeName(
ModelPart modelPart, NavigablePath parentNavigablePath,
NavigablePath parentOfParent, ModelPart parentModelPart,
NavigablePath fetchablePath, NavigablePath fetchablePath,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
if ( modelPart instanceof ToOneAttributeMapping ) { if ( bidirectionalAttributeName == null ) {
return ( (ToOneAttributeMapping) modelPart ).isBidirectionalAttributeName( fetchablePath ); /*
check if mappedBy is on the other side of the association
*/
/*
class Child {
@OneToOne(mappedBy = "biologicalChild")
private Mother mother;
} }
if ( modelPart instanceof PluralAttributeMapping ) { class Mother {
return ( (PluralAttributeMapping) modelPart ).isBidirectionalAttributeName( fetchablePath ); @OneToOne
private Child biologicalChild;
} }
if ( modelPart instanceof EntityCollectionPart ) { fetchablePath = "Child.mother.biologicalChild"
otherSideAssociationModelPart = ToOneAttributeMapping("Child.mother")
otherSideMappedBy = "biologicalChild"
*/
if ( parentModelPart instanceof ToOneAttributeMapping ) {
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) parentModelPart;
if ( toOneAttributeMapping.bidirectionalAttributeName != null ) {
return toOneAttributeMapping.isBidirectionalAttributeName(
fetchablePath,
this,
parentNavigablePath,
creationState
);
}
}
else if ( parentModelPart instanceof PluralAttributeMapping ) {
return ( (PluralAttributeMapping) parentModelPart ).isBidirectionalAttributeName( fetchablePath, this );
}
else if ( parentModelPart instanceof EntityCollectionPart ) {
NavigablePath parentOfParent = parentNavigablePath.getParent();
if ( parentOfParent instanceof EntityIdentifierNavigablePath ) { if ( parentOfParent instanceof EntityIdentifierNavigablePath ) {
parentOfParent = parentOfParent.getParent(); parentOfParent = parentOfParent.getParent();
} }
return ( (PluralAttributeMapping) creationState.resolveModelPart( parentOfParent ) ).isBidirectionalAttributeName( return ( (PluralAttributeMapping) creationState.resolveModelPart( parentOfParent ) )
fetchablePath ); .isBidirectionalAttributeName( fetchablePath, this );
} }
return false; return false;
} }
if ( cardinality == Cardinality.MANY_TO_ONE ) {
/*
class Child {
@OneToOne(mappedBy = "biologicalChild")
private Mother mother;
}
protected boolean isBidirectionalAttributeName(NavigablePath fetchablePath) { class Mother {
if ( bidirectionalAttributeName == null ) { @OneToOne
private Child biologicalChild;
}
fetchablePath= Mother.biologicalChild.mother
this.mappedBy = "biologicalChild"
parent.getFullPath() = "Mother.biologicalChild"
*/
final String fullPath = parentNavigablePath.getFullPath();
if ( fullPath.endsWith( bidirectionalAttributeName + "." + CollectionPart.Nature.ELEMENT.getName() ) ) {
final NavigablePath parentPath = parentNavigablePath.getParent().getParent();
// This can be null for a collection loader
if ( parentPath != null ) {
// If the parent is null, this is a simple collection fetch of a root, in which case the types must match
if ( parentPath.getParent() == null ) {
final String entityName = entityMappingType.getPartName();
return parentPath.getFullPath().startsWith( entityName ) && (
parentPath.getFullPath().length() == entityName.length()
// Ignore a possible alias
|| parentPath.getFullPath().charAt( entityName.length() ) == '('
);
}
// If we have a parent, we ensure that the parent is the same as the attribute name
else {
return parentPath.getUnaliasedLocalName().equals( navigableRole.getLocalName() );
}
}
}
return false; return false;
} }
return fetchablePath.getFullPath().endsWith( bidirectionalAttributeName ); return parentNavigablePath.getUnaliasedLocalName().equals( bidirectionalAttributeName );
} }
public String getBidirectionalAttributeName(){ public String getBidirectionalAttributeName(){
@ -744,16 +758,7 @@ public class ToOneAttributeMapping
side, side,
creationState creationState
); );
boolean selectByUniqueKey; final boolean selectByUniqueKey = isSelectByUniqueKey( side );
if ( side == ForeignKeyDescriptor.Nature.KEY ) {
// case 1.2
selectByUniqueKey = !getKeyTargetMatchPart().getNavigableRole()
.equals( entityMappingType.getIdentifierMapping().getNavigableRole() );
}
else {
// case 1.1
selectByUniqueKey = bidirectionalAttributeName != null;
}
if ( fetchTiming == FetchTiming.IMMEDIATE ) { if ( fetchTiming == FetchTiming.IMMEDIATE ) {
return new EntityFetchSelectImpl( return new EntityFetchSelectImpl(
@ -774,6 +779,18 @@ public class ToOneAttributeMapping
); );
} }
private boolean isSelectByUniqueKey(ForeignKeyDescriptor.Nature side) {
if ( side == ForeignKeyDescriptor.Nature.KEY ) {
// case 1.2
return !getKeyTargetMatchPart().getNavigableRole()
.equals( entityMappingType.getIdentifierMapping().getNavigableRole() );
}
else {
// case 1.1
return bidirectionalAttributeName != null;
}
}
@Override @Override
public <T> DomainResult<T> createDelayedDomainResult( public <T> DomainResult<T> createDelayedDomainResult(
NavigablePath navigablePath, NavigablePath navigablePath,

View File

@ -5134,7 +5134,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
EntityGraphTraversalState.TraversalResult traversalResult = null; EntityGraphTraversalState.TraversalResult traversalResult = null;
final FromClauseIndex fromClauseIndex = getFromClauseIndex(); final FromClauseIndex fromClauseIndex = getFromClauseIndex();
final SqmAttributeJoin fetchedJoin = fromClauseIndex.findFetchedJoinByPath( fetchablePath ); final SqmAttributeJoin<?, ?> fetchedJoin = fromClauseIndex.findFetchedJoinByPath( fetchablePath );
boolean explicitFetch = false;
if ( fetchedJoin != null ) { if ( fetchedJoin != null ) {
// there was an explicit fetch in the SQM // there was an explicit fetch in the SQM
@ -5148,6 +5149,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
} }
joined = true; joined = true;
alias = fetchedJoin.getExplicitAlias(); alias = fetchedJoin.getExplicitAlias();
explicitFetch = true;
} }
else { else {
// there was not an explicit fetch in the SQM // there was not an explicit fetch in the SQM
@ -5158,6 +5160,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
traversalResult = entityGraphTraversalState.traverse( fetchParent, fetchable, isKeyFetchable ); traversalResult = entityGraphTraversalState.traverse( fetchParent, fetchable, isKeyFetchable );
fetchTiming = traversalResult.getFetchTiming(); fetchTiming = traversalResult.getFetchTiming();
joined = traversalResult.isJoined(); joined = traversalResult.isJoined();
explicitFetch = true;
} }
else if ( getLoadQueryInfluencers().hasEnabledFetchProfiles() ) { else if ( getLoadQueryInfluencers().hasEnabledFetchProfiles() ) {
// There is no point in checking the fetch profile if it can't affect this fetchable // There is no point in checking the fetch profile if it can't affect this fetchable
@ -5173,6 +5176,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
if ( profileFetch != null ) { if ( profileFetch != null ) {
fetchTiming = FetchTiming.IMMEDIATE; fetchTiming = FetchTiming.IMMEDIATE;
joined = joined || profileFetch.getStyle() == org.hibernate.engine.profile.Fetch.Style.JOIN; joined = joined || profileFetch.getStyle() == org.hibernate.engine.profile.Fetch.Style.JOIN;
explicitFetch = true;
} }
} }
} }
@ -5224,8 +5228,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
if ( incrementFetchDepth ) { if ( incrementFetchDepth ) {
fetchDepth++; fetchDepth++;
} }
// There is no need to check for circular fetches if this is a fetch join // There is no need to check for circular fetches if this is an explicit fetch
if ( fetchedJoin == null && !isResolvingCircularFetch() ) { if ( !explicitFetch && !isResolvingCircularFetch() ) {
final Fetch biDirectionalFetch = fetchable.resolveCircularFetch( final Fetch biDirectionalFetch = fetchable.resolveCircularFetch(
fetchablePath, fetchablePath,
fetchParent, fetchParent,

View File

@ -192,11 +192,17 @@ public class CircularBiDirectionalFetchImpl implements BiDirectionalFetch, Assoc
public Object assemble(RowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) { public Object assemble(RowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) {
EntityInitializer initializer = resolveCircularInitializer( rowProcessingState ); EntityInitializer initializer = resolveCircularInitializer( rowProcessingState );
if ( initializer == null ) { if ( initializer == null ) {
final Initializer parentInitializer = rowProcessingState.resolveInitializer( circularPath );
if ( circularPath.getParent() != null ) { if ( circularPath.getParent() != null ) {
initializer = (EntityInitializer) rowProcessingState.resolveInitializer( circularPath.getParent() ); NavigablePath path = circularPath.getParent();
Initializer parentInitializer = rowProcessingState.resolveInitializer( path );
while ( !( parentInitializer instanceof EntityInitializer ) && path.getParent() != null ) {
path = path.getParent();
parentInitializer = rowProcessingState.resolveInitializer( path );
}
initializer = (EntityInitializer) parentInitializer;
} }
else { else {
final Initializer parentInitializer = rowProcessingState.resolveInitializer( circularPath );
assert parentInitializer instanceof CollectionInitializer; assert parentInitializer instanceof CollectionInitializer;
final CollectionInitializer circ = (CollectionInitializer) parentInitializer; final CollectionInitializer circ = (CollectionInitializer) parentInitializer;
final EntityPersister entityPersister = (EntityPersister) ( (AttributeMapping) fetchable ).getMappedType(); final EntityPersister entityPersister = (EntityPersister) ( (AttributeMapping) fetchable ).getMappedType();
@ -280,7 +286,6 @@ public class CircularBiDirectionalFetchImpl implements BiDirectionalFetch, Assoc
while ( !( parentInitializer instanceof EntityInitializer ) && path.getParent() != null ) { while ( !( parentInitializer instanceof EntityInitializer ) && path.getParent() != null ) {
path = path.getParent(); path = path.getParent();
parentInitializer = rowProcessingState.resolveInitializer( path ); parentInitializer = rowProcessingState.resolveInitializer( path );
} }
if ( !( parentInitializer instanceof EntityInitializer ) ) { if ( !( parentInitializer instanceof EntityInitializer ) ) {

View File

@ -28,6 +28,7 @@ import org.hibernate.sql.results.graph.FetchParentAccess;
import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.entity.EntityInitializer; import org.hibernate.sql.results.graph.entity.EntityInitializer;
import org.hibernate.sql.results.graph.entity.internal.EntityDelayedFetchInitializer; import org.hibernate.sql.results.graph.entity.internal.EntityDelayedFetchInitializer;
import org.hibernate.sql.results.graph.entity.internal.EntitySelectFetchByUniqueKeyInitializer;
import org.hibernate.sql.results.graph.entity.internal.EntitySelectFetchInitializer; import org.hibernate.sql.results.graph.entity.internal.EntitySelectFetchInitializer;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions; import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
import org.hibernate.sql.results.jdbc.spi.RowProcessingState; import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
@ -37,12 +38,13 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
* @author Andrea Boriero * @author Andrea Boriero
*/ */
public class CircularFetchImpl implements BiDirectionalFetch, Association { public class CircularFetchImpl implements BiDirectionalFetch, Association {
private DomainResult keyResult; private final DomainResult<?> keyResult;
private EntityValuedModelPart referencedModelPart; private final EntityValuedModelPart referencedModelPart;
private final EntityMappingType entityMappingType; private final EntityMappingType entityMappingType;
private final FetchTiming timing; private final FetchTiming timing;
private final NavigablePath navigablePath; private final NavigablePath navigablePath;
private final ToOneAttributeMapping fetchable; private final ToOneAttributeMapping fetchable;
private final boolean selectByUniqueKey;
private final FetchParent fetchParent; private final FetchParent fetchParent;
private final NavigablePath referencedNavigablePath; private final NavigablePath referencedNavigablePath;
@ -54,13 +56,15 @@ public class CircularFetchImpl implements BiDirectionalFetch, Association {
NavigablePath navigablePath, NavigablePath navigablePath,
FetchParent fetchParent, FetchParent fetchParent,
ToOneAttributeMapping fetchable, ToOneAttributeMapping fetchable,
boolean selectByUniqueKey,
NavigablePath referencedNavigablePath, NavigablePath referencedNavigablePath,
DomainResult keyResult) { DomainResult<?> keyResult) {
this.referencedModelPart = referencedModelPart; this.referencedModelPart = referencedModelPart;
this.entityMappingType = entityMappingType; this.entityMappingType = entityMappingType;
this.timing = timing; this.timing = timing;
this.fetchParent = fetchParent; this.fetchParent = fetchParent;
this.navigablePath = navigablePath; this.navigablePath = navigablePath;
this.selectByUniqueKey = selectByUniqueKey;
this.referencedNavigablePath = referencedNavigablePath; this.referencedNavigablePath = referencedNavigablePath;
this.fetchable = fetchable; this.fetchable = fetchable;
this.keyResult = keyResult; this.keyResult = keyResult;
@ -102,6 +106,15 @@ public class CircularFetchImpl implements BiDirectionalFetch, Association {
getNavigablePath(), getNavigablePath(),
referencedModelPart, referencedModelPart,
() -> { () -> {
if ( selectByUniqueKey ) {
return new EntitySelectFetchByUniqueKeyInitializer(
parentAccess,
fetchable,
getNavigablePath(),
entityMappingType.getEntityPersister(),
resultAssembler
);
}
if ( timing == FetchTiming.IMMEDIATE ) { if ( timing == FetchTiming.IMMEDIATE ) {
return new EntitySelectFetchInitializer( return new EntitySelectFetchInitializer(
parentAccess, parentAccess,