From c7c57fc7ee3319f371ee9778d3974af94399c265 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Thu, 3 Jul 2014 11:21:26 -0700 Subject: [PATCH] HHH-9106 : Merging multiple representations of the same entity (tests using StrategySelector) (cherry picked from commit 368cdc1966b2018e03a3a5a0a6cf420a9939c398) --- ...MultipleEntityCopiesAllowedLoggedTest.java | 41 +++ ...eEntityCopiesAllowedOrphanDeleteTest.java} | 5 +- ...MergeMultipleEntityCopiesAllowedTest.java} | 4 +- .../MergeMultipleEntityCopiesCustomTest.java | 235 ++++++++++++++++++ ...eEntityCopiesDisallowedByDefaultTest.java} | 4 +- ...rgeMultipleEntityCopiesDisallowedTest.java | 45 ++++ ...MultipleEntityCopiesAllowedLoggedTest.java | 46 ++++ ...MergeMultipleEntityCopiesAllowedTest.java} | 7 +- .../MergeMultipleEntityCopiesCustomTest.java | 235 ++++++++++++++++++ ...eEntityCopiesDisallowedByDefaultTest.java} | 6 +- ...rgeMultipleEntityCopiesDisallowedTest.java | 47 ++++ 11 files changed, 662 insertions(+), 13 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesAllowedLoggedTest.java rename hibernate-core/src/test/java/org/hibernate/test/ops/{MergeMultipleEntityRepresentationsOrphanDeleteTest.java => MergeMultipleEntityCopiesAllowedOrphanDeleteTest.java} (99%) rename hibernate-core/src/test/java/org/hibernate/test/ops/{MergeMultipleEntityRepresentationsTest.java => MergeMultipleEntityCopiesAllowedTest.java} (99%) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesCustomTest.java rename hibernate-core/src/test/java/org/hibernate/test/ops/{MergeMultipleEntityRepresentationsNotAllowedTest.java => MergeMultipleEntityCopiesDisallowedByDefaultTest.java} (97%) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesDisallowedTest.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityCopiesAllowedLoggedTest.java rename hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/{MergeMultipleEntityRepresentationsAllowedTest.java => MergeMultipleEntityCopiesAllowedTest.java} (96%) create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityCopiesCustomTest.java rename hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/{MergeMultipleEntityRepresentationsNotAllowedTest.java => MergeMultipleEntityCopiesDisallowedByDefaultTest.java} (97%) create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityCopiesDisallowedTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesAllowedLoggedTest.java b/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesAllowedLoggedTest.java new file mode 100644 index 0000000000..d6ad101129 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesAllowedLoggedTest.java @@ -0,0 +1,41 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.ops; + +import org.hibernate.cfg.Configuration; + +/** + * Tests merging multiple detached representations of the same entity when explicitly allowed and logged. + * + * @author Gail Badner + */ +public class MergeMultipleEntityCopiesAllowedLoggedTest extends MergeMultipleEntityCopiesAllowedTest { + public void configure(Configuration cfg) { + super.configure( cfg ); + cfg.setProperty( + "hibernate.event.merge.entity_copy_observer", + "log" + ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityRepresentationsOrphanDeleteTest.java b/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesAllowedOrphanDeleteTest.java similarity index 99% rename from hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityRepresentationsOrphanDeleteTest.java rename to hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesAllowedOrphanDeleteTest.java index 0de5d18d1f..51cb5b6f76 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityRepresentationsOrphanDeleteTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesAllowedOrphanDeleteTest.java @@ -36,17 +36,16 @@ import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; /** - * Tests merging multiple detached representations of the same entity + * Tests merging multiple detached representations of the same entity (allowed) * where some associations include cascade="delete-orphan" * * @author Gail Badner */ -public class MergeMultipleEntityRepresentationsOrphanDeleteTest extends BaseCoreFunctionalTestCase { +public class MergeMultipleEntityCopiesAllowedOrphanDeleteTest extends BaseCoreFunctionalTestCase { public String[] getMappings() { return new String[] { diff --git a/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityRepresentationsTest.java b/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesAllowedTest.java similarity index 99% rename from hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityRepresentationsTest.java rename to hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesAllowedTest.java index 46dc19aaf1..d039226c89 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityRepresentationsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesAllowedTest.java @@ -44,11 +44,11 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** - * Tests merging multiple detached representations of the same entity. + * Tests merging multiple detached representations of the same entity when explicitly allowed. * * @author Gail Badner */ -public class MergeMultipleEntityRepresentationsTest extends BaseCoreFunctionalTestCase { +public class MergeMultipleEntityCopiesAllowedTest extends BaseCoreFunctionalTestCase { public String[] getMappings() { return new String[] { diff --git a/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesCustomTest.java b/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesCustomTest.java new file mode 100644 index 0000000000..1454308413 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesCustomTest.java @@ -0,0 +1,235 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.ops; + +import java.util.List; + +import org.junit.Test; + +import org.hibernate.Hibernate; +import org.hibernate.Session; +import org.hibernate.cfg.Configuration; +import org.hibernate.event.spi.EntityCopyObserver; +import org.hibernate.event.spi.EventSource; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +import static junit.framework.TestCase.fail; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * Tests merging multiple detached representations of the same entity using a custom EntityCopyObserver. + * + * @author Gail Badner + */ +@TestForIssue( jiraKey = "HHH-9106") +public class MergeMultipleEntityCopiesCustomTest extends BaseCoreFunctionalTestCase { + + public String[] getMappings() { + return new String[] { + "ops/Hoarder.hbm.xml" + }; + } + + public void configure(Configuration cfg) { + super.configure( cfg ); + cfg.setProperty( + "hibernate.event.merge.entity_copy_observer", + CustomEntityCopyObserver.class.getName() + ); + } + + @Test + public void testMergeMultipleEntityCopiesAllowed() { + Item item1 = new Item(); + item1.setName( "item1" ); + + Hoarder hoarder = new Hoarder(); + hoarder.setName( "joe" ); + + Session s = openSession(); + s.getTransaction().begin(); + s.persist( item1 ); + s.persist( hoarder ); + s.getTransaction().commit(); + s.close(); + + // Get another representation of the same Item. + + s = openSession(); + Item item1_1 = (Item) s.get( Item.class, item1.getId() ); + s.close(); + + // item1_1 and item1_2 are unmodified representations of the same persistent entity. + assertFalse( item1 == item1_1 ); + assertTrue( item1.equals( item1_1 ) ); + + // Update hoarder (detached) to references both representations. + hoarder.getItems().add( item1 ); + hoarder.setFavoriteItem( item1_1 ); + + s = openSession(); + s.getTransaction().begin(); + // the merge should succeed because it does not have Category copies. + // (CustomEntityCopyObserver does not allow Category copies; it does allow Item copies) + hoarder = (Hoarder) s.merge( hoarder ); + assertEquals( 1, hoarder.getItems().size() ); + assertSame( hoarder.getFavoriteItem(), hoarder.getItems().iterator().next() ); + assertEquals( item1.getId(), hoarder.getFavoriteItem().getId() ); + assertEquals( item1.getCategory(), hoarder.getFavoriteItem().getCategory() ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.getTransaction().begin(); + hoarder = (Hoarder) s.get( Hoarder.class, hoarder.getId() ); + assertEquals( 1, hoarder.getItems().size() ); + assertSame( hoarder.getFavoriteItem(), hoarder.getItems().iterator().next() ); + assertEquals( item1.getId(), hoarder.getFavoriteItem().getId() ); + assertEquals( item1.getCategory(), hoarder.getFavoriteItem().getCategory() ); + s.getTransaction().commit(); + s.close(); + + cleanup(); + } + + @Test + public void testMergeMultipleEntityCopiesAllowedAndDisallowed() { + Item item1 = new Item(); + item1.setName( "item1 name" ); + Category category = new Category(); + category.setName( "category" ); + item1.setCategory( category ); + category.setExampleItem( item1 ); + + Session s = openSession(); + s.getTransaction().begin(); + s.persist( item1 ); + s.getTransaction().commit(); + s.close(); + + // get another representation of item1 + s = openSession(); + s.getTransaction().begin(); + Item item1_1 = (Item) s.get( Item.class, item1.getId() ); + // make sure item1_1.category is initialized + Hibernate.initialize( item1_1.getCategory() ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.getTransaction().begin(); + Item item1Merged = (Item) s.merge( item1 ); + + item1Merged.setCategory( category ); + category.setExampleItem( item1_1 ); + + // now item1Merged is managed and it has a nested detached item + // and there is multiple managed/detached Category objects + try { + // the following should fail because multiple copies of Category objects is not allowed by + // CustomEntityCopyObserver + s.merge( item1Merged ); + fail( "should have failed because CustomEntityCopyObserver does not allow multiple copies of a Category. "); + } + catch (IllegalStateException ex ) { + // expected + } + finally { + s.getTransaction().rollback(); + } + s.close(); + + s = openSession(); + s.getTransaction().begin(); + item1 = (Item) s.get( Item.class, item1.getId() ); + assertEquals( category.getName(), item1.getCategory().getName() ); + assertSame( item1, item1.getCategory().getExampleItem() ); + s.getTransaction().commit(); + s.close(); + + cleanup(); + } + + @SuppressWarnings( {"unchecked"}) + private void cleanup() { + Session s = openSession(); + s.getTransaction().begin(); + + for ( Hoarder hoarder : (List) s.createQuery( "from Hoarder" ).list() ) { + hoarder.getItems().clear(); + s.delete( hoarder ); + } + + for ( Category category : (List) s.createQuery( "from Category" ).list() ) { + if ( category.getExampleItem() != null ) { + category.setExampleItem( null ); + s.delete( category ); + } + } + + for ( Item item : (List) s.createQuery( "from Item" ).list() ) { + item.setCategory( null ); + s.delete( item ); + } + + s.createQuery( "delete from Item" ).executeUpdate(); + + s.getTransaction().commit(); + s.close(); + } + + @Override + public Class[] getAnnotatedClasses() { + return new Class[] { + Category.class, + Hoarder.class, + Item.class + }; + } + + public static class CustomEntityCopyObserver implements EntityCopyObserver { + + @Override + public void entityCopyDetected(Object managedEntity, Object mergeEntity1, Object mergeEntity2, EventSource session) { + if ( Category.class.isInstance( managedEntity ) ) { + throw new IllegalStateException( + String.format( "Entity copies of type [%s] not allowed", Category.class.getName() ) + ); + } + } + + @Override + public void topLevelMergeComplete(EventSource session) { + } + + @Override + public void clear() { + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityRepresentationsNotAllowedTest.java b/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesDisallowedByDefaultTest.java similarity index 97% rename from hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityRepresentationsNotAllowedTest.java rename to hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesDisallowedByDefaultTest.java index 70a08a26fd..2ecb90d66e 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityRepresentationsNotAllowedTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesDisallowedByDefaultTest.java @@ -38,12 +38,12 @@ import static org.junit.Assert.assertTrue; /** * Tests merging multiple detached representations of the same entity using - * a the default MergeEventListener (that does not allow this). + * a the default (that does not allow this). * * @author Gail Badner */ @TestForIssue( jiraKey = "HHH-9106") -public class MergeMultipleEntityRepresentationsNotAllowedTest extends BaseCoreFunctionalTestCase { +public class MergeMultipleEntityCopiesDisallowedByDefaultTest extends BaseCoreFunctionalTestCase { public String[] getMappings() { return new String[] { diff --git a/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesDisallowedTest.java b/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesDisallowedTest.java new file mode 100644 index 0000000000..ba63fdf560 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/ops/MergeMultipleEntityCopiesDisallowedTest.java @@ -0,0 +1,45 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.ops; + +import org.hibernate.cfg.Configuration; +import org.hibernate.testing.TestForIssue; + + +/** + * Tests merging multiple detached representations of the same entity when explicitly disallowed. + * + * @author Gail Badner + */ +@TestForIssue( jiraKey = "HHH-9106") +public class MergeMultipleEntityCopiesDisallowedTest extends MergeMultipleEntityCopiesDisallowedByDefaultTest { + + public void configure(Configuration cfg) { + super.configure( cfg ); + cfg.setProperty( + "hibernate.event.merge.entity_copy_observer", + "disallow" + ); + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityCopiesAllowedLoggedTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityCopiesAllowedLoggedTest.java new file mode 100644 index 0000000000..9962d79614 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityCopiesAllowedLoggedTest.java @@ -0,0 +1,46 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.jpa.test.emops; + +import java.util.Map; + +import org.hibernate.testing.TestForIssue; + +/** + * Tests merging multiple detached representations of the same entity when it is explicitly allowed and logged. + * + * @author Gail Badner + */ +@TestForIssue( jiraKey = "HHH-9106") +public class MergeMultipleEntityCopiesAllowedLoggedTest extends MergeMultipleEntityCopiesAllowedTest { + + @SuppressWarnings( {"unchecked"}) + protected void addConfigOptions(Map options) { + super.addConfigOptions( options ); + options.put( + "hibernate.event.merge.entity_copy_observer", + "log" + ); + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityRepresentationsAllowedTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityCopiesAllowedTest.java similarity index 96% rename from hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityRepresentationsAllowedTest.java rename to hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityCopiesAllowedTest.java index 5e65bf9f05..fa1f86df83 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityRepresentationsAllowedTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityCopiesAllowedTest.java @@ -38,15 +38,16 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; /** - * Tests merging multiple detached representations of the same entity. + * Tests merging multiple detached representations of the same entity when it is explicitly allowed. * * @author Gail Badner */ @TestForIssue( jiraKey = "HHH-9106") -public class MergeMultipleEntityRepresentationsAllowedTest extends BaseEntityManagerFunctionalTestCase { +public class MergeMultipleEntityCopiesAllowedTest extends BaseEntityManagerFunctionalTestCase { + @SuppressWarnings( {"unchecked"}) protected void addConfigOptions(Map options) { - + super.addConfigOptions( options ); options.put( "hibernate.event.merge.entity_copy_observer", "allow" diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityCopiesCustomTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityCopiesCustomTest.java new file mode 100644 index 0000000000..e690bd7e6f --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityCopiesCustomTest.java @@ -0,0 +1,235 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.jpa.test.emops; + +import java.util.List; +import java.util.Map; + +import javax.persistence.EntityManager; + +import org.junit.Test; + +import org.hibernate.Hibernate; +import org.hibernate.event.spi.EntityCopyObserver; +import org.hibernate.event.spi.EventSource; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; +import org.hibernate.testing.TestForIssue; + +import static junit.framework.TestCase.fail; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * Tests merging multiple detached representations of the same entity using a custom EntityCopyObserver. + * + * @author Gail Badner + */ +@TestForIssue( jiraKey = "HHH-9106") +public class MergeMultipleEntityCopiesCustomTest extends BaseEntityManagerFunctionalTestCase { + + @SuppressWarnings( {"unchecked"}) + protected void addConfigOptions(Map options) { + super.addConfigOptions( options ); + options.put( + "hibernate.event.merge.entity_copy_observer", + CustomEntityCopyObserver.class.getName() + ); + } + + @Test + public void testMergeMultipleEntityCopiesAllowed() { + Item item1 = new Item(); + item1.setName( "item1" ); + + Hoarder hoarder = new Hoarder(); + hoarder.setName( "joe" ); + + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + em.persist( item1 ); + em.persist( hoarder ); + em.getTransaction().commit(); + em.close(); + + // Get another representation of the same Item from a different EntityManager. + + em = getOrCreateEntityManager(); + Item item1_1 = em.find( Item.class, item1.getId() ); + em.close(); + + // item1_1 and item1_2 are unmodified representations of the same persistent entity. + assertFalse( item1 == item1_1 ); + assertTrue( item1.equals( item1_1 ) ); + + // Update hoarder (detached) to references both representations. + hoarder.getItems().add( item1 ); + hoarder.setFavoriteItem( item1_1 ); + + em = getOrCreateEntityManager(); + em.getTransaction().begin(); + // the merge should succeed because it does not have Category copies. + // (CustomEntityCopyObserver does not allow Category copies; it does allow Item copies) + hoarder = em.merge( hoarder ); + assertEquals( 1, hoarder.getItems().size() ); + assertSame( hoarder.getFavoriteItem(), hoarder.getItems().iterator().next() ); + assertEquals( item1.getId(), hoarder.getFavoriteItem().getId() ); + assertEquals( item1.getCategory(), hoarder.getFavoriteItem().getCategory() ); + em.getTransaction().commit(); + em.close(); + + em = getOrCreateEntityManager(); + em.getTransaction().begin(); + hoarder = em.find( Hoarder.class, hoarder.getId() ); + assertEquals( 1, hoarder.getItems().size() ); + assertSame( hoarder.getFavoriteItem(), hoarder.getItems().iterator().next() ); + assertEquals( item1.getId(), hoarder.getFavoriteItem().getId() ); + assertEquals( item1.getCategory(), hoarder.getFavoriteItem().getCategory() ); + em.getTransaction().commit(); + em.close(); + + cleanup(); + } + + @Test + public void testMergeMultipleEntityCopiesAllowedAndDisallowed() { + Item item1 = new Item(); + item1.setName( "item1 name" ); + Category category = new Category(); + category.setName( "category" ); + item1.setCategory( category ); + category.setExampleItem( item1 ); + + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + em.persist( item1 ); + em.getTransaction().commit(); + em.close(); + + // get another representation of item1 + em = getOrCreateEntityManager(); + em.getTransaction().begin(); + Item item1_1 = em.find( Item.class, item1.getId() ); + // make sure item1_1.category is initialized + Hibernate.initialize( item1_1.getCategory() ); + em.getTransaction().commit(); + em.close(); + + em = getOrCreateEntityManager(); + em.getTransaction().begin(); + Item item1Merged = em.merge( item1 ); + + // make sure item1Merged.category is also managed + Hibernate.initialize( item1Merged.getCategory() ); + + item1Merged.setCategory( category ); + category.setExampleItem( item1_1 ); + + // now item1Merged is managed and it has a nested detached item + // and there is multiple managed/detached Category objects + try { + // the following should fail because multiple copies of Category objects is not allowed by + // CustomEntityCopyObserver + em.merge( item1Merged ); + fail( "should have failed because CustomEntityCopyObserver does not allow multiple copies of a Category. "); + } + catch (IllegalStateException ex ) { + // expected + } + finally { + em.getTransaction().rollback(); + } + em.close(); + + em = getOrCreateEntityManager(); + em.getTransaction().begin(); + item1 = em.find( Item.class, item1.getId() ); + assertEquals( category.getName(), item1.getCategory().getName() ); + assertSame( item1, item1.getCategory().getExampleItem() ); + em.getTransaction().commit(); + em.close(); + + cleanup(); + } + + @SuppressWarnings( {"unchecked"}) + private void cleanup() { + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + + for ( Hoarder hoarder : (List) em.createQuery( "from Hoarder" ).getResultList() ) { + hoarder.getItems().clear(); + em.remove( hoarder ); + } + + for ( Category category : (List) em.createQuery( "from Category" ).getResultList() ) { + if ( category.getExampleItem() != null ) { + category.setExampleItem( null ); + em.remove( category ); + } + } + + for ( Item item : (List) em.createQuery( "from Item" ).getResultList() ) { + item.setCategory( null ); + em.remove( item ); + } + + em.createQuery( "delete from Item" ).executeUpdate(); + + em.getTransaction().commit(); + em.close(); + } + + @Override + public Class[] getAnnotatedClasses() { + return new Class[] { + Category.class, + Hoarder.class, + Item.class + }; + } + + public static class CustomEntityCopyObserver implements EntityCopyObserver { + + @Override + public void entityCopyDetected(Object managedEntity, Object mergeEntity1, Object mergeEntity2, EventSource session) { + if ( Category.class.isInstance( managedEntity ) ) { + throw new IllegalStateException( + String.format( "Entity copies of type [%s] not allowed", Category.class.getName() ) + ); + } + } + + @Override + public void topLevelMergeComplete(EventSource session) { + + } + + @Override + public void clear() { + + } + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityRepresentationsNotAllowedTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityCopiesDisallowedByDefaultTest.java similarity index 97% rename from hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityRepresentationsNotAllowedTest.java rename to hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityCopiesDisallowedByDefaultTest.java index cc6560dbb4..3f06e482dd 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityRepresentationsNotAllowedTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityCopiesDisallowedByDefaultTest.java @@ -37,13 +37,13 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; /** - * Tests merging multiple detached representations of the same entity using - * a the default MergeEventListener (that does not allow this). + * Tests merging multiple detached representations of the same entity when + * not allowed by default. * * @author Gail Badner */ @TestForIssue( jiraKey = "HHH-9106") -public class MergeMultipleEntityRepresentationsNotAllowedTest extends BaseEntityManagerFunctionalTestCase { +public class MergeMultipleEntityCopiesDisallowedByDefaultTest extends BaseEntityManagerFunctionalTestCase { @Test public void testCascadeFromDetachedToNonDirtyRepresentations() { diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityCopiesDisallowedTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityCopiesDisallowedTest.java new file mode 100644 index 0000000000..fbab7cef86 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/emops/MergeMultipleEntityCopiesDisallowedTest.java @@ -0,0 +1,47 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.jpa.test.emops; + +import java.util.Map; + +import org.hibernate.testing.TestForIssue; + +/** + * Tests merging multiple detached representations of the same entity when + * explicitly disallowed. + * + * @author Gail Badner + */ +@TestForIssue( jiraKey = "HHH-9106") +public class MergeMultipleEntityCopiesDisallowedTest extends MergeMultipleEntityCopiesDisallowedByDefaultTest { + + @SuppressWarnings( {"unchecked"}) + protected void addConfigOptions(Map options) { + super.addConfigOptions( options ); + options.put( + "hibernate.event.merge.entity_copy_observer", + "disallow" + ); + } +}