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 f22ad6c35db0..63bcc03169ca 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 @@ -78,6 +78,7 @@ import static org.hibernate.engine.internal.ManagedTypeHelper.asManagedEntity; import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable; +import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; /** * A stateful implementation of the {@link PersistenceContext} contract, meaning that we maintain this @@ -231,8 +232,14 @@ public void clear() { if ( entitiesByKey != null ) { //Strictly avoid lambdas in this case for ( EntityHolderImpl value : entitiesByKey.values() ) { - if ( value != null && value.proxy != null ) { - HibernateProxy.extractLazyInitializer( value.proxy ).unsetSession(); + if ( value != null ) { + value.state = EntityHolderState.DETACHED; + if ( value.proxy != null ) { + final LazyInitializer lazyInitializer = extractLazyInitializer( value.proxy ); + if ( lazyInitializer != null ) { + lazyInitializer.unsetSession(); + } + } } } } @@ -2243,6 +2250,11 @@ public boolean isEventuallyInitialized() { return state == EntityHolderState.INITIALIZED || entityInitializer != null; } + @Override + public boolean isDetached() { + return state == EntityHolderState.DETACHED; + } + public static EntityHolderImpl forProxy(EntityKey entityKey, EntityPersister descriptor, Object proxy) { return new EntityHolderImpl( entityKey, descriptor, null, proxy ); } @@ -2255,7 +2267,8 @@ public static EntityHolderImpl forEntity(EntityKey entityKey, EntityPersister de enum EntityHolderState { UNINITIALIZED, ENHANCED_PROXY, - INITIALIZED + INITIALIZED, + DETACHED } // NATURAL ID RESOLUTION HANDLING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2271,4 +2284,13 @@ public NaturalIdResolutions getNaturalIdResolutions() { return naturalIdResolutions; } + @Override + public EntityHolder detachEntity(EntityKey key) { + final EntityHolderImpl entityHolder = removeEntityHolder( key ); + if ( entityHolder != null ) { + entityHolder.state = EntityHolderState.DETACHED; + } + return entityHolder; + } + } 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 8502197cfb49..ece1f1c8633b 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 @@ -67,4 +67,9 @@ public interface EntityHolder { * Whether the entity is already initialized or will be initialized through an initializer eventually. */ boolean isEventuallyInitialized(); + + /** + * Whether the entity is detached. + */ + boolean isDetached(); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java index b60436330bc5..b5198bf3a4a1 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/PersistenceContext.java @@ -864,4 +864,12 @@ EntityHolder claimEntityHolderIfPossible( * @return This persistence context's natural-id helper */ NaturalIdResolutions getNaturalIdResolutions(); + + /** + Remove the {@link EntityHolder} and set its state to DETACHED + */ + default @Nullable EntityHolder detachEntity(EntityKey key) { + EntityHolder entityHolder = removeEntityHolder( key ); + return entityHolder; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultEvictEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultEvictEventListener.java index 9b6f9714552e..c22929a5c4d1 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultEvictEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultEvictEventListener.java @@ -59,7 +59,7 @@ public void onEvict(EvictEvent event) throws HibernateException { .getMappingMetamodel() .getEntityDescriptor( lazyInitializer.getEntityName() ); final EntityKey key = source.generateEntityKey( id, persister ); - final EntityHolder holder = persistenceContext.removeEntityHolder( key ); + final EntityHolder holder = persistenceContext.detachEntity( key ); // if the entity has been evicted then its holder is null if ( holder != null && !lazyInitializer.isUninitialized() ) { final Object entity = holder.getEntity(); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityInitializerImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityInitializerImpl.java index 8c61d7a7e299..1c943c33c10c 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityInitializerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityInitializerImpl.java @@ -565,7 +565,7 @@ protected void resolveKey(EntityInitializerData data, boolean entityKeyOnly) { } if ( oldEntityKey != null && previousRowReuse && oldEntityInstance != null - && areKeysEqual( oldEntityKey.getIdentifier(), id ) ) { + && areKeysEqual( oldEntityKey.getIdentifier(), id ) && !oldEntityHolder.isDetached() ) { data.setState( State.INITIALIZED ); data.entityKey = oldEntityKey; data.setInstance( oldEntityInstance );