Java Mailing List Archive

http://www.gg3721.com/

Home » Hibernate Commits List »

[hibernate-commits] Hibernate SVN: r14903 - in
 core/branches/Branch_3_2_4_SP1_CP: src/org/hibernate/type and
 2 other directories.

hibernate-commits

2008-07-09


Author LoginPost Reply
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)
@@(protected) @@
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;
@@(protected) @@
   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() );
+      }
+    }
 }

 /**
@@(protected) @@
       entity = original;
     }
     
-      if ( copyCache.containsKey(entity) ) {
+      if ( copyCache.containsKey(entity) &&
+          source.getContextEntityIdentifier( copyCache.get( entity ) ) != null ) {
       log.trace("already merged");
       event.setResult(entity);
     }
@@(protected) @@
           entityIsPersistent(event, copyCache);
           break;
         default: //DELETED
-            throw new ObjectDeletedException(
+            throw new ObjectDeletedException(
               "deleted instance passed to merge",
               null,
               getLoggableName( event.getEntityName(), entity )
@@(protected) @@
   }
   
 }
-  
+
 protected void entityIsPersistent(MergeEvent event, Map copyCache) {
   log.trace("ignoring persistent instance");
   
@@(protected) @@
   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)
@@(protected) @@
     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)
@@(protected) @@
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;
@@(protected) @@
     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)
@@(protected) @@
+// $Id$
+
+package org.hibernate.test.cascade;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * @author <a href="mailto:ovidiu@(protected)>
+ *
+ * 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)
@@(protected) @@
+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)
@@(protected) @@
+package org.hibernate.test.cascade;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * @author <a href="mailto:ovidiu@(protected)>
+ *
+ * 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)
@@(protected) @@
+// $Id$
+
+
+package org.hibernate.test.cascade;
+
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * @author <a href="mailto:ovidiu@(protected)>
+ *
+ * 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)
@@(protected) @@
+<?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)
@@(protected) @@
+//$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@(protected)>
+ * @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();
+  }
+
+}

_______________________________________________
hibernate-commits mailing list
hibernate-commits@(protected)
https://lists.jboss.org/mailman/listinfo/hibernate-commits
©2008 gg3721.com - Jax Systems, LLC, U.S.A.