diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SubselectFetch.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SubselectFetch.java index 97ae1ad86c..1c957109ea 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SubselectFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SubselectFetch.java @@ -21,6 +21,7 @@ import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.entity.LoadingEntityEntry; +import org.hibernate.sql.results.graph.entity.internal.EntityResultInitializer; /** * Encapsulates details related to entities which contain sub-select-fetchable @@ -180,7 +181,7 @@ public class SubselectFetch { } private boolean shouldAddSubselectFetch(LoadingEntityEntry entry) { - if ( entry.getEntityInitializer().isEntityResultInitializer() ) { + if ( entry.getEntityInitializer() instanceof EntityResultInitializer ) { return true; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractImmediateCollectionInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractImmediateCollectionInitializer.java index 40f90ef01d..c96f8293c5 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractImmediateCollectionInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractImmediateCollectionInitializer.java @@ -36,7 +36,6 @@ import static org.hibernate.sql.results.graph.collection.CollectionLoadingLogger * @implNote Mainly an intention contract wrt the immediacy of the fetch. */ public abstract class AbstractImmediateCollectionInitializer extends AbstractCollectionInitializer { - private final LockMode lockMode; // per-row state private LoadingCollectionEntryImpl responsibility; @@ -61,7 +60,6 @@ public abstract class AbstractImmediateCollectionInitializer extends AbstractCol DomainResultAssembler collectionValueKeyResultAssembler) { super( collectionPath, collectionAttributeMapping, parentAccess, collectionKeyResultAssembler ); this.collectionValueKeyResultAssembler = collectionValueKeyResultAssembler; - this.lockMode = lockMode; } protected abstract String getSimpleConcreteImplName(); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/DelayedCollectionInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/DelayedCollectionInitializer.java index dced3579ce..6a8628c840 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/DelayedCollectionInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/DelayedCollectionInitializer.java @@ -11,6 +11,7 @@ import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.DomainResultAssembler; import org.hibernate.sql.results.graph.FetchParentAccess; +import org.hibernate.sql.results.graph.entity.EntityInitializer; import org.hibernate.sql.results.jdbc.spi.RowProcessingState; /** @@ -29,6 +30,12 @@ public class DelayedCollectionInitializer extends AbstractCollectionInitializer @Override public void resolveInstance(RowProcessingState rowProcessingState) { + if ( parentAccess != null ) { + final EntityInitializer parentEntityInitializer = parentAccess.findFirstEntityInitializer(); + if ( parentEntityInitializer != null && parentEntityInitializer.isInitialized() ) { + return; + } + } resolveInstance( rowProcessingState, false ); } 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 046884d9de..5e1418810d 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 @@ -143,12 +143,19 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA @Override public FetchParentAccess findFirstEntityDescriptorAccess() { - return getFetchParentAccess().findFirstEntityDescriptorAccess(); + if ( fetchParentAccess == null ) { + return null; + } + return fetchParentAccess.findFirstEntityDescriptorAccess(); } @Override public EntityInitializer findFirstEntityInitializer() { - return findFirstEntityDescriptorAccess().findFirstEntityInitializer(); + final FetchParentAccess firstEntityDescriptorAccess = findFirstEntityDescriptorAccess(); + if ( firstEntityDescriptorAccess == null ) { + return null; + } + return firstEntityDescriptorAccess.findFirstEntityInitializer(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java index 8a493a11a7..7f20fa2baf 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java @@ -42,6 +42,7 @@ import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl; +import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import org.hibernate.proxy.map.MapProxy; import org.hibernate.spi.NavigablePath; @@ -51,6 +52,7 @@ import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultAssembler; import org.hibernate.sql.results.graph.Fetch; import org.hibernate.sql.results.graph.basic.BasicResultAssembler; +import org.hibernate.sql.results.graph.entity.internal.EntityResultInitializer; import org.hibernate.sql.results.internal.NullValueAssembler; import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions; import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState; @@ -409,7 +411,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces final Object entityInstanceFromExecutionContext = rowProcessingState.getJdbcValuesSourceProcessingState().getExecutionContext().getEntityInstance(); if ( isProxyInstance( proxy ) ) { - if ( isEntityResultInitializer() && entityInstanceFromExecutionContext != null ) { + if ( this instanceof EntityResultInitializer && entityInstanceFromExecutionContext != null ) { entityInstance = entityInstanceFromExecutionContext; registerLoadingEntity( rowProcessingState, entityInstance ); } @@ -421,8 +423,12 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces final Object existingEntity = persistenceContext.getEntity( entityKey ); if ( existingEntity != null ) { entityInstance = existingEntity; + if ( existingLoadingEntry == null && isExistingEntityInitialized( existingEntity ) ) { + notifyResolutionListeners( entityInstance ); + this.isInitialized = true; + } } - else if ( isEntityResultInitializer() && entityInstanceFromExecutionContext != null ) { + else if ( this instanceof EntityResultInitializer && entityInstanceFromExecutionContext != null ) { entityInstance = entityInstanceFromExecutionContext; registerLoadingEntity( rowProcessingState, entityInstance ); } @@ -457,6 +463,26 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces && ( proxy instanceof MapProxy || entityDescriptor.getJavaType().getJavaTypeClass().isInstance( proxy ) ); } + private boolean isExistingEntityInitialized(Object existingEntity) { + final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( entityInstance ); + if ( lazyInitializer != null ) { + if ( lazyInitializer.isUninitialized() ) { + return false; + } + return true; + } + else if ( isPersistentAttributeInterceptable( existingEntity ) ) { + final PersistentAttributeInterceptor persistentAttributeInterceptor = asPersistentAttributeInterceptable( + entityInstance ).$$_hibernate_getInterceptor(); + if ( persistentAttributeInterceptor == null || persistentAttributeInterceptor instanceof EnhancementAsProxyLazinessInterceptor ) { + return false; + } + return true; + } + + return true; + } + /** * Check the version of the object in the {@code RowProcessingState} against * the object version in the session cache, throwing an exception @@ -953,6 +979,11 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces } } + @Override + public boolean isInitialized() { + return isInitialized; + } + @Override public EntityPersister getConcreteDescriptor() { return concreteDescriptor; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/EntityInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/EntityInitializer.java index a3413e796e..72d57d7736 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/EntityInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/EntityInitializer.java @@ -60,6 +60,9 @@ public interface EntityInitializer extends FetchParentAccess { return this; } + default boolean isInitialized() { + return false; + } default boolean isEntityResultInitializer() { return false; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchInitializer.java index 57fa0607c4..f6bba841ed 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchInitializer.java @@ -83,6 +83,11 @@ public class EntityDelayedFetchInitializer extends AbstractFetchParentAccess imp return; } + final EntityInitializer parentEntityInitializer = getParentEntityInitializer( parentAccess ); + if ( parentEntityInitializer != null && parentEntityInitializer.isInitialized() ) { + return; + } + if ( !isAttributeAssignableToConcreteDescriptor( parentAccess, referencedModelPart ) ) { return; } @@ -115,7 +120,7 @@ public class EntityDelayedFetchInitializer extends AbstractFetchParentAccess imp if ( referencedModelPart.isOptional() && parentAccess != null && !parentAccess.isEmbeddableInitializer() - && isEnhancedForLazyLoading( parentAccess ) ) { + && isEnhancedForLazyLoading( parentEntityInitializer ) ) { entityInstance = LazyPropertyInitializer.UNFETCHED_PROPERTY; } else { @@ -141,7 +146,7 @@ public class EntityDelayedFetchInitializer extends AbstractFetchParentAccess imp if ( entityInstance == null ) { if ( parentAccess != null && !parentAccess.isEmbeddableInitializer() - && isEnhancedForLazyLoading( parentAccess ) ) { + && isEnhancedForLazyLoading( parentEntityInitializer ) ) { return; } entityInstance = ( (UniqueKeyLoadable) concreteDescriptor ).loadByUniqueKey( @@ -180,8 +185,16 @@ public class EntityDelayedFetchInitializer extends AbstractFetchParentAccess imp } } - private static boolean isEnhancedForLazyLoading(FetchParentAccess parentAccess) { - return parentAccess.findFirstEntityInitializer().getEntityDescriptor().getBytecodeEnhancementMetadata() + private EntityInitializer getParentEntityInitializer(FetchParentAccess parentAccess) { + if ( parentAccess != null ) { + return parentAccess.findFirstEntityInitializer(); + } + return null; + } + + private static boolean isEnhancedForLazyLoading(EntityInitializer parentEntityIntialiazer) { + return parentEntityIntialiazer != null && parentEntityIntialiazer.getEntityDescriptor() + .getBytecodeEnhancementMetadata() .isEnhancedForLazyLoading(); } @@ -238,4 +251,9 @@ public class EntityDelayedFetchInitializer extends AbstractFetchParentAccess imp return "EntityDelayedFetchInitializer(" + LoggingHelper.toLoggableString( navigablePath ) + ")"; } + @Override + public boolean isEntityResultInitializer() { + return true; + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultInitializer.java index 529d8db7fc..157af70aaf 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultInitializer.java @@ -57,8 +57,4 @@ public class EntityResultInitializer extends AbstractEntityInitializer { return CONCRETE_NAME + "(" + getNavigablePath() + ")"; } - @Override - public boolean isEntityResultInitializer() { - return true; - } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesSourceProcessingStateStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesSourceProcessingStateStandardImpl.java index 944310bc9d..81d0a3ae89 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesSourceProcessingStateStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesSourceProcessingStateStandardImpl.java @@ -15,6 +15,7 @@ import java.util.function.BiConsumer; import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityUniqueKey; +import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.event.service.spi.EventListenerGroup; import org.hibernate.event.spi.EventSource;