HHH-5255 : Merge detached entity failed when the instrumented lazy property is initialized

This commit is contained in:
Gail Badner 2015-07-15 12:37:08 -07:00
parent 3d21604602
commit c00d4609ef
7 changed files with 357 additions and 46 deletions

View File

@ -157,6 +157,22 @@ public class TypeHelper {
|| original[i] == PropertyAccessStrategyBackRefImpl.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 );
}

View File

@ -14,7 +14,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;
@ -73,8 +74,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

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -0,0 +1,78 @@
<?xml version="1.0"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ License: GNU Lesser General Public License (LGPL), version 2.1 or later.
~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!--
This mapping demonstrates
(1) use of lazy properties - this feature requires buildtime
bytecode instrumentation; we don't think this is a very
necessary feature, but provide it for completeleness; if
Hibernate encounters uninstrumented classes, lazy property
fetching will be silently disabled, to enable testing
(2) use of a formula to define a "derived property"
-->
<hibernate-mapping package="org.hibernate.test.instrument.domain" default-access="property">
<class name="Folder" table="folders">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true" length="50"/>
<many-to-one name="parent"/>
<bag name="subfolders" inverse="true" cascade="save-update">
<key column="parent"/>
<one-to-many class="Folder"/>
</bag>
<bag name="documents" inverse="true" cascade="all-delete-orphan">
<key column="folder"/>
<one-to-many class="Document"/>
</bag>
</class>
<class name="Owner" table="owners" lazy="false">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true" length="50"/>
</class>
<class name="Document" table="documents">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true" length="50"/>
<property name="upperCaseName" formula="upper(name)" lazy="true"/>
<property name="summary" column="`summary`" not-null="true" length="200" lazy="true"/>
<many-to-one name="folder" not-null="true" lazy="no-proxy"/>
<many-to-one name="owner" not-null="true" lazy="no-proxy" fetch="select"/>
<property name="text" not-null="true" length="2000" lazy="true"/>
<property name="lastTextModification" not-null="true" lazy="true" access="field"/>
<property name="sizeKb" lazy="true">
<column name="size_mb"
read="size_mb * 1024.0"
write="? / cast( 1024.0 as float )"/>
</property>
</class>
<class name="Entity" table="entity">
<id name="id" column="ID" type="long">
<generator class="increment"/>
</id>
<property name="name" column="NAME" type="string" lazy="true"/>
<many-to-one name="child" column="PRNT_ID" class="Entity" lazy="proxy" cascade="all" />
<many-to-one name="sibling" column="RIGHT_ID" class="Entity" lazy="no-proxy" cascade="all" />
</class>
</hibernate-mapping>

View File

@ -116,8 +116,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