Fix bidirectional fetching issues
This commit is contained in:
parent
905227d2ed
commit
e8d337828b
|
@ -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,
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(){
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 ) ) {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue