persistence@glassfish.java.net

Re: Fix for issue 1139 "cascade merge"

From: Wonseok Kim <guruwons_at_gmail.com>
Date: Mon, 25 Sep 2006 11:18:39 +0900

Hi, Gordon and team

Please review the attached fix.
* The fix is almost same as the first fix.
* A new test is added to entity-persistence-tests and the test is using
cmp3/advanced models - Project and Employee.

Diffs and the added test are as follows:

Index:
src/java/oracle/toplink/essentials/testing/models/cmp3/advanced/Employee.java
===================================================================
RCS file:
/cvs/glassfish/entity-persistence-tests/src/java/oracle/toplink/essentials/testing/models/cmp3/advanced/Employee.java,v
retrieving revision 1.10
diff -c -r1.10 Employee.java
***
src/java/oracle/toplink/essentials/testing/models/cmp3/advanced/Employee.java
8 Sep 2006 18:53:37 -0000 1.10
---
src/java/oracle/toplink/essentials/testing/models/cmp3/advanced/Employee.java
23 Sep 2006 07:06:58 -0000
***************
*** 205,211 ****
          this.manager = manager;
      }
!     @ManyToMany(cascade=PERSIST)
      @JoinTable(
          name="CMP3_EMP_PROJ",
          // Default for the project side and specify for the employee side
--- 205,211 ----
          this.manager = manager;
      }
!     @ManyToMany(cascade={PERSIST, MERGE})
      @JoinTable(
          name="CMP3_EMP_PROJ",
          // Default for the project side and specify for the employee side
Index:
src/java/oracle/toplink/essentials/testing/models/cmp3/advanced/Project.java
===================================================================
RCS file:
/cvs/glassfish/entity-persistence-tests/src/java/oracle/toplink/essentials/testing/models/cmp3/advanced/Project.java,v
retrieving revision 1.3
diff -c -r1.3 Project.java
***
src/java/oracle/toplink/essentials/testing/models/cmp3/advanced/Project.java
20 Jan 2006 15:57:15 -0000    1.3
---
src/java/oracle/toplink/essentials/testing/models/cmp3/advanced/Project.java
23 Sep 2006 07:06:58 -0000
***************
*** 111,117 ****
          this.description = description;
      }
!     @OneToOne
      @JoinColumn(name="LEADER_ID")
      public Employee getTeamLeader() {
          return teamLeader;
--- 111,117 ----
          this.description = description;
      }
!     @OneToOne(cascade = {CascadeType.MERGE})
      @JoinColumn(name="LEADER_ID")
      public Employee getTeamLeader() {
          return teamLeader;
Index:
src/java/oracle/toplink/essentials/testing/tests/FullRegressionTestSuite.java
===================================================================
RCS file:
/cvs/glassfish/entity-persistence-tests/src/java/oracle/toplink/essentials/testing/tests/FullRegressionTestSuite.java,v
retrieving revision 1.16
diff -c -r1.16 FullRegressionTestSuite.java
***
src/java/oracle/toplink/essentials/testing/tests/FullRegressionTestSuite.java
8 Sep 2006 18:53:38 -0000    1.16
---
src/java/oracle/toplink/essentials/testing/tests/FullRegressionTestSuite.java
23 Sep 2006 07:06:59 -0000
***************
*** 24,38 ****
  import junit.framework.TestSuite;
  import junit.framework.Test;
! import
oracle.toplink.essentials.testing.tests.cmp3.advanced.NamedNativeQueryJUnitTest
;
! import
oracle.toplink.essentials.testing.tests.cmp3.advanced.CallbackEventJUnitTestSuite
;
! import
oracle.toplink.essentials.testing.tests.cmp3.advanced.EntityManagerJUnitTestSuite
;
! import
oracle.toplink.essentials.testing.tests.cmp3.advanced.SQLResultSetMappingTestSuite
;
! import
oracle.toplink.essentials.testing.tests.cmp3.advanced.JoinedAttributeAdvancedJunitTest
;
! import
oracle.toplink.essentials.testing.tests.cmp3.advanced.ReportQueryMultipleReturnTestSuite
;
! import
oracle.toplink.essentials.testing.tests.cmp3.advanced.ExtendedPersistenceContextJUnitTestSuite
;
! import
oracle.toplink.essentials.testing.tests.cmp3.advanced.ReportQueryConstructorExpressionTestSuite
;
! import
oracle.toplink.essentials.testing.tests.cmp3.advanced.OptimisticConcurrencyJUnitTestSuite
;
  import
oracle.toplink.essentials.testing.tests.cmp3.advanced.compositepk.AdvancedCompositePKJunitTest
;
--- 24,30 ----
  import junit.framework.TestSuite;
  import junit.framework.Test;
! import oracle.toplink.essentials.testing.tests.cmp3.advanced.*;
  import
oracle.toplink.essentials.testing.tests.cmp3.advanced.compositepk.AdvancedCompositePKJunitTest
;
***************
*** 80,85 ****
--- 72,78 ----
          fullSuite.addTest(ExtendedPersistenceContextJUnitTestSuite.suite
());
          fullSuite.addTest(ReportQueryConstructorExpressionTestSuite.suite
());
          fullSuite.addTest(OptimisticConcurrencyJUnitTestSuite.suite());
+         fullSuite.addTest(CascadeOperationJUnitTestSuite.suite());
          // Advanced - compositepk model
          fullSuite.addTest(AdvancedCompositePKJunitTest.suite());
src/java/oracle/toplink/essentials/testing/tests/cmp3/advanced/CascadeOperationJUnitTestSuite.java
package oracle.toplink.essentials.testing.tests.cmp3.advanced;
import oracle.toplink.essentials.testing.framework.junit.JUnitTestCase;
import
oracle.toplink.essentials.testing.models.cmp3.advanced.AdvancedTableCreator;
import oracle.toplink.essentials.testing.models.cmp3.advanced.Project;
import oracle.toplink.essentials.testing.models.cmp3.advanced.Employee;
import oracle.toplink.essentials.tools.schemaframework.SchemaManager;
import junit.framework.Test;
import junit.framework.TestSuite;
import junit.extensions.TestSetup;
import javax.persistence.EntityManager;
public class CascadeOperationJUnitTestSuite extends JUnitTestCase {
    public CascadeOperationJUnitTestSuite() {
        super();
    }
    public CascadeOperationJUnitTestSuite(String name) {
        super(name);
    }
    public static Test suite() {
        TestSuite suite = new TestSuite(CascadeOperationJUnitTestSuite.class
);
        return new TestSetup(suite) {
            protected void setUp(){
                SchemaManager schemaManager = new SchemaManager(
JUnitTestCase.getServerSession());
                new AdvancedTableCreator().replaceTables(
JUnitTestCase.getServerSession(), schemaManager);
            }
            protected void tearDown(){
                clearCache();
            }
        };
    }
    // JUnit framework will automatically execute all methods starting with
test...
    // Test cascade merge on a detached entity
    public void testCascadeMergeDetached() {
        // setup
        Project p1 = new Project();
        p1.setName("p1");
        Project p2 = new Project();
        p1.setName("p2");
        Employee e1 = new Employee("e1", "");
        Employee e2 = new Employee("e2", "");
        EntityManager em = createEntityManager();
        em.getTransaction().begin();
        try {
            em.persist(p1);
            em.persist(p2);
            em.persist(e1);
            em.persist(e2);
            em.getTransaction().commit();
            em.clear();
        } catch (RuntimeException re){
            em.getTransaction().rollback();
            throw re;
        }
        // end of setup
        //p1,p2,e1,e2 are detached
        // associate relationships
        //p1 -> e1 (one-to-one)
        p1.setTeamLeader(e1);
        //e1 -> e2 (one-to-many)
        e1.addManagedEmployee(e2);
        //e2 -> p2 (many-to-many)
        e2.addProject(p2);
        p2.addTeamMember(e2);
        em.getTransaction().begin();
        try {
            Project mp1 = em.merge(p1); // cascade merge
            assertTrue(em.contains(mp1));
            assertTrue("Managed instance and detached instance must not be
same", mp1 != p1);
            Employee me1 = mp1.getTeamLeader();
            assertTrue("Cascade merge failed", em.contains(me1));
            assertTrue("Managed instance and detached instance must not be
same", me1 != e1);
            Employee me2 = me1.getManagedEmployees().iterator().next();
            assertTrue("Cascade merge failed", em.contains(me2));
            assertTrue("Managed instance and detached instance must not be
same", me2 != e2);
            Project mp2 = me2.getProjects().iterator().next();
            assertTrue("Cascade merge failed", em.contains(mp2));
            assertTrue("Managed instance and detached instance must not be
same", mp2 != p2);
            em.getTransaction().commit();
            em.clear();
        } catch (RuntimeException re){
            em.getTransaction().rollback();
            throw re;
        }
    }
    // Test cascade merge on a managed entity
    // Test for GF#1139 - Cascade doesn't work when merging managed entity
    public void testCascadeMergeManaged() {
        // setup
        Project p1 = new Project();
        p1.setName("p1");
        Project p2 = new Project();
        p1.setName("p2");
        Employee e1 = new Employee("e1", "");
        Employee e2 = new Employee("e2", "");
        EntityManager em = createEntityManager();
        em.getTransaction().begin();
        try {
            em.persist(p1);
            em.persist(p2);
            em.persist(e1);
            em.persist(e2);
            em.getTransaction().commit();
            em.clear();
        } catch (RuntimeException re){
            em.getTransaction().rollback();
            throw re;
        }
        // end of setup
        //p1,p2,e1,e2 are detached
        em.getTransaction().begin();
        try {
            Project mp1 = em.merge(p1);
            assertTrue(em.contains(mp1));
            assertTrue("Managed instance and detached instance must not be
same", mp1 != p1);
            //p1 -> e1 (one-to-one)
            mp1.setTeamLeader(e1);
            mp1 = em.merge(mp1); // merge again - trigger cascade merge
            Employee me1 = mp1.getTeamLeader();
            assertTrue("Cascade merge failed", em.contains(me1));
            assertTrue("Managed instance and detached instance must not be
same", me1 != e1);
            //e1 -> e2 (one-to-many)
            me1.addManagedEmployee(e2);
            me1 = em.merge(me1); // merge again - trigger cascade merge
            Employee me2 = me1.getManagedEmployees().iterator().next();
            assertTrue("Cascade merge failed", em.contains(me2));
            assertTrue("Managed instance and detached instance must not be
same", me2 != e2);
            //e2 -> p2 (many-to-many)
            me2.addProject(p2);
            p2.addTeamMember(me2);
            me2 = em.merge(me2); // merge again - trigger cascade merge
            Project mp2 = me2.getProjects().iterator().next();
            assertTrue("Cascade merge failed", em.contains(mp2));
            assertTrue("Managed instance and detached instance must not be
same", mp2 != p2);
            em.getTransaction().commit();
            em.clear();
        } catch (RuntimeException re){
            em.getTransaction().rollback();
            throw re;
        }
    }
    public static void main(String[] args) {
        // Now run JUnit.
        junit.swingui.TestRunner.main(args);
    }
}
Thanks
-Wonseok
On 9/22/06, Gordon Yorke <gordon.yorke_at_oracle.com> wrote:
>
>  Wonseok, Markus,
>     I think the original proposal was closer to a better solution.  Adding
> another method to ObjectBuilder adds a second merge path that will need to
> be maintained in the future.
>     I think that if we refactor MergeManager a bit we can have MergeManger
> not perform the 'short-circuit' when called from a
> RepeatableWriteUnitOfWork.  This will isolate the cascade merging behaviour
> to the EJB30 implemetation.
> --Gordon
>