[hibernate-commits] Hibernate SVN: r14903 - in core/branches/Branch_3_2_4_SP1_CP: src/org/hibernate/type and 2 other directories.
hibernate-commits at lists.jboss.org
hibernate-commits at lists.jboss.org
Wed Jul 9 03:16:31 EDT 2008
Author: gbadner
Date: 2008-07-09 03:16:31 -0400 (Wed, 09 Jul 2008)
New Revision: 14903
Added:
core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/A.java
core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/CascadeSuite.java
core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/G.java
core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/H.java
core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/MultiPathCascade.hbm.xml
core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/MultiPathCascadeTest.java
Modified:
core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/event/def/DefaultMergeEventListener.java
core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/type/EntityType.java
core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/AllTests.java
Log:
JBPAPP-915,HHH-3229 : Cascade merge transient entities regardless of property traversal order
Modified: core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/event/def/DefaultMergeEventListener.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/event/def/DefaultMergeEventListener.java 2008-07-09 05:14:03 UTC (rev 14902)
+++ core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/event/def/DefaultMergeEventListener.java 2008-07-09 07:16:31 UTC (rev 14903)
@@ -2,23 +2,27 @@
package org.hibernate.event.def;
import java.io.Serializable;
+import java.util.Iterator;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.ObjectDeletedException;
import org.hibernate.StaleObjectStateException;
+import org.hibernate.TransientObjectException;
import org.hibernate.WrongClassException;
import org.hibernate.engine.Cascade;
import org.hibernate.engine.CascadingAction;
+import org.hibernate.engine.EntityEntry;
+import org.hibernate.engine.EntityKey;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.engine.Status;
import org.hibernate.event.EventSource;
import org.hibernate.event.MergeEvent;
import org.hibernate.event.MergeEventListener;
-import org.hibernate.engine.SessionImplementor;
-import org.hibernate.engine.EntityEntry;
-import org.hibernate.engine.EntityKey;
import org.hibernate.intercept.FieldInterceptionHelper;
import org.hibernate.intercept.FieldInterceptor;
import org.hibernate.persister.entity.EntityPersister;
@@ -43,14 +47,33 @@
return IdentityMap.invert( (Map) anything );
}
- /**
+ /**
* Handle the given merge event.
*
* @param event The merge event to be handled.
* @throws HibernateException
*/
public void onMerge(MergeEvent event) throws HibernateException {
- onMerge( event, IdentityMap.instantiate(10) );
+ Map copyCache = IdentityMap.instantiate(10);
+ onMerge( event, copyCache );
+ for ( Iterator it=copyCache.values().iterator(); it.hasNext(); ) {
+ Object entity = it.next();
+ if ( entity instanceof HibernateProxy ) {
+ entity = ( (HibernateProxy) entity ).getHibernateLazyInitializer().getImplementation();
+ }
+ EntityEntry entry = event.getSession().getPersistenceContext().getEntry( entity );
+ if ( entry == null ) {
+ throw new TransientObjectException(
+ "object references an unsaved transient instance - save the transient instance before merging: " +
+ event.getSession().guessEntityName( entity )
+ );
+ // TODO: cache the entity name somewhere so that it is available to this exception
+ // entity name will not be available for non-POJO entities
+ }
+ if ( entry.getStatus() != Status.MANAGED ) {
+ throw new AssertionFailure( "Merged entity does not have status set to MANAGED; "+entry+" status="+entry.getStatus() );
+ }
+ }
}
/**
@@ -82,7 +105,8 @@
entity = original;
}
- if ( copyCache.containsKey(entity) ) {
+ if ( copyCache.containsKey(entity) &&
+ source.getContextEntityIdentifier( copyCache.get( entity ) ) != null ) {
log.trace("already merged");
event.setResult(entity);
}
@@ -126,7 +150,7 @@
entityIsPersistent(event, copyCache);
break;
default: //DELETED
- throw new ObjectDeletedException(
+ throw new ObjectDeletedException(
"deleted instance passed to merge",
null,
getLoggableName( event.getEntityName(), entity )
@@ -137,7 +161,7 @@
}
}
-
+
protected void entityIsPersistent(MergeEvent event, Map copyCache) {
log.trace("ignoring persistent instance");
@@ -168,10 +192,15 @@
final Serializable id = persister.hasIdentifierProperty() ?
persister.getIdentifier( entity, source.getEntityMode() ) :
null;
-
- final Object copy = persister.instantiate( id, source.getEntityMode() ); //TODO: should this be Session.instantiate(Persister, ...)?
- copyCache.put(entity, copy); //before cascade!
-
+ if ( copyCache.containsKey( entity ) ) {
+ persister.setIdentifier( copyCache.get( entity ), id, source.getEntityMode() );
+ }
+ else {
+ copyCache.put(entity, persister.instantiate( id, source.getEntityMode() ) ); //before cascade!
+ //TODO: should this be Session.instantiate(Persister, ...)?
+ }
+ final Object copy = copyCache.get( entity );
+
// cascade first, so that all unsaved objects get their
// copy created before we actually copy
//cascadeOnMerge(event, persister, entity, copyCache, Cascades.CASCADE_BEFORE_MERGE);
Modified: core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/type/EntityType.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/type/EntityType.java 2008-07-09 05:14:03 UTC (rev 14902)
+++ core/branches/Branch_3_2_4_SP1_CP/src/org/hibernate/type/EntityType.java 2008-07-09 07:16:31 UTC (rev 14903)
@@ -250,13 +250,23 @@
if ( original == target ) {
return target;
}
- Object id = getIdentifier( original, session );
- if ( id == null ) {
- throw new AssertionFailure("cannot copy a reference to an object with a null id");
+ if ( session.getContextEntityIdentifier( original ) == null &&
+ ForeignKeys.isTransient( associatedEntityName, original, Boolean.FALSE, session ) ) {
+ final Object copy = session.getFactory().getEntityPersister( associatedEntityName )
+ .instantiate( null, session.getEntityMode() );
+ //TODO: should this be Session.instantiate(Persister, ...)?
+ copyCache.put( original, copy );
+ return copy;
}
- id = getIdentifierOrUniqueKeyType( session.getFactory() )
- .replace(id, null, session, owner, copyCache);
- return resolve( id, session, owner );
+ else {
+ Object id = getIdentifier( original, session );
+ if ( id == null ) {
+ throw new AssertionFailure("non-transient entity has a null id");
+ }
+ id = getIdentifierOrUniqueKeyType( session.getFactory() )
+ .replace(id, null, session, owner, copyCache);
+ return resolve( id, session, owner );
+ }
}
}
Modified: core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/AllTests.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/AllTests.java 2008-07-09 05:14:03 UTC (rev 14902)
+++ core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/AllTests.java 2008-07-09 07:16:31 UTC (rev 14903)
@@ -20,7 +20,7 @@
import org.hibernate.test.bidi.AuctionTest2;
import org.hibernate.test.bytecode.BytecodeSuite;
import org.hibernate.test.cache.CacheSuite;
-import org.hibernate.test.cascade.RefreshTest;
+import org.hibernate.test.cascade.CascadeSuite;
import org.hibernate.test.cid.CompositeIdTest;
import org.hibernate.test.collection.CollectionSuite;
import org.hibernate.test.component.ComponentSuite;
@@ -259,7 +259,7 @@
suite.addTest( SortTest.suite() );
suite.addTest( WhereTest.suite() );
suite.addTest( IterateTest.suite() );
- suite.addTest( RefreshTest.suite() );
+ suite.addTest( CascadeSuite.suite() );
suite.addTest( CollectionEventSuite.suite() );
suite.addTest( ExtraLazyTest.suite() );
suite.addTest( StatsTest.suite() );
Added: core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/A.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/A.java (rev 0)
+++ core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/A.java 2008-07-09 07:16:31 UTC (rev 14903)
@@ -0,0 +1,106 @@
+// $Id$
+
+package org.hibernate.test.cascade;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * @author <a href="mailto:ovidiu at feodorov.com">Ovidiu Feodorov</a>
+ *
+ * Copyright 2008 Ovidiu Feodorov
+ *
+ */
+public class A
+{
+ // Constants -----------------------------------------------------------------------------------
+
+ // Static --------------------------------------------------------------------------------------
+
+ // Attributes ----------------------------------------------------------------------------------
+
+ private long id;
+
+ private String data;
+
+ // A 1 - * H
+ private Set hs;
+
+ // A 1 - 1 G
+ private G g;
+
+
+ // Constructors --------------------------------------------------------------------------------
+
+ public A()
+ {
+ hs = new HashSet();
+ }
+
+ public A(String data)
+ {
+ this();
+ this.data = data;
+ }
+
+ // Public --------------------------------------------------------------------------------------
+
+ public long getId()
+ {
+ return id;
+ }
+
+ public void setId(long id)
+ {
+ this.id = id;
+ }
+
+ public void setData(String data)
+ {
+ this.data = data;
+ }
+
+ public String getData()
+ {
+ return data;
+ }
+
+ public void setHs(Set hs)
+ {
+ this.hs = hs;
+ }
+
+ public Set getHs()
+ {
+ return hs;
+ }
+
+ public void setG(G g)
+ {
+ this.g = g;
+ }
+
+ public G getG()
+ {
+ return g;
+ }
+
+ public void addH(H h)
+ {
+ hs.add(h);
+ h.setA(this);
+ }
+
+ public String toString()
+ {
+ return "A[" + id + ", " + data + "]";
+ }
+
+ // Package protected ---------------------------------------------------------------------------
+
+ // Protected -----------------------------------------------------------------------------------
+
+ // Private -------------------------------------------------------------------------------------
+
+ // Inner classes -------------------------------------------------------------------------------
+}
Property changes on: core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/A.java
___________________________________________________________________
Name: svn:executable
+ *
Added: core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/CascadeSuite.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/CascadeSuite.java (rev 0)
+++ core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/CascadeSuite.java 2008-07-09 07:16:31 UTC (rev 14903)
@@ -0,0 +1,17 @@
+package org.hibernate.test.cascade;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Implementation of CascadeSuite.
+ */
+public class CascadeSuite {
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite( "Cascade tests" );
+ suite.addTest( RefreshTest.suite() );
+ suite.addTest( MultiPathCascadeTest.suite() );
+ return suite;
+ }
+}
Added: core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/G.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/G.java (rev 0)
+++ core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/G.java 2008-07-09 07:16:31 UTC (rev 14903)
@@ -0,0 +1,95 @@
+package org.hibernate.test.cascade;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * @author <a href="mailto:ovidiu at feodorov.com">Ovidiu Feodorov</a>
+ *
+ * Copyright 2008 Ovidiu Feodorov
+ *
+ * @version <tt>$Revision$</tt>
+ *
+ * $Id$
+ */
+public class G
+{
+ // Constants -----------------------------------------------------------------------------------
+
+ // Static --------------------------------------------------------------------------------------
+
+ // Attributes ----------------------------------------------------------------------------------
+
+ private long id;
+
+ private String data;
+
+ // A 1 <-> 1 G
+ private A a;
+
+ // G * <-> * H
+ private Set hs;
+
+ // Constructors --------------------------------------------------------------------------------
+
+ public G()
+ {
+ this(null);
+ }
+
+ public G(String data)
+ {
+ this.data = data;
+ hs = new HashSet();
+ }
+
+ // Public --------------------------------------------------------------------------------------
+
+ public String getData()
+ {
+ return data;
+ }
+
+ public void setData(String data)
+ {
+ this.data = data;
+ }
+
+ public A getA()
+ {
+ return a;
+ }
+
+ public void setA(A a)
+ {
+ this.a = a;
+ }
+
+ public Set getHs()
+ {
+ return hs;
+ }
+
+ public void setHs(Set s)
+ {
+ hs = s;
+ }
+
+ // Package protected ---------------------------------------------------------------------------
+
+ long getId()
+ {
+ return id;
+ }
+
+ // Protected -----------------------------------------------------------------------------------
+
+ // Private -------------------------------------------------------------------------------------
+
+ private void setId(long id)
+ {
+ this.id = id;
+ }
+
+ // Inner classes -------------------------------------------------------------------------------
+}
Property changes on: core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/G.java
___________________________________________________________________
Name: svn:executable
+ *
Added: core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/H.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/H.java (rev 0)
+++ core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/H.java 2008-07-09 07:16:31 UTC (rev 14903)
@@ -0,0 +1,94 @@
+// $Id$
+
+
+package org.hibernate.test.cascade;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * @author <a href="mailto:ovidiu at feodorov.com">Ovidiu Feodorov</a>
+ *
+ * Copyright 2008 Ovidiu Feodorov
+ *
+ */
+public class H
+{
+ // Constants -----------------------------------------------------------------------------------
+
+ // Static --------------------------------------------------------------------------------------
+
+ // Attributes ----------------------------------------------------------------------------------
+
+ private long id;
+
+ private String data;
+
+ private A a;
+
+ // G * <-> * H
+ private Set gs;
+
+ // Constructors --------------------------------------------------------------------------------
+
+ public H()
+ {
+ this(null);
+ }
+
+ public H(String data)
+ {
+ this.data = data;
+ gs = new HashSet();
+ }
+
+ // Public --------------------------------------------------------------------------------------
+
+ public long getId()
+ {
+ return id;
+ }
+
+ public String getData()
+ {
+ return data;
+ }
+
+ public void setData(String data)
+ {
+ this.data = data;
+ }
+
+ public A getA()
+ {
+ return a;
+ }
+
+ public void setA(A a)
+ {
+ this.a = a;
+ }
+
+ public Set getGs()
+ {
+ return gs;
+ }
+
+ public void setGs(Set gs)
+ {
+ this.gs = gs;
+ }
+
+ // Package protected ---------------------------------------------------------------------------
+
+ // Protected -----------------------------------------------------------------------------------
+
+ // Private -------------------------------------------------------------------------------------
+
+ private void setId(long id)
+ {
+ this.id = id;
+ }
+
+ // Inner classes -------------------------------------------------------------------------------
+}
Property changes on: core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/H.java
___________________________________________________________________
Name: svn:executable
+ *
Added: core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/MultiPathCascade.hbm.xml
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/MultiPathCascade.hbm.xml (rev 0)
+++ core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/MultiPathCascade.hbm.xml 2008-07-09 07:16:31 UTC (rev 14903)
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
+
+<hibernate-mapping package="org.hibernate.test.cascade">
+
+ <class name="A" table="HB_A">
+
+ <id name="id" type="long"><generator class="native"/></id>
+
+ <property name="data" type="string" not-null="true"/>
+
+ <!--
+ Associations
+ -->
+
+ <set name="hs" inverse="true" cascade="all">
+ <key column="a_fk"/>
+ <one-to-many class="H"/>
+ </set>
+ <one-to-one name="g" class="G" property-ref="a" cascade="all"/>
+
+ </class>
+
+ <class name="G" table="HB_G">
+
+ <id name="id" type="long"><generator class="native"/></id>
+
+ <property name="data" type="string" not-null="true"/>
+
+ <!--
+ Associations
+ -->
+
+ <set name="hs" inverse="true" table="HB_G_H" cascade="all">
+ <key column="g_fk"/>
+ <many-to-many class="H" column="h_fk"/>
+ </set>
+
+ <many-to-one name="a"
+ column="aId"
+ unique="true"
+ not-null="false"/>
+
+ </class>
+
+ <class name="H" table="HB_H">
+
+ <id name="id" type="long"><generator class="native"/></id>
+
+ <property name="data" type="string" not-null="true"/>
+
+ <!--
+ Associations
+ -->
+
+ <!-- *NOT* cascaded -->
+ <set name="gs" table="HB_G_H">
+ <key column="h_fk"/>
+ <many-to-many class="G" column="g_fk"/>
+ </set>
+
+ <many-to-one name="a" column="a_fk" class="A"/>
+
+ </class>
+
+
+</hibernate-mapping>
\ No newline at end of file
Added: core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/MultiPathCascadeTest.java
===================================================================
--- core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/MultiPathCascadeTest.java (rev 0)
+++ core/branches/Branch_3_2_4_SP1_CP/test/org/hibernate/test/cascade/MultiPathCascadeTest.java 2008-07-09 07:16:31 UTC (rev 14903)
@@ -0,0 +1,337 @@
+//$Id: $
+
+package org.hibernate.test.cascade;
+
+import junit.framework.Test;
+
+import org.hibernate.Session;
+import org.hibernate.TransientObjectException;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.proxy.HibernateProxy;
+
+/**
+ * @author <a href="mailto:ovidiu at feodorov.com">Ovidiu Feodorov</a>
+ * @author Gail Badner
+ *
+ */
+
+public class MultiPathCascadeTest extends FunctionalTestCase {
+
+ public MultiPathCascadeTest(String name) {
+ super( name );
+ }
+
+ public String[] getMappings() {
+ return new String[] {
+ "cascade/MultiPathCascade.hbm.xml"
+ };
+ }
+
+ public static Test suite() {
+ return new FunctionalTestClassTestSuite( MultiPathCascadeTest.class );
+ }
+
+ protected void cleanupTest() {
+ Session s = openSession();
+ s.beginTransaction();
+ s.createQuery( "delete from A" );
+ s.createQuery( "delete from G" );
+ s.createQuery( "delete from H" );
+ }
+
+ public void testMultiPathMergeModifiedDetached() throws Exception
+ {
+ // persist a simple A in the database
+
+ Session s = openSession();
+ s.beginTransaction();
+ A a = new A();
+ a.setData( "Anna" );
+ s.save( a );
+ s.getTransaction().commit();
+ s.close();
+
+ // modify detached entity
+ modifyEntity( a );
+
+ s = openSession();
+ s.beginTransaction();
+ a = ( A ) s.merge( a );
+ s.getTransaction().commit();
+ s.close();
+
+ verifyModifications( a.getId() );
+ }
+
+ public void testMultiPathMergeModifiedDetachedIntoProxy() throws Exception
+ {
+ // persist a simple A in the database
+
+ Session s = openSession();
+ s.beginTransaction();
+ A a = new A();
+ a.setData( "Anna" );
+ s.save( a );
+ s.getTransaction().commit();
+ s.close();
+
+ // modify detached entity
+ modifyEntity( a );
+
+ s = openSession();
+ s.beginTransaction();
+ A aLoaded = ( A ) s.load( A.class, new Long( a.getId() ) );
+ assertTrue( aLoaded instanceof HibernateProxy );
+ assertSame( aLoaded, s.merge( a ) );
+ s.getTransaction().commit();
+ s.close();
+
+ verifyModifications( a.getId() );
+ }
+
+ public void testMultiPathUpdateModifiedDetached() throws Exception
+ {
+ // persist a simple A in the database
+
+ Session s = openSession();
+ s.beginTransaction();
+ A a = new A();
+ a.setData( "Anna" );
+ s.save( a );
+ s.getTransaction().commit();
+ s.close();
+
+ // modify detached entity
+ modifyEntity( a );
+
+ s = openSession();
+ s.beginTransaction();
+ s.update( a );
+ s.getTransaction().commit();
+ s.close();
+
+ verifyModifications( a.getId() );
+ }
+
+ public void testMultiPathGetAndModify() throws Exception
+ {
+ // persist a simple A in the database
+
+ Session s = openSession();
+ s.beginTransaction();
+ A a = new A();
+ a.setData( "Anna" );
+ s.save( a );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ // retrieve the previously saved instance from the database, and update it
+ a = ( A ) s.get( A.class, new Long( a.getId() ) );
+ modifyEntity( a );
+ s.getTransaction().commit();
+ s.close();
+
+ verifyModifications( a.getId() );
+ }
+
+ public void testMultiPathMergeNonCascadedTransientEntityInCollection() throws Exception
+ {
+ // persist a simple A in the database
+
+ Session s = openSession();
+ s.beginTransaction();
+ A a = new A();
+ a.setData( "Anna" );
+ s.save( a );
+ s.getTransaction().commit();
+ s.close();
+
+ // modify detached entity
+ modifyEntity( a );
+
+ s = openSession();
+ s.beginTransaction();
+ a = ( A ) s.merge( a );
+ s.getTransaction().commit();
+ s.close();
+
+ verifyModifications( a.getId() );
+
+ // add a new (transient) G to collection in h
+ // there is no cascade from H to the collection, so this should fail when merged
+ assertEquals( 1, a.getHs().size() );
+ H h = ( H ) a.getHs().iterator().next();
+ G gNew = new G();
+ gNew.setData( "Gail" );
+ gNew.getHs().add( h );
+ h.getGs().add( gNew );
+
+ s = openSession();
+ s.beginTransaction();
+ try {
+ s.merge( a );
+ s.merge( h );
+ fail( "should have thrown TransientObjectException" );
+ }
+ catch ( TransientObjectException ex ) {
+ // expected
+ }
+ finally {
+ s.getTransaction().rollback();
+ }
+ s.close();
+ }
+
+ public void testMultiPathMergeNonCascadedTransientEntityInOneToOne() throws Exception
+ {
+ // persist a simple A in the database
+
+ Session s = openSession();
+ s.beginTransaction();
+ A a = new A();
+ a.setData( "Anna" );
+ s.save( a );
+ s.getTransaction().commit();
+ s.close();
+
+ // modify detached entity
+ modifyEntity( a );
+
+ s = openSession();
+ s.beginTransaction();
+ a = ( A ) s.merge( a );
+ s.getTransaction().commit();
+ s.close();
+
+ verifyModifications( a.getId() );
+
+ // change the one-to-one association from g to be a new (transient) A
+ // there is no cascade from G to A, so this should fail when merged
+ G g = a.getG();
+ a.setG( null );
+ A aNew = new A();
+ aNew.setData( "Alice" );
+ g.setA( aNew );
+ aNew.setG( g );
+
+ s = openSession();
+ s.beginTransaction();
+ try {
+ s.merge( a );
+ s.merge( g );
+ fail( "should have thrown TransientObjectException" );
+ }
+ catch ( TransientObjectException ex ) {
+ // expected
+ }
+ finally {
+ s.getTransaction().rollback();
+ }
+ s.close();
+ }
+
+ public void testMultiPathMergeNonCascadedTransientEntityInManyToOne() throws Exception
+ {
+ // persist a simple A in the database
+
+ Session s = openSession();
+ s.beginTransaction();
+ A a = new A();
+ a.setData( "Anna" );
+ s.save( a );
+ s.getTransaction().commit();
+ s.close();
+
+ // modify detached entity
+ modifyEntity( a );
+
+ s = openSession();
+ s.beginTransaction();
+ a = ( A ) s.merge( a );
+ s.getTransaction().commit();
+ s.close();
+
+ verifyModifications( a.getId() );
+
+ // change the many-to-one association from h to be a new (transient) A
+ // there is no cascade from H to A, so this should fail when merged
+ assertEquals( 1, a.getHs().size() );
+ H h = ( H ) a.getHs().iterator().next();
+ a.getHs().remove( h );
+ A aNew = new A();
+ aNew.setData( "Alice" );
+ aNew.addH( h );
+
+ s = openSession();
+ s.beginTransaction();
+ try {
+ s.merge( a );
+ s.merge( h );
+ fail( "should have thrown TransientObjectException" );
+ }
+ catch ( TransientObjectException ex ) {
+ // expected
+ }
+ finally {
+ s.getTransaction().rollback();
+ }
+ s.close();
+ }
+
+ private void modifyEntity(A a) {
+ // create a *circular* graph in detached entity
+ a.setData("Anthony");
+
+ G g = new G();
+ g.setData( "Giovanni" );
+
+ H h = new H();
+ h.setData( "Hellen" );
+
+ a.setG( g );
+ g.setA( a );
+
+ a.getHs().add( h );
+ h.setA( a );
+
+ g.getHs().add( h );
+ h.getGs().add( g );
+ }
+
+ private void verifyModifications(long aId) {
+ Session s = openSession();
+ s.beginTransaction();
+
+ // retrieve the A object and check it
+ A a = ( A ) s.get( A.class, new Long( aId ) );
+ assertEquals( aId, a.getId() );
+ assertEquals( "Anthony", a.getData() );
+ assertNotNull( a.getG() );
+ assertNotNull( a.getHs() );
+ assertEquals( 1, a.getHs().size() );
+
+ G gFromA = a.getG();
+ H hFromA = ( H ) a.getHs().iterator().next();
+
+ // check the G object
+ assertEquals( "Giovanni", gFromA.getData() );
+ assertSame( a, gFromA.getA() );
+ assertNotNull( gFromA.getHs() );
+ assertEquals( a.getHs(), gFromA.getHs() );
+ assertSame( hFromA, gFromA.getHs().iterator().next() );
+
+ // check the H object
+ assertEquals( "Hellen", hFromA.getData() );
+ assertSame( a, hFromA.getA() );
+ assertNotNull( hFromA.getGs() );
+ assertEquals( 1, hFromA.getGs().size() );
+ assertSame( gFromA, hFromA.getGs().iterator().next() );
+
+ s.getTransaction().commit();
+ s.close();
+ }
+
+}
More information about the hibernate-commits
mailing list