diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/dereferenced/UnversionedCascadeDereferencedCollectionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/dereferenced/UnversionedCascadeDereferencedCollectionTest.java index c84dc138a3..1e2e07e401 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/dereferenced/UnversionedCascadeDereferencedCollectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/dereferenced/UnversionedCascadeDereferencedCollectionTest.java @@ -36,6 +36,7 @@ import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.Test; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -76,12 +77,9 @@ public class UnversionedCascadeDereferencedCollectionTest extends AbstractDerefe scope.inTransaction( session -> { - UnversionedCascadeOne one = (UnversionedCascadeOne) session.merge( unversionedCascadeOne ); + UnversionedCascadeOne one = session.merge( unversionedCascadeOne ); - // after merging, one.getManies() should still be null; - // the EntityEntry loaded state should contain a PersistentCollection though. - - assertNull( one.getManies() ); + assertThat( one.getManies().size() ).isEqualTo( 0 ); EntityEntry eeOne = getEntityEntry( session, one ); AbstractPersistentCollection maniesEEOneStateOrig = (AbstractPersistentCollection) eeOne.getLoadedValue( "manies" ); @@ -109,27 +107,7 @@ public class UnversionedCascadeDereferencedCollectionTest extends AbstractDerefe // Ensure the same EntityEntry is being used. assertSame( eeOne, getEntityEntry( session, one ) ); - // Ensure one.getManies() is still null. - assertNull( one.getManies() ); - - // Ensure CollectionEntry for maniesEEOneStateOrig is no longer in the PersistenceContext. - assertNull( getCollectionEntry( session, maniesEEOneStateOrig ) ); - - // Ensure the original CollectionEntry has role, persister, and key set to null. - assertNull( ceManiesOrig.getRole() ); - assertNull( ceManiesOrig.getLoadedPersister() ); - assertNull( ceManiesOrig.getKey() ); - - // Ensure the PersistentCollection (that was previously returned by eeOne.getLoadedState()) - // has key and role set to null. - assertNull( maniesEEOneStateOrig.getKey() ); - assertNull( maniesEEOneStateOrig.getRole() ); - - // Ensure eeOne.getLoadedState() returns null for collection after flush. - assertNull( eeOne.getLoadedValue( "manies" ) ); - - // Ensure the session in maniesEEOneStateOrig has been unset. - assertNull( maniesEEOneStateOrig.getSession() ); + assertThat( one.getManies().size() ).isEqualTo( 0 ); } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/dereferenced/UnversionedNoCascadeDereferencedCollectionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/dereferenced/UnversionedNoCascadeDereferencedCollectionTest.java index 04976c981c..0a4613f422 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/dereferenced/UnversionedNoCascadeDereferencedCollectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/dereferenced/UnversionedNoCascadeDereferencedCollectionTest.java @@ -36,6 +36,7 @@ import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.Test; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -74,12 +75,10 @@ public class UnversionedNoCascadeDereferencedCollectionTest extends AbstractDere scope.inTransaction( session -> { - UnversionedNoCascadeOne one = (UnversionedNoCascadeOne) session.merge( unversionedNoCascadeOne ); + UnversionedNoCascadeOne one = session.merge( unversionedNoCascadeOne ); - // after merging, one.getManies() should still be null; - // the EntityEntry loaded state should contain a PersistentCollection though. + assertThat( one.getManies().size() ).isEqualTo( 0 ); - assertNull( one.getManies() ); EntityEntry eeOne = getEntityEntry( session, one ); AbstractPersistentCollection maniesEEOneStateOrig = (AbstractPersistentCollection) eeOne.getLoadedValue( "manies" ); @@ -107,27 +106,8 @@ public class UnversionedNoCascadeDereferencedCollectionTest extends AbstractDere // Ensure the same EntityEntry is being used. assertSame( eeOne, getEntityEntry( session, one ) ); - // Ensure one.getManies() is still null. - assertNull( one.getManies() ); + assertThat( one.getManies().size() ).isEqualTo( 0 ); - // Ensure CollectionEntry for maniesEEOneStateOrig is no longer in the PersistenceContext. - assertNull( getCollectionEntry( session, maniesEEOneStateOrig ) ); - - // Ensure the original CollectionEntry has role, persister, and key set to null. - assertNull( ceManiesOrig.getRole() ); - assertNull( ceManiesOrig.getLoadedPersister() ); - assertNull( ceManiesOrig.getKey() ); - - // Ensure the PersistentCollection (that was previously returned by eeOne.getLoadedState()) - // has key and role set to null. - assertNull( maniesEEOneStateOrig.getKey() ); - assertNull( maniesEEOneStateOrig.getRole() ); - - // Ensure eeOne.getLoadedState() returns null for collection after flush. - assertNull( eeOne.getLoadedValue( "manies" ) ); - - // Ensure the session in maniesEEOneStateOrig has been unset. - assertNull( maniesEEOneStateOrig.getSession() ); } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/dereferenced/VersionedCascadeDereferencedCollectionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/dereferenced/VersionedCascadeDereferencedCollectionTest.java index afd7c628d4..2bd4742cc5 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/dereferenced/VersionedCascadeDereferencedCollectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/dereferenced/VersionedCascadeDereferencedCollectionTest.java @@ -36,6 +36,7 @@ import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.Test; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -76,12 +77,9 @@ public class VersionedCascadeDereferencedCollectionTest extends AbstractDerefere scope.inTransaction( session -> { - VersionedCascadeOne one = (VersionedCascadeOne) session.merge( versionedCascadeOne ); + VersionedCascadeOne one = session.merge( versionedCascadeOne ); - // after merging, one.getManies() should still be null; - // the EntityEntry loaded state should contain a PersistentCollection though. - - assertNull( one.getManies() ); + assertThat( one.getManies().size() ).isEqualTo( 0 ); EntityEntry eeOne = getEntityEntry( session, one ); AbstractPersistentCollection maniesEEOneStateOrig = (AbstractPersistentCollection) eeOne.getLoadedValue( "manies" ); @@ -109,28 +107,7 @@ public class VersionedCascadeDereferencedCollectionTest extends AbstractDerefere // Ensure the same EntityEntry is being used. assertSame( eeOne, getEntityEntry( session, one ) ); - // Ensure one.getManies() is still null. - assertNull( one.getManies() ); - - // Ensure CollectionEntry for maniesEEOneStateOrig is no longer in the PersistenceContext. - assertNull( getCollectionEntry( session, maniesEEOneStateOrig ) ); - - // Ensure the original CollectionEntry has role, persister, and key set to null. - assertNull( ceManiesOrig.getRole() ); - assertNull( ceManiesOrig.getLoadedPersister() ); - assertNull( ceManiesOrig.getKey() ); - - // Ensure the PersistentCollection (that was previously returned by eeOne.getLoadedState()) - // has key and role set to null. - assertNull( maniesEEOneStateOrig.getKey() ); - assertNull( maniesEEOneStateOrig.getRole() ); - - // Ensure eeOne.getLoadedState() returns null for collection after flush. - assertNull( eeOne.getLoadedValue( "manies" ) ); - - // Ensure the session in maniesEEOneStateOrig has been unset. - assertNull( maniesEEOneStateOrig.getSession() ); - + assertThat( one.getManies().size() ).isEqualTo( 0 ); } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/dereferenced/VersionedNoCascadeDereferencedCollectionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/dereferenced/VersionedNoCascadeDereferencedCollectionTest.java index 034974ba8e..aa9b45bcd9 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/dereferenced/VersionedNoCascadeDereferencedCollectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/dereferenced/VersionedNoCascadeDereferencedCollectionTest.java @@ -36,6 +36,7 @@ import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.Test; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -76,12 +77,9 @@ public class VersionedNoCascadeDereferencedCollectionTest extends AbstractDerefe scope.inTransaction( session -> { - VersionedNoCascadeOne one = (VersionedNoCascadeOne) session.merge( versionedNoCascadeOne ); + VersionedNoCascadeOne one = session.merge( versionedNoCascadeOne ); - // after merging, one.getManies() should still be null; - // the EntityEntry loaded state should contain a PersistentCollection though. - - assertNull( one.getManies() ); + assertThat( one.getManies().size() ).isEqualTo( 0 ); EntityEntry eeOne = getEntityEntry( session, one ); AbstractPersistentCollection maniesEEOneStateOrig = (AbstractPersistentCollection) eeOne.getLoadedValue( "manies" ); @@ -109,27 +107,7 @@ public class VersionedNoCascadeDereferencedCollectionTest extends AbstractDerefe // Ensure the same EntityEntry is being used. assertSame( eeOne, getEntityEntry( session, one ) ); - // Ensure one.getManies() is still null. - assertNull( one.getManies() ); - - // Ensure CollectionEntry for maniesEEOneStateOrig is no longer in the PersistenceContext. - assertNull( getCollectionEntry( session, maniesEEOneStateOrig ) ); - - // Ensure the original CollectionEntry has role, persister, and key set to null. - assertNull( ceManiesOrig.getRole() ); - assertNull( ceManiesOrig.getLoadedPersister() ); - assertNull( ceManiesOrig.getKey() ); - - // Ensure the PersistentCollection (that was previously returned by eeOne.getLoadedState()) - // has key and role set to null. - assertNull( maniesEEOneStateOrig.getKey() ); - assertNull( maniesEEOneStateOrig.getRole() ); - - // Ensure eeOne.getLoadedState() returns null for collection after flush. - assertNull( eeOne.getLoadedValue( "manies" ) ); - - // Ensure the session in maniesEEOneStateOrig has been unset. - assertNull( maniesEEOneStateOrig.getSession() ); + assertThat( one.getManies().size() ).isEqualTo( 0 ); } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/merge/MergeBidirectionalCascadeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/merge/MergeBidirectionalCascadeTest.java new file mode 100644 index 0000000000..edd9322aac --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/merge/MergeBidirectionalCascadeTest.java @@ -0,0 +1,218 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.jpa.orphan.onetomany.merge; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; + +import static org.assertj.core.api.Assertions.assertThat; + +@DomainModel( + annotatedClasses = { + MergeBidirectionalCascadeTest.Parent.class, + MergeBidirectionalCascadeTest.Child.class, + } +) +@SessionFactory +@JiraKey("HHH-18389") +public class MergeBidirectionalCascadeTest { + + private static final Long ID_PARENT_WITHOUT_CHILDREN = 1L; + private static final Long ID_PARENT_WITH_CHILDREN = 2L; + + @BeforeEach + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITHOUT_CHILDREN, "old name" ); + session.persist( parent ); + + Parent parent2 = new Parent( ID_PARENT_WITH_CHILDREN, "old name" ); + Child child = new Child( 2l, "Child" ); + parent2.addChild( child ); + + session.persist( child ); + session.persist( parent2 ); + } + ); + } + + @AfterEach + private void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createMutationQuery( "delete from Child" ).executeUpdate(); + session.createMutationQuery( "delete from Parent" ).executeUpdate(); + } + ); + } + + @Test + public void testMergeParentWihoutChildren(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITHOUT_CHILDREN, "new name" ); + Parent merged = session.merge( parent ); + assertThat( merged.getName() ).isEqualTo( "new name" ); + + } + ); + } + + @Test + public void testMergeParentWithChildren(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITH_CHILDREN, "new name" ); + Child child = new Child( 2l, "Child" ); + parent.addChild( child ); + Parent merged = session.merge( parent ); + assertThat( merged.getName() ).isEqualTo( "new name" ); + + } + ); + scope.inTransaction( + session -> { + Parent parent = session.get( Parent.class, ID_PARENT_WITH_CHILDREN ); + assertThat( parent.getChildren().size() ).isEqualTo( 1 ); + + } + ); + } + + @Test + public void testMergeParentWithChildren2(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITH_CHILDREN, "new name" ); + Parent merged = session.merge( parent ); + + } + ); + scope.inTransaction( + session -> { + Parent parent = session.get( Parent.class, ID_PARENT_WITH_CHILDREN ); + assertThat( parent.getName() ).isEqualTo( "new name" ); + assertThat( parent.getChildren().size() ).isEqualTo( 1 ); + + List children = session.createQuery( "Select c from Child c", Child.class ).list(); + assertThat( children.size() ).isEqualTo( 1 ); + } + ); + } + + @Entity(name = "Parent") + public static class Parent { + + @Id + private Long id; + + private String name; + + @OneToMany(cascade = CascadeType.MERGE, mappedBy = "parent") + private List children; + + public Parent() { + } + + public Parent(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + + public void addChild(Child child) { + if ( children == null ) { + children = new ArrayList(); + } + children.add( child ); + child.setParent( this ); + } + } + + @Entity(name = "Child") + public static class Child { + + @Id + private Long id; + + private String name; + + public Child() { + } + + public Child(Long id, String name) { + this.id = id; + this.name = name; + } + + @ManyToOne + @JoinColumn + private Parent parent; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Parent getParent() { + return parent; + } + + public void setParent(Parent parent) { + this.parent = parent; + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/merge/MergeCascadeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/merge/MergeCascadeTest.java new file mode 100644 index 0000000000..5a3a603dc2 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/merge/MergeCascadeTest.java @@ -0,0 +1,200 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.jpa.orphan.onetomany.merge; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; + +import static org.assertj.core.api.Assertions.assertThat; + +@DomainModel( + annotatedClasses = { + MergeCascadeTest.Parent.class, + MergeCascadeTest.Child.class, + } +) +@SessionFactory +@JiraKey("HHH-18389") +public class MergeCascadeTest { + + private static final Long ID_PARENT_WITHOUT_CHILDREN = 1L; + private static final Long ID_PARENT_WITH_CHILDREN = 2L; + + @BeforeEach + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITHOUT_CHILDREN, "old name" ); + session.persist( parent ); + + Parent parent2 = new Parent( ID_PARENT_WITH_CHILDREN, "old name" ); + Child child = new Child( 2l, "Child" ); + parent2.addChild( child ); + + session.persist( child ); + session.persist( parent2 ); + } + ); + } + + @AfterEach + private void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createMutationQuery( "delete from Parent" ).executeUpdate(); + session.createMutationQuery( "delete from Child" ).executeUpdate(); + } + ); + } + + @Test + public void testMergeParentWihoutChildren(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITHOUT_CHILDREN, "new name" ); + Parent merged = session.merge( parent ); + assertThat( merged.getName() ).isEqualTo( "new name" ); + } + ); + } + + @Test + public void testMergeParentWithChildren(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITH_CHILDREN, "new name" ); + Child child = new Child( 2l, "Child" ); + parent.addChild( child ); + Parent merged = session.merge( parent ); + assertThat( merged.getName() ).isEqualTo( "new name" ); + } + ); + scope.inTransaction( + session -> { + Parent parent = session.get( Parent.class, ID_PARENT_WITH_CHILDREN ); + assertThat( parent.getChildren().size() ).isEqualTo( 1 ); + } + ); + } + + @Test + public void testMergeParentWithChildren2(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITH_CHILDREN, "new name" ); + session.merge( parent ); + } + ); + scope.inTransaction( + session -> { + Parent parent = session.get( Parent.class, ID_PARENT_WITH_CHILDREN ); + assertThat( parent.getName() ).isEqualTo( "new name" ); + assertThat( parent.getChildren().size() ).isEqualTo( 0 ); + + List children = session.createQuery( "Select c from Child c", Child.class ).list(); + assertThat( children.size() ).isEqualTo( 1 ); + } + ); + } + + @Entity(name = "Parent") + public static class Parent { + + @Id + private Long id; + + private String name; + + @OneToMany(cascade = CascadeType.MERGE) + private List children; + + public Parent() { + } + + public Parent(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + + public void addChild(Child child) { + if ( children == null ) { + children = new ArrayList(); + } + children.add( child ); + } + } + + @Entity(name = "Child") + public static class Child { + + @Id + private Long id; + + private String name; + + public Child() { + } + + public Child(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/merge/MergeOrphanRemovalBidirectionalTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/merge/MergeOrphanRemovalBidirectionalTest.java new file mode 100644 index 0000000000..89aac97c0f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/merge/MergeOrphanRemovalBidirectionalTest.java @@ -0,0 +1,217 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.jpa.orphan.onetomany.merge; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; + +import static org.assertj.core.api.Assertions.assertThat; + +@DomainModel( + annotatedClasses = { + MergeOrphanRemovalBidirectionalTest.Parent.class, + MergeOrphanRemovalBidirectionalTest.Child.class, + } +) +@SessionFactory +@JiraKey("HHH-18389") +public class MergeOrphanRemovalBidirectionalTest { + + private static final Long ID_PARENT_WITHOUT_CHILDREN = 1L; + private static final Long ID_PARENT_WITH_CHILDREN = 2L; + + @BeforeEach + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITHOUT_CHILDREN, "old name" ); + session.persist( parent ); + + Parent parent2 = new Parent( ID_PARENT_WITH_CHILDREN, "old name" ); + Child child = new Child( 2l, "Child" ); + parent2.addChild( child ); + + session.persist( child ); + session.persist( parent2 ); + } + ); + } + + @AfterEach + private void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createMutationQuery( "delete from Child" ).executeUpdate(); + session.createMutationQuery( "delete from Parent" ).executeUpdate(); + } + ); + } + + @Test + public void testMergeParentWihoutChildren(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITHOUT_CHILDREN, "new name" ); + Parent merged = session.merge( parent ); + assertThat( merged.getName() ).isEqualTo( "new name" ); + + } + ); + } + + @Test + public void testMergeParentWithChildren(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITH_CHILDREN, "new name" ); + Child child = new Child( 2l, "Child" ); + parent.addChild( child ); + Parent merged = session.merge( parent ); + assertThat( merged.getName() ).isEqualTo( "new name" ); + + } + ); + scope.inTransaction( + session -> { + Parent parent = session.get( Parent.class, ID_PARENT_WITH_CHILDREN ); + assertThat( parent.getChildren().size() ).isEqualTo( 1 ); + + } + ); + } + + @Test + public void testMergeParentWithChildren2(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITH_CHILDREN, "new name" ); + Parent merged = session.merge( parent ); + + } + ); + scope.inTransaction( + session -> { + Parent parent = session.get( Parent.class, ID_PARENT_WITH_CHILDREN ); + assertThat( parent.getName() ).isEqualTo( "new name" ); + assertThat( parent.getChildren().size() ).isEqualTo( 0 ); + + List children = session.createQuery( "Select c from Child c", Child.class ).list(); + assertThat( children.size() ).isEqualTo( 0 ); + } + ); + } + + @Entity(name = "Parent") + public static class Parent { + + @Id + private Long id; + + private String name; + + @OneToMany(orphanRemoval = true, mappedBy = "parent") + private List children; + + public Parent() { + } + + public Parent(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + + public void addChild(Child child) { + if ( children == null ) { + children = new ArrayList(); + } + children.add( child ); + child.setParent( this ); + } + } + + @Entity(name = "Child") + public static class Child { + + @Id + private Long id; + + private String name; + + public Child() { + } + + public Child(Long id, String name) { + this.id = id; + this.name = name; + } + + @ManyToOne + @JoinColumn + private Parent parent; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Parent getParent() { + return parent; + } + + public void setParent(Parent parent) { + this.parent = parent; + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/merge/MergeOrphanRemovalTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/merge/MergeOrphanRemovalTest.java new file mode 100644 index 0000000000..88197e7438 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/merge/MergeOrphanRemovalTest.java @@ -0,0 +1,229 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.jpa.orphan.onetomany.merge; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; + +import static org.assertj.core.api.Assertions.assertThat; + +@DomainModel( + annotatedClasses = { + MergeOrphanRemovalTest.Parent.class, + MergeOrphanRemovalTest.Child.class, + } +) +@SessionFactory +@JiraKey("HHH-18389") +public class MergeOrphanRemovalTest { + + private static final Long ID_PARENT_WITHOUT_CHILDREN = 1L; + private static final Long ID_PARENT_WITH_CHILDREN = 2L; + + @BeforeEach + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITHOUT_CHILDREN, "old name" ); + session.persist( parent ); + + Parent parent2 = new Parent( ID_PARENT_WITH_CHILDREN, "old name" ); + Child child = new Child( 2l, "Child" ); + parent2.addChild( child ); + + session.persist( child ); + session.persist( parent2 ); + } + ); + } + + @AfterEach + private void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createMutationQuery( "delete from Parent" ).executeUpdate(); + session.createMutationQuery( "delete from Child" ).executeUpdate(); + } + ); + } + + @Test + public void testMergeParentWihoutChildren(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITHOUT_CHILDREN, "new name" ); + Parent merged = session.merge( parent ); + assertThat( merged.getName() ).isEqualTo( "new name" ); + + } + ); + } + + @Test + public void testMergeParentWithChildren(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITH_CHILDREN, "new name" ); + Child child = new Child( 2l, "Child" ); + parent.addChild( child ); + Parent merged = session.merge( parent ); + assertThat( merged.getName() ).isEqualTo( "new name" ); + + } + ); + scope.inTransaction( + session -> { + Parent parent = session.get( Parent.class, ID_PARENT_WITH_CHILDREN ); + assertThat( parent.getChildren().size() ).isEqualTo( 1 ); + + } + ); + } + + @Test + public void testMergeParentWithChildren2(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITH_CHILDREN, "new name" ); + Parent merged = session.merge( parent ); + + } + ); + scope.inTransaction( + session -> { + Parent parent = session.get( Parent.class, ID_PARENT_WITH_CHILDREN ); + assertThat( parent.getName() ).isEqualTo( "new name" ); + assertThat( parent.getChildren().size() ).isEqualTo( 0 ); + + List children = session.createQuery( "Select c from Child c", Child.class ).list(); + assertThat( children.size() ).isEqualTo( 0 ); + } + ); + } + + @Test + public void testMergeParentWithChildren3(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITH_CHILDREN, "new name" ); + Child child = new Child( 3l, "Child2" ); + parent.addChild( child ); + session.persist( child ); + Parent merged = session.merge( parent ); + + } + ); + scope.inTransaction( + session -> { + Parent parent = session.get( Parent.class, ID_PARENT_WITH_CHILDREN ); + assertThat( parent.getName() ).isEqualTo( "new name" ); + assertThat( parent.getChildren().size() ).isEqualTo( 1 ); + assertThat( parent.getChildren().get( 0 ).getName() ).isEqualTo( "Child2" ); + + List children = session.createQuery( "Select c from Child c", Child.class ).list(); + assertThat( children.size() ).isEqualTo( 1 ); + assertThat( children.get( 0 ).getName() ).isEqualTo( "Child2" ); + } + ); + } + + @Entity(name = "Parent") + public static class Parent { + + @Id + private Long id; + + private String name; + + @OneToMany(orphanRemoval = true) + private List children; + + public Parent() { + } + + public Parent(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + + public void addChild(Child child) { + if ( children == null ) { + children = new ArrayList(); + } + children.add( child ); + } + } + + @Entity(name = "Child") + public static class Child { + + @Id + private Long id; + + private String name; + + public Child() { + } + + public Child(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/merge/MergeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/merge/MergeTest.java new file mode 100644 index 0000000000..1e9304804a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/orphan/onetomany/merge/MergeTest.java @@ -0,0 +1,200 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.jpa.orphan.onetomany.merge; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; + +import static org.assertj.core.api.Assertions.assertThat; + +@DomainModel( + annotatedClasses = { + MergeTest.Parent.class, + MergeTest.Child.class, + } +) +@SessionFactory +@JiraKey("HHH-18389") +public class MergeTest { + + private static final Long ID_PARENT_WITHOUT_CHILDREN = 1L; + private static final Long ID_PARENT_WITH_CHILDREN = 2L; + + @BeforeEach + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITHOUT_CHILDREN, "old name" ); + session.persist( parent ); + + Parent parent2 = new Parent( ID_PARENT_WITH_CHILDREN, "old name" ); + Child child = new Child( 2l, "Child" ); + parent2.addChild( child ); + + session.persist( child ); + session.persist( parent2 ); + } + ); + } + + @AfterEach + private void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createMutationQuery( "delete from Parent" ).executeUpdate(); + session.createMutationQuery( "delete from Child" ).executeUpdate(); + } + ); + } + + @Test + public void testMergeParentWihoutChildren(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITHOUT_CHILDREN, "new name" ); + Parent merged = session.merge( parent ); + assertThat( merged.getName() ).isEqualTo( "new name" ); + } + ); + } + + @Test + public void testMergeParentWithChildren(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITH_CHILDREN, "new name" ); + Child child = new Child( 2l, "Child" ); + parent.addChild( child ); + Parent merged = session.merge( parent ); + assertThat( merged.getName() ).isEqualTo( "new name" ); + } + ); + scope.inTransaction( + session -> { + Parent parent = session.get( Parent.class, ID_PARENT_WITH_CHILDREN ); + assertThat( parent.getChildren().size() ).isEqualTo( 1 ); + } + ); + } + + @Test + public void testMergeParentWithChildren2(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Parent parent = new Parent( ID_PARENT_WITH_CHILDREN, "new name" ); + session.merge( parent ); + } + ); + scope.inTransaction( + session -> { + Parent parent = session.get( Parent.class, ID_PARENT_WITH_CHILDREN ); + assertThat( parent.getName() ).isEqualTo( "new name" ); + assertThat( parent.getChildren().size() ).isEqualTo( 0 ); + + List children = session.createQuery( "Select c from Child c", Child.class ).list(); + assertThat( children.size() ).isEqualTo( 1 ); + } + ); + } + + + @Entity(name = "Parent") + public static class Parent { + + @Id + private Long id; + + private String name; + + @OneToMany + private List children; + + public Parent() { + } + + public Parent(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + + public void addChild(Child child) { + if ( children == null ) { + children = new ArrayList(); + } + children.add( child ); + } + } + + @Entity(name = "Child") + public static class Child { + + @Id + private Long id; + + private String name; + + public Child() { + } + + public Child(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + } + +}