From 7b96a0ade25e74a63f2485456c1424d2489585a3 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 28 Feb 2024 12:08:23 +0100 Subject: [PATCH] HHH-17783 Allow early initialization of entity instance by non-owning initializer --- ...EnhancementAsProxyLazinessInterceptor.java | 19 ++----------------- .../internal/StatefulPersistenceContext.java | 6 ++++++ .../hibernate/engine/spi/EntityHolder.java | 5 +++++ .../entity/AbstractEntityInitializer.java | 16 +++++++++------- .../EntitySelectFetchInitializer.java | 2 +- 5 files changed, 23 insertions(+), 25 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementAsProxyLazinessInterceptor.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementAsProxyLazinessInterceptor.java index 5939537178..8354438f14 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementAsProxyLazinessInterceptor.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/EnhancementAsProxyLazinessInterceptor.java @@ -22,6 +22,7 @@ import org.hibernate.type.CompositeType; import org.hibernate.type.Type; +import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; import static org.hibernate.engine.internal.ManagedTypeHelper.asSelfDirtinessTracker; import static org.hibernate.engine.internal.ManagedTypeHelper.isSelfDirtinessTrackerType; @@ -215,23 +216,7 @@ public Object forceInitialize(Object target, String attributeName, SharedSession if ( isTemporarySession ) { // Add an entry for this entity in the PC of the temp Session - session.getPersistenceContextInternal().addEntity( - target, - org.hibernate.engine.spi.Status.READ_ONLY, - // loaded state - ArrayHelper.filledArray( - LazyPropertyInitializer.UNFETCHED_PROPERTY, - Object.class, - persister.getPropertyTypes().length - ), - entityKey, - persister.getVersion( target ), - LockMode.NONE, - // we assume an entry exists in the db - true, - persister, - true - ); + session.getPersistenceContext().addEnhancedProxy( entityKey, asPersistentAttributeInterceptable( target ) ); } return persister.initializeEnhancedEntityUsedAsProxy( diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java index 79bb0d14f5..bc491bd456 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/StatefulPersistenceContext.java @@ -516,6 +516,7 @@ public Object removeEntity(EntityKey key) { final Object entity = holder.entity; if ( holder.proxy != null ) { holder.entity = null; + holder.state = EntityHolderState.UNINITIALIZED; entitiesByKey.put( key, holder ); } return entity; @@ -2199,6 +2200,11 @@ public void markAsReloaded(JdbcValuesSourceProcessingState processingState) { processingState.registerReloadedEntityHolder( this ); } + @Override + public boolean isInitialized() { + return state == EntityHolderState.INITIALIZED; + } + @Override public boolean isEventuallyInitialized() { return state == EntityHolderState.INITIALIZED || entityInitializer != null; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/EntityHolder.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/EntityHolder.java index 3dc3a29dd3..5edd6f35f4 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/EntityHolder.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/EntityHolder.java @@ -52,6 +52,11 @@ public interface EntityHolder { */ void markAsReloaded(JdbcValuesSourceProcessingState processingState); + /** + * Whether the entity is already initialized + */ + boolean isInitialized(); + /** * Whether the entity is already initialized or will be initialized through an initializer eventually. */ 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 0ff7ed8b66..e81948474f 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 @@ -114,6 +114,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces private Object version; private Object entityInstance; protected Object entityInstanceForNotify; + private EntityHolder holder; protected State state = State.UNINITIALIZED; private boolean isOwningInitializer; private Object[] resolvedEntityState; @@ -444,7 +445,7 @@ public void resolveInstance(RowProcessingState rowProcessingState) { // even if the parent instance will not refer to this entity. final PersistenceContext persistenceContext = rowProcessingState.getSession().getPersistenceContextInternal(); - final EntityHolder holder = persistenceContext.claimEntityHolderIfPossible( + holder = persistenceContext.claimEntityHolderIfPossible( entityKey, null, rowProcessingState.getJdbcValuesSourceProcessingState(), @@ -544,9 +545,6 @@ else if ( isResultInitializer() ) { registerLoadingEntity( rowProcessingState, entityInstance ); } } - else if ( !isOwningInitializer ) { - state = State.INITIALIZED; - } } else if ( ( entityFromExecutionContext = getEntityFromExecutionContext( rowProcessingState ) ) != null ) { // This is the entity to refresh, so don't set the state to initialized @@ -1050,8 +1048,9 @@ private void resolveState(RowProcessingState rowProcessingState) { } protected boolean skipInitialization(Object toInitialize, RowProcessingState rowProcessingState) { - if ( !isOwningInitializer ) { - return true; + if ( holder.isInitialized() ) { + return rowProcessingState.getJdbcValuesSourceProcessingState().getProcessingOptions() + .getEffectiveOptionalObject() != toInitialize; } final EntityEntry entry = rowProcessingState.getSession().getPersistenceContextInternal().getEntry( toInitialize ); @@ -1062,7 +1061,7 @@ protected boolean skipInitialization(Object toInitialize, RowProcessingState row else if ( entry.getStatus().isDeletedOrGone() ) { return true; } - else { + else if ( isOwningInitializer ) { if ( isPersistentAttributeInterceptable( toInitialize ) ) { final PersistentAttributeInterceptor interceptor = asPersistentAttributeInterceptable( toInitialize ).$$_hibernate_getInterceptor(); @@ -1085,6 +1084,9 @@ else if ( entry.getStatus().isDeletedOrGone() ) { return false; } } + else { + return true; + } } private boolean isReadOnly(RowProcessingState rowProcessingState, SharedSessionContractImplementor persistenceContext) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java index 4b68f66607..9f6254ba08 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java @@ -154,7 +154,7 @@ public void resolveInstance(RowProcessingState rowProcessingState) { ) ); } - entityInstance = holder.getEntity(); + entityInstance = persistenceContext.proxyFor( holder, concreteDescriptor ); if ( holder.getEntityInitializer() == null ) { if ( entityInstance != null && Hibernate.isInitialized( entityInstance ) ) { state = State.INITIALIZED;