HHH-15878 Micro optimisations of StatefulPersistenceContext#clear

This commit is contained in:
Sanne Grinovero 2022-12-15 11:22:23 +00:00 committed by Sanne Grinovero
parent c6ecdb78f4
commit 49c096f146
3 changed files with 37 additions and 30 deletions

View File

@ -9,9 +9,12 @@ package org.hibernate.engine.internal;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; 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.EntityEntry;
import org.hibernate.engine.spi.ManagedEntity; import org.hibernate.engine.spi.ManagedEntity;
import org.hibernate.engine.spi.PersistenceContext; 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.CoreLogging;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
@ -25,6 +28,7 @@ import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
import static org.hibernate.engine.internal.ManagedTypeHelper.asManagedEntity; import static org.hibernate.engine.internal.ManagedTypeHelper.asManagedEntity;
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptableOrNull;
import static org.hibernate.engine.internal.ManagedTypeHelper.isManagedEntity; import static org.hibernate.engine.internal.ManagedTypeHelper.isManagedEntity;
/** /**
@ -329,18 +333,6 @@ public class EntityEntryContext {
return reentrantSafeEntries; return reentrantSafeEntries;
} }
/**
* Not reentrant like #reentrantSafeEntityEntries but most likely
* the more efficient choice, when reentrant safety isn't required.
*/
public void processEachEntity(final Consumer<Object> entityProcessor) {
ManagedEntity managedEntity = head;
while ( managedEntity != null ) {
entityProcessor.accept( managedEntity.$$_hibernate_getEntityInstance() );
managedEntity = managedEntity.$$_hibernate_getNextManagedEntity();
}
}
private void processEachManagedEntity(final Consumer<ManagedEntity> action) { private void processEachManagedEntity(final Consumer<ManagedEntity> action) {
ManagedEntity node = head; ManagedEntity node = head;
while ( node != null ) { 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 * Clear this context of all managed entities
*/ */
public void clear() { public void clear() {
dirty = true; dirty = true;
processEachManagedEntity( EntityEntryContext::clearManagedEntity ); clearAllReferencesFromManagedEntities();
if ( immutableManagedEntityXref != null ) { if ( immutableManagedEntityXref != null ) {
immutableManagedEntityXref.clear(); immutableManagedEntityXref.clear();

View File

@ -272,6 +272,14 @@ public final class ManagedTypeHelper {
throw new ClassCastException( "Object of type '" + entity.getClass() + "' can't be cast to PersistentAttributeInterceptable" ); 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 * Cast the object to HibernateProxy
* (using this is highly preferrable over a direct cast) * (using this is highly preferrable over a direct cast)

View File

@ -35,7 +35,6 @@ import org.hibernate.PersistentObjectException;
import org.hibernate.TransientObjectException; import org.hibernate.TransientObjectException;
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor; import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; 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.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.AssociationKey; import org.hibernate.engine.spi.AssociationKey;
import org.hibernate.engine.spi.BatchFetchQueue; 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.EntityEntry;
import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.EntityUniqueKey; import org.hibernate.engine.spi.EntityUniqueKey;
import org.hibernate.engine.spi.ManagedEntity;
import org.hibernate.engine.spi.NaturalIdResolutions; import org.hibernate.engine.spi.NaturalIdResolutions;
import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.PersistentAttributeInterceptable; 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.asHibernateProxy;
import static org.hibernate.engine.internal.ManagedTypeHelper.asManagedEntity; import static org.hibernate.engine.internal.ManagedTypeHelper.asManagedEntity;
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable;
import static org.hibernate.engine.internal.ManagedTypeHelper.isHibernateProxy;
import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable; 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(); final SharedSessionContractImplementor session = getSession();
if ( collectionEntries != null ) { if ( collectionEntries != null ) {
IdentityMap.onEachKey( collectionEntries, k -> k.unsetSession( session ) ); IdentityMap.onEachKey( collectionEntries, k -> k.unsetSession( session ) );
@ -267,18 +262,6 @@ public class StatefulPersistenceContext implements PersistenceContext {
naturalIdResolutions = null; 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 @Override
public boolean isDefaultReadOnly() { public boolean isDefaultReadOnly() {
return defaultReadOnly; return defaultReadOnly;