mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-03-07 02:09:31 +00:00
HHH-14390 : StackOverflowError with @Fetch(FetchMode.SELECT) mapped for entity with an ID that is a bidirectional one-to-one eager association
Move fix into FetchStyleLoadPlanBuildingAssociationVisitationStrategy (cherry picked from commit cb18fdb4f7ddbe6ed6bf2db7c9399173d6318a9d)
This commit is contained in:
parent
4448d42c7d
commit
681cc77dbb
hibernate-core/src/main/java/org/hibernate
cfg
loader/plan/build/internal
@ -237,18 +237,6 @@ else if ( otherSideProperty.getValue() instanceof ManyToOne ) {
|
||||
boolean referenceToPrimaryKey = referencesDerivedId || mappedBy == null;
|
||||
value.setReferenceToPrimaryKey( referenceToPrimaryKey );
|
||||
|
||||
// If the other side is an entity with an ID that is derived from
|
||||
// this side's owner entity, and both sides of the association are eager,
|
||||
// then this side must be set to FetchMode.SELECT; otherwise,
|
||||
// there will be an infinite loop attempting to load the derived ID on
|
||||
// the opposite side.
|
||||
if ( referencesDerivedId &&
|
||||
!value.isLazy() &&
|
||||
value.getFetchMode() == FetchMode.JOIN &&
|
||||
!otherSideProperty.isLazy() ) {
|
||||
value.setFetchMode( FetchMode.SELECT );
|
||||
}
|
||||
|
||||
String propertyRef = value.getReferencedPropertyName();
|
||||
if ( propertyRef != null ) {
|
||||
buildingContext.getMetadataCollector().addUniquePropertyReference(
|
||||
|
@ -15,6 +15,7 @@
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.loader.plan.spi.CollectionReturn;
|
||||
import org.hibernate.loader.plan.spi.EntityReference;
|
||||
import org.hibernate.loader.plan.spi.EntityReturn;
|
||||
import org.hibernate.loader.plan.spi.FetchSource;
|
||||
import org.hibernate.loader.plan.spi.LoadPlan;
|
||||
@ -29,6 +30,8 @@
|
||||
import org.hibernate.type.CompositeType;
|
||||
import org.hibernate.type.EmbeddedComponentType;
|
||||
import org.hibernate.type.EntityType;
|
||||
import org.hibernate.type.ForeignKeyDirection;
|
||||
import org.hibernate.type.OneToOneType;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
@ -252,6 +255,52 @@ protected FetchStrategy adjustJoinFetchIfNeeded(
|
||||
}
|
||||
}
|
||||
|
||||
if ( attributeType.isEntityType() &&
|
||||
fetchStrategy.getTiming() == FetchTiming.IMMEDIATE &&
|
||||
fetchStrategy.getStyle() == FetchStyle.JOIN ) {
|
||||
final EntityType entityType = (EntityType) attributeType;
|
||||
final EntityReference currentEntityReference = currentSource.resolveEntityReference();
|
||||
if ( currentEntityReference != null ) {
|
||||
final EntityPersister currentEntityPersister = currentEntityReference.getEntityPersister();
|
||||
if ( entityType.isOneToOne() && entityType.getForeignKeyDirection() == ForeignKeyDirection.TO_PARENT ) {
|
||||
// attributeDefinition is the "mappedBy" (inverse) side of a
|
||||
// bidirectional one-to-one association.
|
||||
final OneToOneType oneToOneType = (OneToOneType) attributeType;
|
||||
final String associatedUniqueKeyPropertyName = oneToOneType.getIdentifierOrUniqueKeyPropertyName(
|
||||
sessionFactory()
|
||||
);
|
||||
if ( associatedUniqueKeyPropertyName == null ) {
|
||||
// The foreign key for the other side of the association is the ID.
|
||||
// Now check if the ID itself is the other side of the association.
|
||||
final EntityPersister associatedEntityPersister = (EntityPersister) oneToOneType.getAssociatedJoinable(
|
||||
sessionFactory()
|
||||
);
|
||||
final Type associatedIdentifierType = associatedEntityPersister.getIdentifierType();
|
||||
if ( associatedIdentifierType.isComponentType() &&
|
||||
( (CompositeType) associatedIdentifierType ).isEmbedded() ) {
|
||||
final EmbeddedComponentType associatedNonEncapsulatedIdentifierType =
|
||||
(EmbeddedComponentType) associatedIdentifierType;
|
||||
if ( associatedNonEncapsulatedIdentifierType.getSubtypes().length == 1 &&
|
||||
EntityType.class.isInstance( associatedNonEncapsulatedIdentifierType.getSubtypes()[0] ) ) {
|
||||
final EntityType otherSideEntityType =
|
||||
( (EntityType) associatedNonEncapsulatedIdentifierType.getSubtypes()[0] );
|
||||
if ( otherSideEntityType.isLogicalOneToOne() &&
|
||||
otherSideEntityType.isReferenceToPrimaryKey() &&
|
||||
otherSideEntityType.getAssociatedEntityName().equals( currentEntityPersister.getEntityName() ) ) {
|
||||
// The associated entity's ID is the other side of the association.
|
||||
// This side must be set to FetchMode.SELECT; otherwise,
|
||||
// there will be an infinite loop because the current entity
|
||||
// would need to be loaded before the associated entity can be loaded,
|
||||
// but the associated entity cannot be loaded until after the current
|
||||
// entity is loaded (since the current entity is the associated entity's ID).
|
||||
return new FetchStrategy( fetchStrategy.getTiming(), FetchStyle.SELECT );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return fetchStrategy;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user