From e2ec3cd3e7fac1ccd79373230cd6dbec8608ddee Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 13 Jul 2023 16:46:42 +0200 Subject: [PATCH] HHH-16812 StackOverflowError an embeddable's @Parent is a subclass in an inheritance tree --- .../PropertySetterAccessException.java | 4 +- .../engine/internal/ManagedTypeHelper.java | 11 +++++ .../AbstractEmbeddableInitializer.java | 44 ++++++++++++++----- 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/PropertySetterAccessException.java b/hibernate-core/src/main/java/org/hibernate/PropertySetterAccessException.java index a41c748beb..1675732bd4 100644 --- a/hibernate-core/src/main/java/org/hibernate/PropertySetterAccessException.java +++ b/hibernate-core/src/main/java/org/hibernate/PropertySetterAccessException.java @@ -8,6 +8,8 @@ package org.hibernate; import java.util.Collection; +import org.hibernate.proxy.HibernateProxy; + import org.checkerframework.checker.nullness.qual.Nullable; /** @@ -49,7 +51,7 @@ public class PropertySetterAccessException extends PropertyAccessException { } public static String loggablePropertyValueString(Object value) { - if ( value instanceof Collection ) { + if ( value instanceof Collection || value instanceof HibernateProxy ) { return value.getClass().getSimpleName(); } return value.toString(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/ManagedTypeHelper.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/ManagedTypeHelper.java index 774c181660..b1a204c613 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/ManagedTypeHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/ManagedTypeHelper.java @@ -377,6 +377,17 @@ public final class ManagedTypeHelper { throw new ClassCastException( "Object of type '" + entity.getClass() + "' can't be cast to SelfDirtinessTracker" ); } + /** + * Cast the object to an HibernateProxy, or return null in case it is not an instance of HibernateProxy + * @param entity the entity to cast + * @return the same instance after casting or null if it is not an instance of HibernateProxy + */ + public static HibernateProxy asHibernateProxyOrNull(final Object entity) { + return entity instanceof PrimeAmongSecondarySupertypes ? + ( (PrimeAmongSecondarySupertypes) entity ).asHibernateProxy() : + null; + } + private static final class TypeMeta { final boolean isManagedType; final boolean isSelfDirtinessTrackerType; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java index 252bc5dd88..9dc6c04447 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java @@ -6,6 +6,7 @@ */ package org.hibernate.sql.results.graph.embeddable; +import org.hibernate.engine.internal.ManagedTypeHelper; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.CompositeIdentifierMapping; import org.hibernate.metamodel.mapping.EmbeddableMappingType; @@ -372,7 +373,8 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA return; } - final Object parent = determineParentInstance( processingState ); + Initializer parentInitializer = determineParentInitializer( processingState ); + final Object parent = determineParentInstance( parentInitializer ); if ( parent == null ) { EmbeddableLoadingLogger.EMBEDDED_LOAD_LOGGER.debugf( "Unable to determine parent for injection into embeddable [%s]", @@ -388,10 +390,27 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA compositeInstance ); - parentInjectionAccess.getSetter().set( compositeInstance, parent ); + + final HibernateProxy proxy; + if ( fetchParentAccess != null + && ( proxy = ManagedTypeHelper.asHibernateProxyOrNull( parent ) ) != null ) { + assert parentInitializer != null; + assert parentInitializer instanceof EntityInitializer; + parentInitializer.asEntityInitializer().registerResolutionListener( + o -> + parentInjectionAccess.getSetter() + .set( + compositeInstance, + proxy.getHibernateLazyInitializer().getImplementation() + ) + ); + } + else { + parentInjectionAccess.getSetter().set( compositeInstance, parent ); + } } - private Object determineParentInstance(RowProcessingState processingState) { + private Initializer determineParentInitializer(RowProcessingState processingState){ // use `fetchParentAccess` if it is available - it is more efficient // and the correct way to do it. @@ -409,8 +428,7 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA // todo (6.x) - allow injection of containing composite as parent if // it is the direct parent - final FetchParentAccess firstEntityDescriptorAccess = fetchParentAccess.findFirstEntityDescriptorAccess(); - return firstEntityDescriptorAccess.getInitializedInstance(); + return fetchParentAccess.findFirstEntityDescriptorAccess(); } // Otherwise, fallback to determining the parent-initializer by path @@ -418,11 +436,15 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA // comment above final NavigablePath parentPath = navigablePath.getParent(); - if ( parentPath == null ) { - return null; - } + assert parentPath != null; - final Initializer parentInitializer = processingState.resolveInitializer( parentPath ); + return processingState.resolveInitializer( parentPath ); + } + + private Object determineParentInstance(Initializer parentInitializer) { + if ( parentInitializer == null ) { + throw new UnsupportedOperationException( "Cannot determine Embeddable: " + navigablePath + " parent instance, parent initializer is null" ); + } if ( parentInitializer.isCollectionInitializer() ) { return ( (CollectionInitializer) parentInitializer ).getCollectionInstance().getOwner(); @@ -430,10 +452,10 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA final EntityInitializer parentEntityInitializer = parentInitializer.asEntityInitializer(); if ( parentEntityInitializer != null ) { - return parentEntityInitializer.getEntityInstance(); + return parentEntityInitializer.getInitializedInstance(); } - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException( "The Embeddable: " + navigablePath + " parent initializer is neither an instance of an EntityInitializer nor of a CollectionInitializer" ); } @Override