From cb3690466f20deccf17d8100d04b144c546e5720 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Tue, 15 Mar 2022 12:13:43 +0100 Subject: [PATCH] HHH-15098 Incorrect behavior when updating managed oneToMany collection on entity with naturalId --- .../internal/DefaultMergeEventListener.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java index 9b6fc42a94..d726cf4230 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java @@ -14,10 +14,12 @@ import org.hibernate.ObjectDeletedException; import org.hibernate.StaleObjectStateException; import org.hibernate.WrongClassException; import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; +import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.internal.Cascade; import org.hibernate.engine.internal.CascadePoint; 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.EntityKey; import org.hibernate.engine.spi.PersistenceContext; @@ -35,10 +37,13 @@ import org.hibernate.event.spi.MergeEventListener; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.loader.ast.spi.CascadingFetchProfile; +import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import org.hibernate.stat.spi.StatisticsImplementor; +import org.hibernate.type.CollectionType; +import org.hibernate.type.EntityType; import org.hibernate.type.ForeignKeyDirection; import org.hibernate.type.TypeHelper; @@ -246,6 +251,10 @@ public class DefaultMergeEventListener super.cascadeAfterSave( session, persister, entity, copyCache ); copyValues( persister, entity, copy, session, copyCache, ForeignKeyDirection.TO_PARENT ); + // saveTransientEntity has been called using a copy that contains empty collections (copyValues uses `ForeignKeyDirection.FROM_PARENT`) + // then the PC may contain a wrong collection snapshot, the CollectionVisitor realigns the collection snapshot values with the final copy + new CollectionVisitor( copy, id, session ).processEntityPropertyValues( persister.getPropertyValuesToInsert( copy, getMergeMap( copyCache ), session ), persister.getPropertyTypes() ); + event.setResult( copy ); if ( copy instanceof PersistentAttributeInterceptable ) { @@ -257,6 +266,35 @@ public class DefaultMergeEventListener } } + private class CollectionVisitor extends WrapVisitor { + CollectionVisitor(Object entity, Object id, EventSource session) { + super( entity, id, session ); + } + + @Override + Object processCollection(Object collection, CollectionType collectionType) throws HibernateException { + if ( collection instanceof PersistentCollection ) { + final PersistentCollection coll = (PersistentCollection) collection; + final CollectionPersister persister = getSession().getFactory() + .getRuntimeMetamodels() + .getMappingMetamodel() + .getCollectionDescriptor( collectionType.getRole() ); + final CollectionEntry collectionEntry = getSession().getPersistenceContextInternal() + .getCollectionEntries() + .get( coll ); + if ( !coll.equalsSnapshot( persister ) ) { + collectionEntry.resetStoredSnapshot( coll, coll.getSnapshot( persister ) ); + } + } + return null; + } + + @Override + Object processEntity(Object value, EntityType entityType) throws HibernateException { + return null; + } + } + private void saveTransientEntity( Object entity, String entityName,