From fd760dcf2e0aacbf3d3e17e79fa2f2441c5ab4eb Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Wed, 15 Jul 2015 12:37:08 -0700 Subject: [PATCH] HHH-5255 : Merge detached entity failed when the instrumented lazy property is initialized (cherry picked from commit c00d4609efa61b32c2cb470aa5785284870a71b6) --- .../java/org/hibernate/type/TypeHelper.java | 16 +++ .../instrument/buildtime/InstrumentTest.java | 12 +- .../TestLazyBasicFieldAccessExecutable.java | 122 ++++++++++++++++++ ...TestLazyBasicPropertyAccessExecutable.java | 122 ++++++++++++++++++ ...TestLazyBasicPropertyUpdateExecutable.java | 41 ------ .../domain/DocumentsPropAccess.hbm.xml | 78 +++++++++++ ...sformingClassLoaderInstrumentTestCase.java | 12 +- 7 files changed, 357 insertions(+), 46 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/instrument/cases/TestLazyBasicFieldAccessExecutable.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/instrument/cases/TestLazyBasicPropertyAccessExecutable.java delete mode 100644 hibernate-core/src/test/java/org/hibernate/test/instrument/cases/TestLazyBasicPropertyUpdateExecutable.java create mode 100755 hibernate-core/src/test/java/org/hibernate/test/instrument/domain/DocumentsPropAccess.hbm.xml diff --git a/hibernate-core/src/main/java/org/hibernate/type/TypeHelper.java b/hibernate-core/src/main/java/org/hibernate/type/TypeHelper.java index 126e792d2a..fdb91b5b33 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/TypeHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/type/TypeHelper.java @@ -173,6 +173,22 @@ public class TypeHelper { || original[i] == BackrefPropertyAccessor.UNKNOWN ) { copied[i] = target[i]; } + else if ( target[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY ) { + // Should be no need to check for target[i] == PropertyAccessStrategyBackRefImpl.UNKNOWN + // because PropertyAccessStrategyBackRefImpl.get( object ) returns + // PropertyAccessStrategyBackRefImpl.UNKNOWN, so target[i] == original[i]. + // + // We know from above that original[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY && + // original[i] != PropertyAccessStrategyBackRefImpl.UNKNOWN; + // This is a case where the entity being merged has a lazy property + // that has been initialized. Copy the initialized value from original. + if ( types[i].isMutable() ) { + copied[i] = types[i].deepCopy( original[i], session.getFactory() ); + } + else { + copied[i] = original[i]; + } + } else { copied[i] = types[i].replace( original[i], target[i], session, owner, copyCache ); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/instrument/buildtime/InstrumentTest.java b/hibernate-core/src/test/java/org/hibernate/test/instrument/buildtime/InstrumentTest.java index 68626d75ae..cfe0a9f9b6 100755 --- a/hibernate-core/src/test/java/org/hibernate/test/instrument/buildtime/InstrumentTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/instrument/buildtime/InstrumentTest.java @@ -31,7 +31,8 @@ import org.hibernate.test.instrument.cases.TestDirtyCheckExecutable; import org.hibernate.test.instrument.cases.TestFetchAllExecutable; import org.hibernate.test.instrument.cases.TestInjectFieldInterceptorExecutable; import org.hibernate.test.instrument.cases.TestIsPropertyInitializedExecutable; -import org.hibernate.test.instrument.cases.TestLazyBasicPropertyUpdateExecutable; +import org.hibernate.test.instrument.cases.TestLazyBasicFieldAccessExecutable; +import org.hibernate.test.instrument.cases.TestLazyBasicPropertyAccessExecutable; import org.hibernate.test.instrument.cases.TestLazyExecutable; import org.hibernate.test.instrument.cases.TestLazyManyToOneExecutable; import org.hibernate.test.instrument.cases.TestLazyPropertyCustomTypeExecutable; @@ -90,8 +91,13 @@ public class InstrumentTest extends BaseUnitTestCase { } @Test - public void testLazyBasicPropertyUpdate() throws Exception { - execute( new TestLazyBasicPropertyUpdateExecutable() ); + public void testLazyBasicFieldAccess() throws Exception { + execute( new TestLazyBasicFieldAccessExecutable() ); + } + + @Test + public void testLazyBasicPropertyAccess() throws Exception { + execute( new TestLazyBasicPropertyAccessExecutable() ); } @Test diff --git a/hibernate-core/src/test/java/org/hibernate/test/instrument/cases/TestLazyBasicFieldAccessExecutable.java b/hibernate-core/src/test/java/org/hibernate/test/instrument/cases/TestLazyBasicFieldAccessExecutable.java new file mode 100644 index 0000000000..0d75f7c9bb --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/instrument/cases/TestLazyBasicFieldAccessExecutable.java @@ -0,0 +1,122 @@ +package org.hibernate.test.instrument.cases; +import org.junit.Assert; + +import org.hibernate.Hibernate; +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.hibernate.test.instrument.domain.Document; +import org.hibernate.test.instrument.domain.Folder; +import org.hibernate.test.instrument.domain.Owner; + +/** + * @author Andrei Ivanov + */ +public class TestLazyBasicFieldAccessExecutable extends AbstractExecutable { + protected String[] getResources() { + return new String[] {"org/hibernate/test/instrument/domain/Documents.hbm.xml"}; + } + + public void execute() { + Session s = getFactory().openSession(); + Transaction t = s.beginTransaction(); + Owner o = new Owner(); + Document doc = new Document(); + Folder fol = new Folder(); + o.setName( "gavin" ); + doc.setName( "Hibernate in Action" ); + doc.setSummary( "blah" ); + doc.updateText( "blah blah" ); + fol.setName( "books" ); + doc.setOwner( o ); + doc.setFolder( fol ); + fol.getDocuments().add( doc ); + Assert.assertTrue( Hibernate.isPropertyInitialized( doc, "summary" ) ); + s.persist( o ); + s.persist( fol ); + t.commit(); + s.close(); + + s = getFactory().openSession(); + s.getTransaction().begin(); + // update with lazy property initialized + doc.setName( "Doc Name" ); + doc.setSummary( "u" ); + s.update( doc ); + s.getTransaction().commit(); + s.close(); + + s = getFactory().openSession(); + s.getTransaction().begin(); + // merge with lazy property initialized and updated + doc.setName( "Doc Name 1" ); + doc.setSummary( "v" ); + Document docManaged = (Document) s.merge( doc ); + Assert.assertEquals( "v", docManaged.getSummary() ); + Assert.assertTrue( Hibernate.isPropertyInitialized( docManaged, "summary" ) ); + s.getTransaction().commit(); + s.close(); + + s = getFactory().openSession(); + s.getTransaction().begin(); + // get the Document with an uninitialized summary + docManaged = (Document) s.get( Document.class, doc.getId() ); + Assert.assertFalse( Hibernate.isPropertyInitialized( docManaged, "summary" ) ); + // merge with lazy property initialized in doc; uninitialized in docManaged. + doc.setSummary( "w" ); + Assert.assertSame( docManaged, s.merge( doc ) ); + Assert.assertEquals( "w", docManaged.getSummary() ); + s.getTransaction().commit(); + s.close(); + + s = getFactory().openSession(); + s.getTransaction().begin(); + // get the Document with an uninitialized summary + docManaged = (Document) s.get( Document.class, doc.getId() ); + Assert.assertFalse( Hibernate.isPropertyInitialized( docManaged, "summary" ) ); + // initialize docManaged.getSummary + Assert.assertEquals( "w", docManaged.getSummary() ); + Assert.assertTrue( Hibernate.isPropertyInitialized( docManaged, "summary" ) ); + // merge with lazy property initialized in both doc and docManaged. + doc.setSummary( "x" ); + Assert.assertSame( docManaged, s.merge( doc ) ); + Assert.assertTrue( Hibernate.isPropertyInitialized( docManaged, "summary" ) ); + Assert.assertEquals( "x", docManaged.getSummary() ); + s.getTransaction().commit(); + s.close(); + + s = getFactory().openSession(); + s.getTransaction().begin(); + // get the Document with an uninitialized summary + Document docWithLazySummary = (Document) s.get( Document.class, doc.getId() ); + Assert.assertFalse( Hibernate.isPropertyInitialized( docWithLazySummary, "summary" ) ); + s.getTransaction().commit(); + s.close(); + + s = getFactory().openSession(); + s.getTransaction().begin(); + // summary should still be uninitialized. + Assert.assertFalse( Hibernate.isPropertyInitialized( docWithLazySummary, "summary" ) ); + docWithLazySummary.setName( "new name" ); + // merge the Document with an uninitialized summary + docManaged = (Document) s.merge( docWithLazySummary ); + Assert.assertFalse( Hibernate.isPropertyInitialized( docManaged, "summary" ) ); + s.getTransaction().commit(); + s.close(); + + s = getFactory().openSession(); + s.getTransaction().begin(); + // get the Document with an uninitialized summary + docManaged = (Document) s.get( Document.class, doc.getId() ); + Assert.assertFalse( Hibernate.isPropertyInitialized( docManaged, "summary" ) ); + // initialize docManaged.getSummary + Assert.assertEquals( "x", docManaged.getSummary() ); + Assert.assertTrue( Hibernate.isPropertyInitialized( docManaged, "summary" ) ); + // merge the Document with an uninitialized summary + Assert.assertFalse( Hibernate.isPropertyInitialized( docWithLazySummary, "summary" ) ); + docManaged = (Document) s.merge( docWithLazySummary ); + Assert.assertTrue( Hibernate.isPropertyInitialized( docManaged, "summary" ) ); + Assert.assertEquals( "x", docManaged.getSummary() ); + s.getTransaction().commit(); + s.close(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/instrument/cases/TestLazyBasicPropertyAccessExecutable.java b/hibernate-core/src/test/java/org/hibernate/test/instrument/cases/TestLazyBasicPropertyAccessExecutable.java new file mode 100644 index 0000000000..668437eff1 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/instrument/cases/TestLazyBasicPropertyAccessExecutable.java @@ -0,0 +1,122 @@ +package org.hibernate.test.instrument.cases; +import org.junit.Assert; + +import org.hibernate.Hibernate; +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.hibernate.test.instrument.domain.Document; +import org.hibernate.test.instrument.domain.Folder; +import org.hibernate.test.instrument.domain.Owner; + +/** + * @author Andrei Ivanov + */ +public class TestLazyBasicPropertyAccessExecutable extends AbstractExecutable { + protected String[] getResources() { + return new String[] {"org/hibernate/test/instrument/domain/DocumentsPropAccess.hbm.xml"}; + } + + public void execute() { + Session s = getFactory().openSession(); + Transaction t = s.beginTransaction(); + Owner o = new Owner(); + Document doc = new Document(); + Folder fol = new Folder(); + o.setName( "gavin" ); + doc.setName( "Hibernate in Action" ); + doc.setSummary( "blah" ); + doc.updateText( "blah blah" ); + fol.setName( "books" ); + doc.setOwner( o ); + doc.setFolder( fol ); + fol.getDocuments().add( doc ); + Assert.assertTrue( Hibernate.isPropertyInitialized( doc, "summary" ) ); + s.persist( o ); + s.persist( fol ); + t.commit(); + s.close(); + + s = getFactory().openSession(); + s.getTransaction().begin(); + // update with lazy property initialized + doc.setName( "Doc Name" ); + doc.setSummary( "u" ); + s.update( doc ); + s.getTransaction().commit(); + s.close(); + + s = getFactory().openSession(); + s.getTransaction().begin(); + // merge with lazy property initialized and updated + doc.setName( "Doc Name 1" ); + doc.setSummary( "v" ); + Document docManaged = (Document) s.merge( doc ); + Assert.assertEquals( "v", docManaged.getSummary() ); + Assert.assertTrue( Hibernate.isPropertyInitialized( docManaged, "summary" ) ); + s.getTransaction().commit(); + s.close(); + + s = getFactory().openSession(); + s.getTransaction().begin(); + // get the Document with an uninitialized summary + docManaged = (Document) s.get( Document.class, doc.getId() ); + Assert.assertFalse( Hibernate.isPropertyInitialized( docManaged, "summary" ) ); + // merge with lazy property initialized in doc; uninitialized in docManaged. + doc.setSummary( "w" ); + Assert.assertSame( docManaged, s.merge( doc ) ); + Assert.assertEquals( "w", docManaged.getSummary() ); + s.getTransaction().commit(); + s.close(); + + s = getFactory().openSession(); + s.getTransaction().begin(); + // get the Document with an uninitialized summary + docManaged = (Document) s.get( Document.class, doc.getId() ); + Assert.assertFalse( Hibernate.isPropertyInitialized( docManaged, "summary" ) ); + // initialize docManaged.getSummary + Assert.assertEquals( "w", docManaged.getSummary() ); + Assert.assertTrue( Hibernate.isPropertyInitialized( docManaged, "summary" ) ); + // merge with lazy property initialized in both doc and docManaged. + doc.setSummary( "x" ); + Assert.assertSame( docManaged, s.merge( doc ) ); + Assert.assertTrue( Hibernate.isPropertyInitialized( docManaged, "summary" ) ); + Assert.assertEquals( "x", docManaged.getSummary() ); + s.getTransaction().commit(); + s.close(); + + s = getFactory().openSession(); + s.getTransaction().begin(); + // get the Document with an uninitialized summary + Document docWithLazySummary = (Document) s.get( Document.class, doc.getId() ); + Assert.assertFalse( Hibernate.isPropertyInitialized( docWithLazySummary, "summary" ) ); + s.getTransaction().commit(); + s.close(); + + s = getFactory().openSession(); + s.getTransaction().begin(); + // summary should still be uninitialized. + Assert.assertFalse( Hibernate.isPropertyInitialized( docWithLazySummary, "summary" ) ); + docWithLazySummary.setName( "new name" ); + // merge the Document with an uninitialized summary + docManaged = (Document) s.merge( docWithLazySummary ); + Assert.assertFalse( Hibernate.isPropertyInitialized( docManaged, "summary" ) ); + s.getTransaction().commit(); + s.close(); + + s = getFactory().openSession(); + s.getTransaction().begin(); + // get the Document with an uninitialized summary + docManaged = (Document) s.get( Document.class, doc.getId() ); + Assert.assertFalse( Hibernate.isPropertyInitialized( docManaged, "summary" ) ); + // initialize docManaged.getSummary + Assert.assertEquals( "x", docManaged.getSummary() ); + Assert.assertTrue( Hibernate.isPropertyInitialized( docManaged, "summary" ) ); + // merge the Document with an uninitialized summary + Assert.assertFalse( Hibernate.isPropertyInitialized( docWithLazySummary, "summary" ) ); + docManaged = (Document) s.merge( docWithLazySummary ); + Assert.assertTrue( Hibernate.isPropertyInitialized( docManaged, "summary" ) ); + Assert.assertEquals( "x", docManaged.getSummary() ); + s.getTransaction().commit(); + s.close(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/instrument/cases/TestLazyBasicPropertyUpdateExecutable.java b/hibernate-core/src/test/java/org/hibernate/test/instrument/cases/TestLazyBasicPropertyUpdateExecutable.java deleted file mode 100644 index 377573b7ca..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/test/instrument/cases/TestLazyBasicPropertyUpdateExecutable.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.hibernate.test.instrument.cases; -import org.junit.Assert; - -import org.hibernate.Hibernate; -import org.hibernate.Session; -import org.hibernate.Transaction; -import org.hibernate.test.instrument.domain.Document; -import org.hibernate.test.instrument.domain.Folder; -import org.hibernate.test.instrument.domain.Owner; - -/** - * @author Andrei Ivanov - */ -public class TestLazyBasicPropertyUpdateExecutable extends AbstractExecutable { - public void execute() { - Session s = getFactory().openSession(); - Transaction t = s.beginTransaction(); - Owner o = new Owner(); - Document doc = new Document(); - Folder fol = new Folder(); - o.setName("gavin"); - doc.setName("Hibernate in Action"); - doc.setSummary("blah"); - doc.updateText("blah blah"); - fol.setName("books"); - doc.setOwner(o); - doc.setFolder(fol); - fol.getDocuments().add(doc); - Assert.assertTrue( Hibernate.isPropertyInitialized( doc, "summary" ) ); - s.persist(o); - s.persist(fol); - t.commit(); - - s.evict(doc); - - doc.setSummary("u"); - s.merge(doc); - - s.close(); - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/test/instrument/domain/DocumentsPropAccess.hbm.xml b/hibernate-core/src/test/java/org/hibernate/test/instrument/domain/DocumentsPropAccess.hbm.xml new file mode 100755 index 0000000000..33e1227c3f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/instrument/domain/DocumentsPropAccess.hbm.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hibernate-core/src/test/java/org/hibernate/test/instrument/runtime/AbstractTransformingClassLoaderInstrumentTestCase.java b/hibernate-core/src/test/java/org/hibernate/test/instrument/runtime/AbstractTransformingClassLoaderInstrumentTestCase.java index f702fad157..3f2a8f9613 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/instrument/runtime/AbstractTransformingClassLoaderInstrumentTestCase.java +++ b/hibernate-core/src/test/java/org/hibernate/test/instrument/runtime/AbstractTransformingClassLoaderInstrumentTestCase.java @@ -124,8 +124,16 @@ public abstract class AbstractTransformingClassLoaderInstrumentTestCase extends } @Test - public void testLazyBasicPropertyUpdate() { - executeExecutable( "org.hibernate.test.instrument.cases.TestLazyBasicPropertyUpdateExecutable" ); + @FailureExpected( jiraKey = "HHH-9984") + @TestForIssue( jiraKey = "HHH-9984") + public void testLazyBasicFieldAccess() { + executeExecutable( "org.hibernate.test.instrument.cases.TestLazyBasicFieldAccessExecutable" ); + } + + @Test + @TestForIssue( jiraKey = "HHH-5255") + public void testLazyBasicPropertyAccess() { + executeExecutable( "org.hibernate.test.instrument.cases.TestLazyBasicPropertyAccessExecutable" ); } @Test