From 75865d7990052fd020176a69b2cc8ef4b289762d Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Sat, 11 Mar 2023 14:01:00 +0100 Subject: [PATCH 1/2] Ignoring EagerUniqueKeyTest#testMergeReference and add one with persist This is an unusual use case where the association is with an entity column instead of the identifier. It's a specific case and the current mapping, as it is, fails using regular Hibernate ORM anyway. I've added a test with the persist that seems to work as expected. --- .../reactive/EagerUniqueKeyTest.java | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EagerUniqueKeyTest.java b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EagerUniqueKeyTest.java index 3e6416055..78e52a5a2 100644 --- a/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EagerUniqueKeyTest.java +++ b/hibernate-reactive-core/src/test/java/org/hibernate/reactive/EagerUniqueKeyTest.java @@ -12,6 +12,7 @@ import org.hibernate.annotations.FetchMode; import org.junit.After; +import org.junit.Ignore; import org.junit.Test; import jakarta.persistence.CascadeType; @@ -22,6 +23,7 @@ import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; + import java.io.Serializable; import java.util.Collection; import java.util.List; @@ -65,20 +67,38 @@ public void testMergeDetached(TestContext context) { .withTransaction( session -> session.merge( new Foo( bar ) ) ) ) .thenCompose( result -> getSessionFactory() .withTransaction( session -> session.fetch( result.getBar() ) - .thenAccept( b -> context.assertEquals( "unique2", b.getKey() ) ) - ) ) ); + .thenAccept( b -> context.assertEquals( "unique2", b.getKey() ) ) + ) ) ); } + @Ignore // This also fails in ORM @Test public void testMergeReference(TestContext context) { Bar bar = new Bar( "unique3" ); test( context, getSessionFactory() .withTransaction( session -> session.persist( bar ) ) .thenCompose( i -> getSessionFactory() - .withTransaction( session -> session.merge( new Foo( session.getReference( Bar.class, bar.id ) )) ) ) + .withTransaction( session -> session + .merge( new Foo( session.getReference( Bar.class, bar.getId() ) ) ) ) ) .thenCompose( result -> getSessionFactory().withTransaction( session -> session.fetch( result.getBar() ) .thenAccept( b -> context.assertEquals( "unique3", b.getKey() ) ) - ) ) ); + ) ) + ); + } + + @Test + public void testPersistWithReference(TestContext context) { + Bar bar = new Bar( "uniquePersist" ); + test( context, getSessionFactory() + .withTransaction( session -> session.persist( bar ) ) + .thenCompose( i -> getSessionFactory() + .withTransaction( session -> { + Foo foo = new Foo( session.getReference( Bar.class, bar.getId() ) ); + return session.persist( foo ).thenApply( v -> foo ); + } ) ) + .thenCompose( result -> getSessionFactory().withTransaction( session -> session.fetch( result.getBar() ) ) ) + .thenAccept( b -> context.assertEquals( "uniquePersist", b.getKey() ) ) + ); } @Entity(name = "Foo") @@ -90,8 +110,8 @@ static class Foo { Foo() { } - @GeneratedValue @Id + @GeneratedValue long id; @ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER) @Fetch(FetchMode.JOIN) @@ -124,8 +144,8 @@ static class Bar implements Serializable { Bar() { } - @GeneratedValue @Id + @GeneratedValue long id; @Column(name = "nat_key", unique = true) String key; From e765b0a213ab049c2e8e1c16ffc5dbbddd4c22c8 Mon Sep 17 00:00:00 2001 From: Davide D'Alto Date: Sat, 11 Mar 2023 14:04:27 +0100 Subject: [PATCH 2/2] Minor refactoring in DefaultReactiveMergeEventListener Extract copyEntity method from the existing code --- .../DefaultReactiveMergeEventListener.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveMergeEventListener.java b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveMergeEventListener.java index 6598241a3..1995f47ef 100644 --- a/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveMergeEventListener.java +++ b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/DefaultReactiveMergeEventListener.java @@ -233,31 +233,17 @@ protected CompletionStage entityIsPersistent(MergeEvent event, MergeContex } protected CompletionStage entityIsTransient(MergeEvent event, MergeContext copyCache) { - LOG.trace( "Merging transient instance" ); final Object entity = event.getEntity(); final EventSource session = event.getSession(); - final String entityName = event.getEntityName(); final EntityPersister persister = session.getEntityPersister( entityName, entity ); - final Object id = persister.hasIdentifierProperty() ? persister.getIdentifier( entity, session ) : null; - final Object copy; - final Object existingCopy = copyCache.get( entity ); - if ( existingCopy != null ) { - persister.setIdentifier( copyCache.get( entity ), id, session ); - copy = existingCopy; - } - else { - copy = session.instantiate( persister, id ); - - //before cascade! - copyCache.put( entity, copy, true ); - } + final Object copy = copyEntity( copyCache, entity, session, persister, id ); // cascade first, so that all unsaved objects get their // copy created before we actually copy @@ -286,6 +272,20 @@ protected CompletionStage entityIsTransient(MergeEvent event, MergeContext }); } + private static Object copyEntity(MergeContext copyCache, Object entity, EventSource session, EntityPersister persister, Object id) { + final Object existingCopy = copyCache.get( entity ); + if ( existingCopy != null ) { + persister.setIdentifier( copyCache.get( entity ), id, session ); + return existingCopy; + } + else { + final Object copy = session.instantiate( persister, id ); + //before cascade! + copyCache.put( entity, copy, true ); + return copy; + } + } + private static class CollectionVisitor extends WrapVisitor { CollectionVisitor(Object entity, Object id, EventSource session) { super( entity, id, session );