From f6ab5b517ef683ec138f08f8c74e1951e28489e5 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Mon, 6 Jan 2025 16:35:00 +0100 Subject: [PATCH] HHH-1914 Fix merge support for unmodifiable collections --- .../java/org/hibernate/type/CollectionType.java | 13 +++++++++++++ .../manytomany/UnmodifiableCollectionMergeTest.java | 2 -- .../java/org/hibernate/orm/test/ops/MergeTest.java | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/type/CollectionType.java b/hibernate-core/src/main/java/org/hibernate/type/CollectionType.java index aa0b53d1e6d1..f22d147e022a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/CollectionType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/CollectionType.java @@ -661,6 +661,19 @@ private Object replaceOriginal( final boolean wasClean = target instanceof PersistentCollection collection && !collection.isDirty(); + if ( target instanceof PersistentCollection existingPersistentCollection + && existingPersistentCollection.isDirectlyAccessible() ) { + // When a replace/merge is requested and the underlying collection is directly accessible, + // use a new persistent collection, to avoid potential issues + // like the underlying collection being unmodifiable and hence failing the element replacement + final CollectionPersister collectionPersister = getPersister( session ); + final Object key = existingPersistentCollection.getKey(); + final PersistentCollection persistentCollection = instantiate( session, collectionPersister, key ); + persistentCollection.initializeEmptyCollection( collectionPersister ); + persistentCollection.setSnapshot( key, existingPersistentCollection.getRole(), existingPersistentCollection.getStoredSnapshot() ); + session.getPersistenceContextInternal().addInitializedDetachedCollection( collectionPersister, persistentCollection ); + target = persistentCollection; + } //TODO: this is a little inefficient, don't need to do a whole // deep replaceElements() call replaceElements( result, target, owner, copyCache, session ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/manytomany/UnmodifiableCollectionMergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/manytomany/UnmodifiableCollectionMergeTest.java index 46435fb303a8..6bf8b79c6ecc 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/manytomany/UnmodifiableCollectionMergeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/manytomany/UnmodifiableCollectionMergeTest.java @@ -9,7 +9,6 @@ import java.util.Set; import org.hibernate.dialect.H2Dialect; -import org.hibernate.testing.orm.junit.FailureExpected; import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.SessionFactory; @@ -21,7 +20,6 @@ * @author Jan Schatteman */ @RequiresDialect( value = H2Dialect.class ) -@FailureExpected( jiraKey = "HHH-1914", reason = "throws an UnsupportedOperationException") @DomainModel( annotatedClasses = { Cat.class, Woman.class, Man.class diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/ops/MergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/ops/MergeTest.java index 61e410e75756..56eb087b3a92 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/ops/MergeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/ops/MergeTest.java @@ -623,7 +623,7 @@ public void testMergeManaged(SessionFactoryScope scope) { session.getTransaction().commit(); assertInsertCount( 1, scope ); - assertUpdateCount( 0, scope ); + assertUpdateCount( 1, scope ); assertThat( root.getChildren().size(), is( 1 ) ); assertTrue( root.getChildren().contains( mergedChild ) );