diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryContext.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryContext.java index 930714a3a9..7fe035ecb9 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryContext.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/EntityEntryContext.java @@ -9,9 +9,12 @@ package org.hibernate.engine.internal; import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; +import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.ManagedEntity; import org.hibernate.engine.spi.PersistenceContext; +import org.hibernate.engine.spi.PersistentAttributeInterceptable; +import org.hibernate.engine.spi.PersistentAttributeInterceptor; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; @@ -25,6 +28,7 @@ import java.util.Map; import java.util.function.Consumer; import static org.hibernate.engine.internal.ManagedTypeHelper.asManagedEntity; +import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptableOrNull; import static org.hibernate.engine.internal.ManagedTypeHelper.isManagedEntity; /** @@ -329,18 +333,6 @@ public class EntityEntryContext { return reentrantSafeEntries; } - /** - * Not reentrant like #reentrantSafeEntityEntries but most likely - * the more efficient choice, when reentrant safety isn't required. - */ - public void processEachEntity(final Consumer entityProcessor) { - ManagedEntity managedEntity = head; - while ( managedEntity != null ) { - entityProcessor.accept( managedEntity.$$_hibernate_getEntityInstance() ); - managedEntity = managedEntity.$$_hibernate_getNextManagedEntity(); - } - } - private void processEachManagedEntity(final Consumer action) { ManagedEntity node = head; while ( node != null ) { @@ -350,13 +342,37 @@ public class EntityEntryContext { } } + // Could have used #processEachManagedEntity but avoided because of measurable overhead. + // Careful, this needs to be very efficient as we potentially iterate quite a bit! + // Also: we perform two operations at once, so to not iterate on the list twice; + // being a linked list, multiple iterations are not cache friendly at all. + private void clearAllReferencesFromManagedEntities() { + ManagedEntity nextManagedEntity = head; + while ( nextManagedEntity != null ) { + final ManagedEntity current = nextManagedEntity; + nextManagedEntity = current.$$_hibernate_getNextManagedEntity(); + Object toProcess = current.$$_hibernate_getEntityInstance(); + unsetSession( asPersistentAttributeInterceptableOrNull( toProcess ) ); + clearManagedEntity( current );//careful this also unlinks from the "next" entry in the list + } + } + + private static void unsetSession(PersistentAttributeInterceptable persistentAttributeInterceptable) { + if ( persistentAttributeInterceptable != null ) { + final PersistentAttributeInterceptor interceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor(); + if ( interceptor instanceof LazyAttributeLoadingInterceptor ) { + ( (LazyAttributeLoadingInterceptor) interceptor ).unsetSession(); + } + } + } + /** * Clear this context of all managed entities */ public void clear() { dirty = true; - processEachManagedEntity( EntityEntryContext::clearManagedEntity ); + clearAllReferencesFromManagedEntities(); if ( immutableManagedEntityXref != null ) { immutableManagedEntityXref.clear(); 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 d79843380d..f64dd222a8 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 @@ -272,6 +272,14 @@ public final class ManagedTypeHelper { throw new ClassCastException( "Object of type '" + entity.getClass() + "' can't be cast to PersistentAttributeInterceptable" ); } + public static PersistentAttributeInterceptable asPersistentAttributeInterceptableOrNull(final Object entity) { + if ( entity instanceof PrimeAmongSecondarySupertypes ) { + PrimeAmongSecondarySupertypes t = (PrimeAmongSecondarySupertypes) entity; + return t.asPersistentAttributeInterceptable(); + } + return null; + } + /** * Cast the object to HibernateProxy * (using this is highly preferrable over a direct cast) 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 4599349455..5f756c60d7 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 @@ -35,7 +35,6 @@ import org.hibernate.PersistentObjectException; import org.hibernate.TransientObjectException; import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor; import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; -import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.AssociationKey; import org.hibernate.engine.spi.BatchFetchQueue; @@ -44,7 +43,6 @@ import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityUniqueKey; -import org.hibernate.engine.spi.ManagedEntity; import org.hibernate.engine.spi.NaturalIdResolutions; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistentAttributeInterceptable; @@ -71,7 +69,6 @@ import org.jboss.logging.Logger; import static org.hibernate.engine.internal.ManagedTypeHelper.asHibernateProxy; import static org.hibernate.engine.internal.ManagedTypeHelper.asManagedEntity; import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; -import static org.hibernate.engine.internal.ManagedTypeHelper.isHibernateProxy; import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable; /** @@ -236,8 +233,6 @@ public class StatefulPersistenceContext implements PersistenceContext { } } - entityEntryContext.processEachEntity( StatefulPersistenceContext::processEntityOnClear ); - final SharedSessionContractImplementor session = getSession(); if ( collectionEntries != null ) { IdentityMap.onEachKey( collectionEntries, k -> k.unsetSession( session ) ); @@ -267,18 +262,6 @@ public class StatefulPersistenceContext implements PersistenceContext { naturalIdResolutions = null; } - private static void processEntityOnClear(final Object entity) { - //type-cache-pollution agent: it's crucial to use the ManagedTypeHelper rather than attempting a direct cast - ManagedTypeHelper.processIfPersistentAttributeInterceptable( entity, StatefulPersistenceContext::unsetSession, null ); - } - - private static void unsetSession(PersistentAttributeInterceptable persistentAttributeInterceptable, Object ignoredParam) { - final PersistentAttributeInterceptor interceptor = persistentAttributeInterceptable.$$_hibernate_getInterceptor(); - if ( interceptor instanceof LazyAttributeLoadingInterceptor ) { - ( (LazyAttributeLoadingInterceptor) interceptor ).unsetSession(); - } - } - @Override public boolean isDefaultReadOnly() { return defaultReadOnly;