cleanups in event listeners

Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
Gavin King 2024-09-02 10:04:08 +02:00
parent ec3be767e4
commit 9c75adcffc
3 changed files with 45 additions and 63 deletions

View File

@ -38,6 +38,7 @@ import org.hibernate.internal.FastSessionServices;
import org.hibernate.jpa.event.spi.CallbackRegistry; import org.hibernate.jpa.event.spi.CallbackRegistry;
import org.hibernate.jpa.event.spi.CallbackRegistryConsumer; import org.hibernate.jpa.event.spi.CallbackRegistryConsumer;
import org.hibernate.jpa.event.spi.CallbackType; import org.hibernate.jpa.event.spi.CallbackType;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.pretty.MessageHelper; import org.hibernate.pretty.MessageHelper;
@ -463,12 +464,12 @@ public class DefaultDeleteEventListener implements DeleteEventListener, Callback
final String[] propertyNames = persister.getPropertyNames(); final String[] propertyNames = persister.getPropertyNames();
final BytecodeEnhancementMetadata enhancementMetadata = persister.getBytecodeEnhancementMetadata(); final BytecodeEnhancementMetadata enhancementMetadata = persister.getBytecodeEnhancementMetadata();
final MappingMetamodelImplementor metamodel = persister.getFactory().getMappingMetamodel();
for ( int i = 0; i < types.length; i++) { for ( int i = 0; i < types.length; i++) {
if ( types[i] instanceof CollectionType collectionType if ( types[i] instanceof CollectionType collectionType
&& !enhancementMetadata.isAttributeLoaded( parent, propertyNames[i] ) ) { && !enhancementMetadata.isAttributeLoaded( parent, propertyNames[i] ) ) {
final CollectionPersister collectionDescriptor = final CollectionPersister collectionDescriptor =
persister.getFactory().getMappingMetamodel() metamodel.getCollectionDescriptor( collectionType.getRole() );
.getCollectionDescriptor( collectionType.getRole() );
if ( collectionDescriptor.needsRemove() || collectionDescriptor.hasCache() ) { if ( collectionDescriptor.needsRemove() || collectionDescriptor.hasCache() ) {
final Object keyOfOwner = collectionType.getKeyOfOwner( parent, eventSource.getSession() ); final Object keyOfOwner = collectionType.getKeyOfOwner( parent, eventSource.getSession() );
// This will make sure that a CollectionEntry exists // This will make sure that a CollectionEntry exists

View File

@ -247,8 +247,8 @@ public class DefaultMergeEventListener
CompositeType compositeType, CompositeType compositeType,
EventSource session, EventSource session,
MergeContext mergeContext) { MergeContext mergeContext) {
final SessionFactoryImplementor sessionFactory = session.getSessionFactory(); final SessionFactoryImplementor factory = session.getSessionFactory();
final Object idCopy = compositeType.deepCopy( id, sessionFactory ); final Object idCopy = compositeType.deepCopy( id, factory );
final Type[] subtypes = compositeType.getSubtypes(); final Type[] subtypes = compositeType.getSubtypes();
final Object[] propertyValues = compositeType.getPropertyValues( id ); final Object[] propertyValues = compositeType.getPropertyValues( id );
final Object[] copyValues = compositeType.getPropertyValues( idCopy ); final Object[] copyValues = compositeType.getPropertyValues( idCopy );
@ -256,22 +256,17 @@ public class DefaultMergeEventListener
final Type subtype = subtypes[i]; final Type subtype = subtypes[i];
if ( subtype instanceof EntityType ) { if ( subtype instanceof EntityType ) {
// the value of the copy in the MergeContext has the id assigned // the value of the copy in the MergeContext has the id assigned
final Object o = mergeContext.get( propertyValues[i] ); final Object object = mergeContext.get( propertyValues[i] );
if ( o != null ) { copyValues[i] = object == null ? subtype.deepCopy( propertyValues[i], factory ) : object;
copyValues[i] = o;
}
else {
copyValues[i] = subtype.deepCopy( propertyValues[i], sessionFactory );
}
} }
else if ( subtype instanceof AnyType ) { else if ( subtype instanceof AnyType anyType ) {
copyValues[i] = copyCompositeTypeId( propertyValues[i], (AnyType) subtype, session, mergeContext ); copyValues[i] = copyCompositeTypeId( propertyValues[i], anyType, session, mergeContext );
} }
else if ( subtype instanceof ComponentType ) { else if ( subtype instanceof ComponentType componentType ) {
copyValues[i] = copyCompositeTypeId( propertyValues[i], (ComponentType) subtype, session, mergeContext ); copyValues[i] = copyCompositeTypeId( propertyValues[i], componentType, session, mergeContext );
} }
else { else {
copyValues[i] = subtype.deepCopy( propertyValues[i], sessionFactory ); copyValues[i] = subtype.deepCopy( propertyValues[i], factory );
} }
} }
return compositeType.replacePropertyValues( idCopy, copyValues, session ); return compositeType.replacePropertyValues( idCopy, copyValues, session );
@ -326,14 +321,16 @@ public class DefaultMergeEventListener
event.setResult( copy ); event.setResult( copy );
if ( isPersistentAttributeInterceptable( copy ) ) { if ( isPersistentAttributeInterceptable( copy ) ) {
final PersistentAttributeInterceptor interceptor = asPersistentAttributeInterceptable( copy ).$$_hibernate_getInterceptor(); final PersistentAttributeInterceptor interceptor =
asPersistentAttributeInterceptable( copy ).$$_hibernate_getInterceptor();
if ( interceptor == null ) { if ( interceptor == null ) {
persister.getBytecodeEnhancementMetadata().injectInterceptor( copy, id, session ); persister.getBytecodeEnhancementMetadata().injectInterceptor( copy, id, session );
} }
} }
} }
private static Object copyEntity(MergeContext copyCache, Object entity, EventSource session, EntityPersister persister, Object id) { private static Object copyEntity(
MergeContext copyCache, Object entity, EventSource session, EntityPersister persister, Object id) {
final Object existingCopy = copyCache.get( entity ); final Object existingCopy = copyCache.get( entity );
if ( existingCopy != null ) { if ( existingCopy != null ) {
persister.setIdentifier( existingCopy, id, session ); persister.setIdentifier( existingCopy, id, session );
@ -352,15 +349,14 @@ public class DefaultMergeEventListener
super( entity, id, session ); super( entity, id, session );
} }
@Override @Override
protected Object processCollection(Object collection, CollectionType collectionType) throws HibernateException { protected Object processCollection(Object collection, CollectionType collectionType) {
if ( collection instanceof PersistentCollection ) { if ( collection instanceof PersistentCollection ) {
final PersistentCollection<?> coll = (PersistentCollection<?>) collection; final PersistentCollection<?> coll = (PersistentCollection<?>) collection;
final CollectionPersister persister = getSession().getFactory() final CollectionPersister persister =
.getRuntimeMetamodels() getSession().getFactory().getMappingMetamodel()
.getMappingMetamodel() .getCollectionDescriptor( collectionType.getRole() );
.getCollectionDescriptor( collectionType.getRole() ); final CollectionEntry collectionEntry =
final CollectionEntry collectionEntry = getSession().getPersistenceContextInternal() getSession().getPersistenceContextInternal().getCollectionEntry( coll );
.getCollectionEntry( coll );
if ( !coll.equalsSnapshot( persister ) ) { if ( !coll.equalsSnapshot( persister ) ) {
collectionEntry.resetStoredSnapshot( coll, coll.getSnapshot( persister ) ); collectionEntry.resetStoredSnapshot( coll, coll.getSnapshot( persister ) );
} }
@ -368,7 +364,7 @@ public class DefaultMergeEventListener
return null; return null;
} }
@Override @Override
Object processEntity(Object value, EntityType entityType) throws HibernateException { Object processEntity(Object value, EntityType entityType) {
return null; return null;
} }
} }
@ -400,13 +396,9 @@ public class DefaultMergeEventListener
if ( originalId == null ) { if ( originalId == null ) {
originalId = persister.getIdentifier( entity, source ); originalId = persister.getIdentifier( entity, source );
} }
final Object clonedIdentifier; final Object clonedIdentifier = copiedId == null
if ( copiedId == null ) { ? persister.getIdentifierType().deepCopy( originalId, event.getFactory() )
clonedIdentifier = persister.getIdentifierType().deepCopy( originalId, event.getFactory() ); : copiedId;
}
else {
clonedIdentifier = copiedId;
}
final Object id = getDetachedEntityId( event, originalId, persister ); final Object id = getDetachedEntityId( event, originalId, persister );
// we must clone embedded composite identifiers, or we will get back the same instance that we pass in // we must clone embedded composite identifiers, or we will get back the same instance that we pass in
// apply the special MERGE fetch profile and perform the resolution (Session#get) // apply the special MERGE fetch profile and perform the resolution (Session#get)
@ -482,8 +474,7 @@ public class DefaultMergeEventListener
} }
else { else {
// check that entity id = requestedId // check that entity id = requestedId
final Object entityId = originalId; if ( !persister.getIdentifierType().isEqual( id, originalId, source.getFactory() ) ) {
if ( !persister.getIdentifierType().isEqual( id, entityId, source.getFactory() ) ) {
throw new HibernateException( "merge requested with id not matching id of passed entity" ); throw new HibernateException( "merge requested with id not matching id of passed entity" );
} }
return id; return id;
@ -507,8 +498,8 @@ public class DefaultMergeEventListener
final PersistentAttributeInterceptor managedInterceptor = final PersistentAttributeInterceptor managedInterceptor =
asPersistentAttributeInterceptable( managed ).$$_hibernate_getInterceptor(); asPersistentAttributeInterceptable( managed ).$$_hibernate_getInterceptor();
// todo - do we need to specially handle the case where both `incoming` and `managed` are initialized, but // todo - do we need to specially handle the case where both `incoming` and `managed`
// with different attributes initialized? // are initialized, but with different attributes initialized?
// - for now, assume we do not... // - for now, assume we do not...
// if the managed entity is not a proxy, we can just return it // if the managed entity is not a proxy, we can just return it
@ -533,9 +524,9 @@ public class DefaultMergeEventListener
if ( isSelfDirtinessTracker( entity ) && isSelfDirtinessTracker( target ) ) { if ( isSelfDirtinessTracker( entity ) && isSelfDirtinessTracker( target ) ) {
// clear, because setting the embedded attributes dirties them // clear, because setting the embedded attributes dirties them
final ManagedEntity managedEntity = asManagedEntity( target ); final ManagedEntity managedEntity = asManagedEntity( target );
final boolean useTracker = asManagedEntity( entity ).$$_hibernate_useTracker();
final SelfDirtinessTracker selfDirtinessTrackerTarget = asSelfDirtinessTracker( target ); final SelfDirtinessTracker selfDirtinessTrackerTarget = asSelfDirtinessTracker( target );
if ( !selfDirtinessTrackerTarget.$$_hibernate_hasDirtyAttributes() && !useTracker ) { if ( !selfDirtinessTrackerTarget.$$_hibernate_hasDirtyAttributes()
&& !asManagedEntity( entity ).$$_hibernate_useTracker() ) {
managedEntity.$$_hibernate_setUseTracker( false ); managedEntity.$$_hibernate_setUseTracker( false );
} }
else { else {
@ -560,10 +551,9 @@ public class DefaultMergeEventListener
// an entity to be merged during the same transaction // an entity to be merged during the same transaction
// (though during a separate operation) in which it was // (though during a separate operation) in which it was
// originally persisted/saved // originally persisted/saved
boolean changed = !persister.getVersionType().isSame( final boolean changed =
persister.getVersion( target ), !persister.getVersionType()
persister.getVersion( entity ) .isSame( persister.getVersion( target ), persister.getVersion( entity ) );
);
// TODO : perhaps we should additionally require that the incoming entity // TODO : perhaps we should additionally require that the incoming entity
// version be equivalent to the defined unsaved-value? // version be equivalent to the defined unsaved-value?
return changed && existsInDatabase( target, source, persister ); return changed && existsInDatabase( target, source, persister );
@ -577,7 +567,7 @@ public class DefaultMergeEventListener
final PersistenceContext persistenceContext = source.getPersistenceContextInternal(); final PersistenceContext persistenceContext = source.getPersistenceContextInternal();
EntityEntry entry = persistenceContext.getEntry( entity ); EntityEntry entry = persistenceContext.getEntry( entity );
if ( entry == null ) { if ( entry == null ) {
Object id = persister.getIdentifier( entity, source ); final Object id = persister.getIdentifier( entity, source );
if ( id != null ) { if ( id != null ) {
final EntityKey key = source.generateEntityKey( id, persister ); final EntityKey key = source.generateEntityKey( id, persister );
final Object managedEntity = persistenceContext.getEntity( key ); final Object managedEntity = persistenceContext.getEntity( key );
@ -606,7 +596,6 @@ public class DefaultMergeEventListener
target, target,
copyCache copyCache
); );
persister.setValues( target, copiedValues ); persister.setValues( target, copiedValues );
} }
} }
@ -618,9 +607,7 @@ public class DefaultMergeEventListener
final SessionImplementor source, final SessionImplementor source,
final MergeContext copyCache, final MergeContext copyCache,
final ForeignKeyDirection foreignKeyDirection) { final ForeignKeyDirection foreignKeyDirection) {
final Object[] copiedValues; final Object[] copiedValues;
if ( foreignKeyDirection == ForeignKeyDirection.TO_PARENT ) { if ( foreignKeyDirection == ForeignKeyDirection.TO_PARENT ) {
// this is the second pass through on a merge op, so here we limit the // this is the second pass through on a merge op, so here we limit the
// replacement to associations types (value types were already replaced // replacement to associations types (value types were already replaced
@ -646,7 +633,6 @@ public class DefaultMergeEventListener
foreignKeyDirection foreignKeyDirection
); );
} }
persister.setValues( target, copiedValues ); persister.setValues( target, copiedValues );
} }

View File

@ -17,7 +17,6 @@ import org.hibernate.cache.spi.access.EntityDataAccess;
import org.hibernate.cache.spi.access.SoftLock; import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.engine.internal.Cascade; import org.hibernate.engine.internal.Cascade;
import org.hibernate.engine.internal.CascadePoint; import org.hibernate.engine.internal.CascadePoint;
import org.hibernate.engine.spi.ActionQueue;
import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.CascadingActions;
import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistenceContext;
@ -32,14 +31,13 @@ import org.hibernate.loader.ast.spi.CascadingFetchProfile;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor; import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer; import org.hibernate.proxy.LazyInitializer;
import org.hibernate.type.CollectionType; import org.hibernate.type.CollectionType;
import org.hibernate.type.ComponentType; import org.hibernate.type.ComponentType;
import org.hibernate.type.CompositeType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import static org.hibernate.pretty.MessageHelper.infoString; import static org.hibernate.pretty.MessageHelper.infoString;
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
/** /**
* Defines the default refresh event listener used by hibernate for refreshing entities * Defines the default refresh event listener used by hibernate for refreshing entities
@ -71,14 +69,13 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
// cascade refresh and the refresh of the parent will take care of initializing the lazy // cascade refresh and the refresh of the parent will take care of initializing the lazy
// entity and setting the correct lock on it, this is needed only when the refresh is called directly on a lazy entity // entity and setting the correct lock on it, this is needed only when the refresh is called directly on a lazy entity
if ( refreshedAlready.isEmpty() ) { if ( refreshedAlready.isEmpty() ) {
final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( object ); final LazyInitializer lazyInitializer = extractLazyInitializer( object );
final EntityPersister persister; final EntityPersister persister;
if ( lazyInitializer != null ) { if ( lazyInitializer != null ) {
persister = source.getEntityPersister( lazyInitializer.getEntityName(), object ); persister = source.getEntityPersister( lazyInitializer.getEntityName(), object );
} }
else if ( !isTransient ) { else if ( !isTransient ) {
final EntityEntry entry = persistenceContext.getEntry( object ); persister = persistenceContext.getEntry( object ).getPersister();
persister = entry.getPersister();
} }
else { else {
persister = source.getEntityPersister( source.guessEntityName( object ), object ); persister = source.getEntityPersister( source.guessEntityName( object ), object );
@ -275,7 +272,7 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
if ( result != null ) { if ( result != null ) {
// apply `postRefreshLockMode`, if needed // apply `postRefreshLockMode`, if needed
if ( postRefreshLockMode != null ) { if ( postRefreshLockMode != null ) {
// if we get here, there was a previous entry, and we need to re-set its lock-mode // if we get here, there was a previous entry, and we need to reset its lock mode
// - however, the refresh operation actually creates a new entry, so get it // - however, the refresh operation actually creates a new entry, so get it
persistenceContext.getEntry( result ).setLockMode( postRefreshLockMode ); persistenceContext.getEntry( result ).setLockMode( postRefreshLockMode );
} }
@ -312,13 +309,12 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
private static void evictCachedCollections(Type[] types, Object id, EventSource source) private static void evictCachedCollections(Type[] types, Object id, EventSource source)
throws HibernateException { throws HibernateException {
final ActionQueue actionQueue = source.getActionQueue();
final SessionFactoryImplementor factory = source.getFactory(); final SessionFactoryImplementor factory = source.getFactory();
final MappingMetamodelImplementor metamodel = factory.getRuntimeMetamodels().getMappingMetamodel(); final MappingMetamodelImplementor metamodel = factory.getMappingMetamodel();
for ( Type type : types ) { for ( Type type : types ) {
if ( type instanceof CollectionType ) { if ( type instanceof CollectionType collectionType ) {
final String role = ((CollectionType) type).getRole(); final CollectionPersister collectionPersister =
final CollectionPersister collectionPersister = metamodel.getCollectionDescriptor( role ); metamodel.getCollectionDescriptor( collectionType.getRole() );
if ( collectionPersister.hasCache() ) { if ( collectionPersister.hasCache() ) {
final CollectionDataAccess cache = collectionPersister.getCacheAccessStrategy(); final CollectionDataAccess cache = collectionPersister.getCacheAccessStrategy();
final Object ck = cache.generateCacheKey( final Object ck = cache.generateCacheKey(
@ -329,12 +325,11 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
); );
final SoftLock lock = cache.lockItem( source, ck, null ); final SoftLock lock = cache.lockItem( source, ck, null );
cache.remove( source, ck ); cache.remove( source, ck );
actionQueue.registerProcess( (success, session) -> cache.unlockItem( session, ck, lock ) ); source.getActionQueue().registerProcess( (success, session) -> cache.unlockItem( session, ck, lock ) );
} }
} }
else if ( type instanceof ComponentType ) { else if ( type instanceof ComponentType compositeType ) {
// Only components can contain collections // Only components can contain collections
ComponentType compositeType = (ComponentType) type;
evictCachedCollections( compositeType.getSubtypes(), id, source ); evictCachedCollections( compositeType.getSubtypes(), id, source );
} }
} }