From a83ff54671597625d77a574d923c3ba214664b8d Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Tue, 30 Aug 2022 17:14:50 +0200 Subject: [PATCH] HHH-14387 Alternative fix for deletion of bytecode lazy collections, by creating PersistentCollection for deletedState --- .../hibernate/engine/internal/Cascade.java | 31 ----------- .../internal/DefaultDeleteEventListener.java | 54 ++++++++++++++++--- 2 files changed, 46 insertions(+), 39 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/Cascade.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/Cascade.java index 575596db47..662c8f2c9d 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/Cascade.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/Cascade.java @@ -13,11 +13,9 @@ import java.util.List; import org.hibernate.HibernateException; import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor; -import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.CascadingAction; -import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.CollectionEntry; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.PersistenceContext; @@ -81,11 +79,6 @@ public final class Cascade { final EntityPersister persister, final Object parent, final T anything) throws HibernateException { - if ( action == CascadingActions.DELETE && cascadePoint == CascadePoint.AFTER_INSERT_BEFORE_DELETE ) { - // Before deleting an entity, ensure CollectionEntry objects for uninitialized lazy collections exist, - // otherwise these collections are not properly deleted and this leads to FK violations - registerUninitializedLazyCollectionEntries( eventSource, persister, parent ); - } if ( persister.hasCascades() || action.requiresNoCascadeChecking() ) { // performance opt final boolean traceEnabled = LOG.isTraceEnabled(); if ( traceEnabled ) { @@ -202,30 +195,6 @@ public final class Cascade { } } - private static void registerUninitializedLazyCollectionEntries(EventSource eventSource, EntityPersister persister, Object parent) { - if ( !persister.hasCollections() || !persister.hasUninitializedLazyProperties( parent ) ) { - return; - } - - final Type[] types = persister.getPropertyTypes(); - final String[] propertyNames = persister.getPropertyNames(); - final BytecodeEnhancementMetadata enhancementMetadata = persister.getBytecodeEnhancementMetadata(); - for ( int i = 0; i < types.length; i++) { - if ( types[i].isCollectionType() && !enhancementMetadata.isAttributeLoaded( parent, propertyNames[i] ) ) { - final CollectionType collectionType = (CollectionType) types[i]; - final CollectionPersister collectionDescriptor = persister.getFactory() - .getRuntimeMetamodels() - .getMappingMetamodel() - .getCollectionDescriptor( collectionType.getRole() ); - if ( collectionDescriptor.needsRemove() || collectionDescriptor.hasCache() ) { - final Object keyOfOwner = collectionType.getKeyOfOwner( parent, eventSource.getSession() ); - // This will make sure that a CollectionEntry exists - collectionType.getCollection( keyOfOwner, eventSource.getSession(), parent, false ); - } - } - } - } - /** * Cascade an action to the child or children */ diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDeleteEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDeleteEventListener.java index d7f32ec8ed..c7b5cb434f 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDeleteEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDeleteEventListener.java @@ -12,6 +12,8 @@ import org.hibernate.LockMode; import org.hibernate.TransientObjectException; import org.hibernate.action.internal.EntityDeleteAction; import org.hibernate.action.internal.OrphanRemovalAction; +import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; +import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata; import org.hibernate.classic.Lifecycle; import org.hibernate.engine.internal.Cascade; import org.hibernate.engine.internal.CascadePoint; @@ -32,8 +34,11 @@ import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.jpa.event.spi.CallbackRegistry; import org.hibernate.jpa.event.spi.CallbackRegistryConsumer; +import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.pretty.MessageHelper; +import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl; +import org.hibernate.type.CollectionType; import org.hibernate.type.Type; import org.hibernate.type.TypeHelper; @@ -251,7 +256,7 @@ public class DefaultDeleteEventListener implements DeleteEventListener, Callback ? persister.getValues(entity) //i.e. the entity came in from update() : entityEntry.getLoadedState(); - final Object[] deletedState = createDeletedState( persister, currentState, session ); + final Object[] deletedState = createDeletedState( persister, entity, currentState, session ); entityEntry.setDeletedState( deletedState ); session.getInterceptor().onDelete( @@ -313,13 +318,46 @@ public class DefaultDeleteEventListener implements DeleteEventListener, Callback //persistenceContext.removeDatabaseSnapshot(key); } - private Object[] createDeletedState(EntityPersister persister, Object[] currentState, EventSource session) { - Type[] propTypes = persister.getPropertyTypes(); - final Object[] deletedState = new Object[propTypes.length]; -// TypeFactory.deepCopy( currentState, propTypes, persister.getPropertyUpdateability(), deletedState, session ); - boolean[] copyability = new boolean[propTypes.length]; - java.util.Arrays.fill( copyability, true ); - TypeHelper.deepCopy( currentState, propTypes, copyability, deletedState, session ); + private Object[] createDeletedState( + EntityPersister persister, + Object parent, + Object[] currentState, + EventSource eventSource) { + final Type[] types = persister.getPropertyTypes(); + final Object[] deletedState = new Object[types.length]; + if ( !persister.hasCollections() || !persister.hasUninitializedLazyProperties( parent ) ) { + boolean[] copyability = new boolean[types.length]; + java.util.Arrays.fill( copyability, true ); + TypeHelper.deepCopy( currentState, types, copyability, deletedState, eventSource ); + return deletedState; + } + + final String[] propertyNames = persister.getPropertyNames(); + final BytecodeEnhancementMetadata enhancementMetadata = persister.getBytecodeEnhancementMetadata(); + for ( int i = 0; i < types.length; i++) { + if ( types[i].isCollectionType() && !enhancementMetadata.isAttributeLoaded( parent, propertyNames[i] ) ) { + final CollectionType collectionType = (CollectionType) types[i]; + final CollectionPersister collectionDescriptor = persister.getFactory() + .getRuntimeMetamodels() + .getMappingMetamodel() + .getCollectionDescriptor( collectionType.getRole() ); + if ( collectionDescriptor.needsRemove() || collectionDescriptor.hasCache() ) { + final Object keyOfOwner = collectionType.getKeyOfOwner( parent, eventSource.getSession() ); + // This will make sure that a CollectionEntry exists + deletedState[i] = collectionType.getCollection( keyOfOwner, eventSource.getSession(), parent, false ); + } + else { + deletedState[i] = currentState[i]; + } + } + else if ( currentState[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY + || currentState[i] == PropertyAccessStrategyBackRefImpl.UNKNOWN ) { + deletedState[i] = currentState[i]; + } + else { + deletedState[i] = types[i].deepCopy( currentState[i], eventSource.getFactory() ); + } + } return deletedState; }