You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ojb-dev@db.apache.org by ar...@apache.org on 2006/07/15 16:49:28 UTC

svn commit: r422240 [2/2] - in /db/ojb/trunk/src/test/org/apache/ojb: broker/ broker/sequence/ broker/sqlcount/ compare/ junit/ odmg/

Modified: db/ojb/trunk/src/test/org/apache/ojb/odmg/CircularTest.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/test/org/apache/ojb/odmg/CircularTest.java?rev=422240&r1=422239&r2=422240&view=diff
==============================================================================
--- db/ojb/trunk/src/test/org/apache/ojb/odmg/CircularTest.java (original)
+++ db/ojb/trunk/src/test/org/apache/ojb/odmg/CircularTest.java Sat Jul 15 07:49:27 2006
@@ -1,16 +1,6 @@
 package org.apache.ojb.odmg;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-
-import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
-import org.apache.ojb.junit.ODMGTestCase;
-import org.odmg.OQLQuery;
-import org.odmg.Transaction;
-
-/* Copyright 2002-2004 The Apache Software Foundation
+/* Copyright 2002-2005 The Apache Software Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -25,6 +15,19 @@
  * limitations under the License.
  */
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
+import org.apache.ojb.broker.metadata.ClassDescriptor;
+import org.apache.ojb.broker.Identity;
+import org.apache.ojb.junit.ODMGTestCase;
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.odmg.OQLQuery;
+import org.odmg.Transaction;
+
 /**
  * Testing complex object graphs with circular and bidirectional references when
  * using database foreign key settings (without support of deferred foreign keys).
@@ -41,7 +44,7 @@
  * - Shop has a FK to ShopDetail<br/>
  * - Product has a FK  to Product<br/>
  * - Product has a FK  to Shop<br/>
- * - CT_SHOP_DISTRIBUTOR indirection table has FK's to Shop and Distributor<br/>
+ * - CT_SHOP_DIST indirection table has FK's to Shop and Distributor<br/>
  * <p/>
  * Here a summery of the dependencies:<br/>
  * Shop--1:1-->ShopDetail--1:1-->Shop<br/>
@@ -74,6 +77,163 @@
     }
 
     /**
+     * Test for OJB-103
+     *
+     * Reproduce issue posted by user:
+     * Consider A1 and B1 cross referenced objetcs, each one referring the other.
+     The process consists with create A2, and replace A1.
+     So, with ODMG, we wrote something like that :
+    1. retrieve A1, retrieve B1 with A1.getB()
+    2. instanciation and lock of A2
+    3. A2.setB(B1)
+    4. delete A1 (markDelete)
+    5. lock B1, B1.setA(null)
+    6. flush (assume it is required)
+    7. lock B1
+    8. B1.setA(A2)
+    9. commit
+    After commit, we observed that in database, B1 doesn't refers A2 !!
+     */
+    public void testBidirectionalOneToOneMoveObject_2()
+    {
+        String name = "testBidirectionalOneToOneMoveObject_2_" + System.currentTimeMillis();
+        Shop s1_new = new Shop(name + "_1");
+        ShopDetail sd_new = new ShopDetail(name + "_1");
+        TransactionExt tx = (TransactionExt) odmg.newTransaction();
+        tx.begin();
+        // insert new objects
+        database.makePersistent(sd_new);
+        database.makePersistent(s1_new);
+        tx.flush();
+        s1_new.setDetail(sd_new);
+        sd_new.setShop(s1_new);
+        tx.commit();
+        tx.begin();
+        tx.getBroker().clearCache();
+        Identity oid = tx.getBroker().serviceIdentity().buildIdentity(s1_new);
+        Shop s1 = (Shop) tx.getBroker().getObjectByIdentity(oid);
+        tx.commit();
+        // check bidirectional reference
+        assertNotNull(s1);
+        assertNotNull(s1.getDetail());
+
+        tx = (TransactionExt) odmg.newTransaction();
+        // use implicit locking
+        tx.setImplicitLocking(true);
+        tx.begin();
+        // get the object to move
+        ShopDetail sd = s1.getDetail();
+        // create new Shop
+        Shop s2 = new Shop(name + "_2");
+        tx.lock(s2, Transaction.WRITE);
+        //sd.setShop(null);
+        s2.setDetail(sd);
+        database.deletePersistent(s1);
+        tx.lock(sd, Transaction.WRITE);
+        sd.setShop(null);
+        tx.flush();
+        tx.lock(sd, Transaction.WRITE);
+        sd.setShop(s2);
+        tx.commit();
+
+        tx.begin();
+        tx.getBroker().clearCache();
+        s1 = (Shop) tx.getBroker().getObjectByIdentity(oid);
+        tx.commit();
+        assertNull(s1);
+
+        tx.begin();
+        tx.getBroker().clearCache();
+        Identity oid_2 = tx.getBroker().serviceIdentity().buildIdentity(s2);
+        Shop s2_ = (Shop) tx.getBroker().getObjectByIdentity(oid_2);
+        tx.commit();
+        // check bidirectional reference
+        assertNotNull(s2_);
+        assertNotNull(s2_.getDetail());
+        assertEquals(sd_new.getId(), s2_.getDetail().getId());
+        assertNotNull(s2_.getDetail().getShop());
+        assertEquals(s2_, s2_.getDetail().getShop());
+    }
+
+    /**
+     * Reproduce issue posted by user:
+     * Consider A1 and B1 cross referenced objetcs, each one referring the other.
+     The process consists with create A2, and replace A1.
+     So, with ODMG, we wrote something like that :
+    1. retrieve A1, retrieve B1 with A1.getB()
+    2. instanciation and lock of A2
+    3. A2.setB(B1)
+    4. delete A1 (markDelete)
+    5. lock B1, B1.setA(null)
+    6. flush (assume it is required)
+    7. lock B1
+    8. B1.setA(A2)
+    9. commit
+    After commit, we observed that in database, B1 doesn't refers A2 !!
+     */
+    public void testBidirectionalOneToOneMoveObject()
+    {
+        String name = "testBidirectionalOneToOneMoveObject_" + System.currentTimeMillis();
+        Shop s1_new = new Shop(name + "_1");
+        ShopDetail sd_new = new ShopDetail(name + "_1");
+
+        TransactionExt tx = (TransactionExt) odmg.newTransaction();
+        tx.begin();
+        // insert new objects
+        database.makePersistent(sd_new);
+        database.makePersistent(s1_new);
+        tx.flush();
+        s1_new.setDetail(sd_new);
+        sd_new.setShop(s1_new);
+        tx.commit();
+
+        tx.begin();
+        tx.getBroker().clearCache();
+        Identity oid = tx.getBroker().serviceIdentity().buildIdentity(s1_new);
+        Shop s1 = (Shop) tx.getBroker().getObjectByIdentity(oid);
+        tx.commit();
+        // check bidirectional reference
+        assertNotNull(s1);
+        assertNotNull(s1.getDetail());
+
+        tx = (TransactionExt) odmg.newTransaction();
+        tx.begin();
+        // to make this test more transparent disable implicit locking
+        tx.setImplicitLocking(false);
+        // get the object to move
+        ShopDetail sd = s1.getDetail();
+        // create new Shop
+        Shop s2 = new Shop(name + "_2");
+        // now lock all relevant objects
+        tx.lock(s2, Transaction.WRITE);
+        tx.lock(s1, Transaction.WRITE);
+        tx.lock(sd, Transaction.WRITE);
+        s2.setDetail(sd);
+        sd.setShop(s2);
+        s1.setDetail(null);
+        database.deletePersistent(s1);
+        tx.commit();
+
+        tx.begin();
+        tx.getBroker().clearCache();
+        s1 = (Shop) tx.getBroker().getObjectByIdentity(oid);
+        tx.commit();
+        assertNull(s1);
+
+        tx.begin();
+        tx.getBroker().clearCache();
+        Identity oid_2 = tx.getBroker().serviceIdentity().buildIdentity(s2);
+        Shop s2_ = (Shop) tx.getBroker().getObjectByIdentity(oid_2);
+        tx.commit();
+        // check bidirectional reference
+        assertNotNull(s2_);
+        assertNotNull(s2_.getDetail());
+        assertEquals(sd_new.getId(), s2_.getDetail().getId());
+        assertNotNull(s2_.getDetail().getShop());
+        assertEquals(s2_, s2_.getDetail().getShop());
+    }
+
+    /**
      * Handling circular 1:n references with FK settings and use of
      * auto-delete setting to delete object graph.
      */
@@ -90,25 +250,25 @@
         tx.begin();
         p1.addSubProduct(p2);
         p2.addSubProduct(p3);
-        database.makePersistent(p1);
+        database.makePersistent(p3);
         // before establishing the circular references write
         // all objects to DB
         tx.flush();
         // now close the circular references
-        p2.addSubProduct(p1);
+        p3.addSubProduct(p1);
         tx.commit();
 
         tx.begin();
         // on delete break the circular references first, then delete the
         // start object
-        tx.lock(p2, Transaction.WRITE);
+        tx.lock(p3, Transaction.WRITE);
+        // this call is only needed if auto-delete setting in repository is 'object'
         tx.setCascadingDelete(Product.class, "subProducts", false);
-        p2.setSubProducts(null);
+        p3.setSubProducts(null);
         tx.flush();
-        tx.setCascadingDelete(Product.class, "subProducts", true);
-        database.deletePersistent(p1);
-        // this object was unlinked on fluhs(), so we have to remove it by hand
         database.deletePersistent(p3);
+        database.deletePersistent(p2);
+        database.deletePersistent(p1);
         tx.commit();
 
         tx.begin();
@@ -180,6 +340,55 @@
     }
 
     /**
+     * Handling circular 1:n references with FK settings and use of
+     * auto-delete setting to delete object graph.
+     */
+    public void testCircularOneToN_3() throws Exception
+    {
+        String name = "testCircularOneToN_3_" + System.currentTimeMillis();
+        ojbChangeReferenceSetting(Product.class, "subProducts", true, ObjectReferenceDescriptor.CASCADE_NONE, ObjectReferenceDescriptor.CASCADE_OBJECT, false);
+
+        Product p1 = new Product(name + "_p1");
+        Product p2 = new Product(name + "_p2");
+        Product p3 = new Product(name + "_p3");
+
+        TransactionExt tx = (TransactionExt) odmg.newTransaction();
+        tx.begin();
+        p1.addSubProduct(p2);
+        p2.addSubProduct(p3);
+        database.makePersistent(p3);
+        // before establishing the circular references write
+        // all objects to DB
+        tx.flush();
+        // now close the circular references
+        p3.addSubProduct(p1);
+        tx.commit();
+
+        tx.begin();
+        // on delete break the circular references first, then delete the
+        // start object
+        tx.lock(p3, Transaction.WRITE);
+        // this call is only needed if auto-delete setting in repository is 'object'
+        tx.setCascadingDelete(Product.class, "subProducts", false);
+        p3.setSubProducts(null);
+        tx.flush();
+        // this call is only needed if auto-delete setting in repository is 'none'
+        // to enable cascade delete, else we have to delete each object by hand
+        tx.setCascadingDelete(Product.class, "subProducts", true);
+        database.deletePersistent(p1);
+        tx.commit();
+
+        tx.begin();
+        OQLQuery query = odmg.newOQLQuery();
+        query.create("select objects from " + Product.class.getName() + " where name like $1");
+        query.bind(name + "%");
+        Collection result = (Collection) query.execute();
+        tx.commit();
+
+        assertEquals(0, result.size());
+    }
+
+    /**
      * Use auto-delete setting to delete object graph.
      */
     public void testCircularWithAutoDeleteEnabled() throws Exception
@@ -355,30 +564,76 @@
     }
 
     /**
-     * Test show handling with circular references and database FK settings.
+     * Handle circuler 1:1 with default methods.
      */
-    public void testBidirectionalWithConstraint_1a() throws Exception
+    public void testBidirectionalWithConstraint() throws Exception
     {
-        String name = "testBidirectionalWithConstraint_1a_" + System.currentTimeMillis();
+        String name = "testBidirectionalWithConstraint_" + System.currentTimeMillis();
+
+        Shop s1 = new Shop(name + "_1");
+        ShopDetail sd = new ShopDetail(name + "_1");
+
         TransactionExt tx = (TransactionExt) odmg.newTransaction();
         tx.begin();
+        database.makePersistent(sd);
+        database.makePersistent(s1);
+        tx.flush();
+        s1.setDetail(sd);
+        sd.setShop(s1);
+        tx.commit();
+
+        tx.begin();
+        tx.getBroker().clearCache();
+        Identity oid = tx.getBroker().serviceIdentity().buildIdentity(s1);
+        Shop newShop = (Shop) tx.getBroker().getObjectByIdentity(oid);
+        tx.commit();
+        assertNotNull(newShop);
+        assertNotNull(newShop.getDetail());
+
+
+        tx.begin();
+        database.deletePersistent(s1);
+        tx.flush();
+        database.deletePersistent(sd);
+        tx.commit();
+    }
+
+    /**
+     * Handle circuler 1:1 with default methods.
+     */
+    public void testBidirectionalWithConstraint_1a() throws Exception
+    {
+        String name = "testBidirectionalWithConstraint_1a_" + System.currentTimeMillis();
 
         Shop s1 = new Shop(name + "_1");
         ShopDetail sd = new ShopDetail(name + "_1");
         s1.setDetail(sd);
         sd.setShop(s1);
 
+        TransactionExt tx = (TransactionExt) odmg.newTransaction();
+        tx.begin();
+        database.makePersistent(sd);
+        tx.flush();
         database.makePersistent(s1);
         tx.commit();
 
         tx.begin();
+        tx.getBroker().clearCache();
+        Identity oid = tx.getBroker().serviceIdentity().buildIdentity(s1);
+        Shop newShop = (Shop) tx.getBroker().getObjectByIdentity(oid);
+        tx.commit();
+        assertNotNull(newShop);
+        assertNotNull(newShop.getDetail());
+
+        tx.begin();
         database.deletePersistent(s1);
+        tx.flush();
         database.deletePersistent(sd);
         tx.commit();
     }
 
     /**
-     * If the user take care of the ordering itself the test pass.
+     * Define order of object operations using flush() method.
      */
     public void testBidirectionalWithConstraint_1b() throws Exception
     {
@@ -403,9 +658,19 @@
         tx.commit();
 
         tx.begin();
+        tx.getBroker().clearCache();
+        Identity oid = tx.getBroker().serviceIdentity().buildIdentity(s1);
+        Shop newShop = (Shop) tx.getBroker().getObjectByIdentity(oid);
+        tx.commit();
+        assertNotNull(newShop);
+        assertNotNull(newShop.getDetail());
+
+        tx.begin();
         // madatory to mark object with DB FK constraint first on delete
-        // then OJB will use this order to delete the bidirectional objects
+        // (FK from Shop to ShopDetail) then OJB will use this order to
+        // delete the bidirectional objects
         database.deletePersistent(s1);
+        tx.flush();
         database.deletePersistent(sd);
         tx.commit();
     }
@@ -416,22 +681,39 @@
     public void testBidirectionalWithConstraint_1c() throws Exception
     {
         String name = "testBidirectionalWithConstraint_1c_" + System.currentTimeMillis();
-        TransactionExt tx = (TransactionExt) odmg.newTransaction();
-        tx.begin();
 
         Shop s1 = new Shop(name + "_1");
         ShopDetail sd = new ShopDetail(name + "_1");
         s1.setDetail(sd);
         sd.setShop(s1);
 
+        TransactionExt tx = (TransactionExt) odmg.newTransaction();
+        // set implicit locking false to determine order of objects
+        tx.setImplicitLocking(false);
+        // to prevent reordering of object, disable ordering
+        // in many cases this is not needed, because OJB will leave ordering
+        // tx.setOrdering(false);
+        tx.begin();
         // madatory to persist referenced ShopDetail first, the Shop
         // object will be detected automatic. In this case first the ShopDetail
         // will be created and then the Shop
         database.makePersistent(sd);
         database.makePersistent(s1);
+        // now write objects to database
+        tx.flush();
+        // this call will now detect the changed FK values of the bidirectional 1:1 reference
         tx.commit();
 
         tx.begin();
+        tx.getBroker().clearCache();
+        Identity oid = tx.getBroker().serviceIdentity().buildIdentity(s1);
+        Shop newShop = (Shop) tx.getBroker().getObjectByIdentity(oid);
+        tx.commit();
+        assertNotNull(newShop);
+        assertNotNull(newShop.getDetail());
+
+        // we using the same tx, thus locking and (ordering) is still disabled
+        tx.begin();
         // madatory to mark object with DB FK constraint first on delete
         // then OJB will use this order to delete the bidirectional objects
         database.deletePersistent(s1);
@@ -461,8 +743,17 @@
         // only for testing, we completely bypass odmg
         tx.getBroker().beginTransaction();
         tx.getBroker().store(s1);
+        // we need this call to assign the Shop FK in ShopDetail
+        tx.getBroker().store(sd);
         tx.commit();
 
+        tx.begin();
+        tx.getBroker().clearCache();
+        Identity oid = tx.getBroker().serviceIdentity().buildIdentity(s1);
+        Shop newShop = (Shop) tx.getBroker().getObjectByIdentity(oid);
+        tx.commit();
+        assertNotNull(newShop);
+        assertNotNull(newShop.getDetail());
 
         tx.begin();
         // only for testing, we completely bypass odmg
@@ -474,6 +765,63 @@
     }
 
     /**
+     * Handle circular 1:1 by using a 'constraint'-flag property in
+     * reference-descriptor to make OJB's ordering algorithm more
+     * sophisticated.
+     */
+    public void testBidirectionalWithConstraint_1e() throws Exception
+    {
+        String name = "testBidirectionalWithConstraint_1e_" + System.currentTimeMillis();
+        ObjectReferenceDescriptor ord = null;
+
+        try
+        {
+            CircularTest.Shop s1 = new CircularTest.Shop(name + "_1");
+            CircularTest.ShopDetail sd = new CircularTest.ShopDetail(name + "_1");
+            s1.setDetail(sd);
+            sd.setShop(s1);
+
+            TransactionExt tx = (TransactionExt) odmg.newTransaction();
+            tx.begin();
+            // now we tell OJB that one 1:1 reference of the bidirectional 1:1 reference
+            // between Shop and ShopDetail has a FK constraint
+            ClassDescriptor cld = tx.getBroker().getClassDescriptor(CircularTest.Shop.class);
+            ord = cld.getObjectReferenceDescriptorByName("detail");
+            // current DB schema create a foreign key constraint and we can
+            // inform OJB
+            ord.setConstraint(true);
+            // now it doesn't matter in which order we persist the new objects, OJB should
+            // always reorder the objects before insert/update call
+            database.makePersistent(sd);
+            // or
+            // database.makePersistent(s1);
+            tx.commit();
+
+            tx.begin();
+            tx.getBroker().clearCache();
+            Identity oid = tx.getBroker().serviceIdentity().buildIdentity(s1);
+            Shop newShop = (Shop) tx.getBroker().getObjectByIdentity(oid);
+            tx.commit();
+            assertNotNull(newShop);
+            assertNotNull(newShop.getDetail());
+
+            tx.begin();
+            // with cascading delete and the declared FK constraint OJB
+            // always use the correct order on delete.
+            tx.setCascadingDelete(CircularTest.ShopDetail.class, true);
+            database.deletePersistent(sd);
+            // or
+            // database.deletePersistent(s1);
+            tx.commit();
+        }
+        finally
+        {
+            // restore old setting
+            if(ord != null) ord.setConstraint(false);
+        }
+    }
+
+    /**
      * Test show handling with circular references and database FK settings.
      */
     public void testCircularOneToOne_1a() throws Exception
@@ -604,100 +952,10 @@
     }
 
     /**
-     * This test fails!! The ordering isn't able to handle this without causing
-     * a key constraint violation.
-     */
-    public void testCircularOneToOne_1c() throws Exception
-    {
-        String name = "testCircularOneToOne_1c_" + System.currentTimeMillis();
-
-        TransactionExt tx = (TransactionExt) odmg.newTransaction();
-        tx.begin();
-
-        ObjectA a = new ObjectA(name + "_ObjectA");
-        ObjectAA aa = new ObjectAA(name + "_ObjectAA");
-        ObjectAAA aaa = new ObjectAAA(name + "_ObjectAAA");
-        ObjectAAAA aaaa = new ObjectAAAA(name + "_ObjectAAAA");
-        // now set the circular references
-        a.setRefAA(aa);
-        aa.setRefAAA(aaa);
-        aaa.setRefAAAA(aaaa);
-        aaaa.setRefA(a);
-        // use any object to store the whole graph
-        //database.makePersistent(aaaa);
-        //database.makePersistent(aaa);
-        database.makePersistent(aaa);
-        //database.makePersistent(a);
-        tx.commit();
-
-        tx.begin();
-        OQLQuery query = odmg.newOQLQuery();
-        query.create("select objects from " + ObjectA.class.getName() + " where name like $1");
-        query.bind(name + "%");
-        Collection result = (Collection) query.execute();
-        assertEquals(1, result.size());
-
-        query = odmg.newOQLQuery();
-        query.create("select objects from " + ObjectAA.class.getName() + " where name like $1");
-        query.bind(name + "%");
-        result = (Collection) query.execute();
-        assertEquals(1, result.size());
-
-        query = odmg.newOQLQuery();
-        query.create("select objects from " + ObjectAAA.class.getName() + " where name like $1");
-        query.bind(name + "%");
-        result = (Collection) query.execute();
-        assertEquals(1, result.size());
-
-        query = odmg.newOQLQuery();
-        query.create("select objects from " + ObjectAAAA.class.getName() + " where name like $1");
-        query.bind(name + "%");
-        result = (Collection) query.execute();
-        assertEquals(1, result.size());
-        tx.commit();
-
-        tx.begin();
-        // we force OJB to respect user order
-        tx.setNoteUserOrder(true);
-        // we specify the objects to delete in the correct order
-        database.deletePersistent(a);
-        database.deletePersistent(aa);
-        database.deletePersistent(aaa);
-        database.deletePersistent(aaaa);
-        tx.commit();
-
-        tx.begin();
-        query = odmg.newOQLQuery();
-        query.create("select objects from " + ObjectA.class.getName() + " where name like $1");
-        query.bind(name + "%");
-        result = (Collection) query.execute();
-        assertEquals(0, result.size());
-
-        query = odmg.newOQLQuery();
-        query.create("select objects from " + ObjectAA.class.getName() + " where name like $1");
-        query.bind(name + "%");
-        result = (Collection) query.execute();
-        assertEquals(0, result.size());
-
-        query = odmg.newOQLQuery();
-        query.create("select objects from " + ObjectAAA.class.getName() + " where name like $1");
-        query.bind(name + "%");
-        result = (Collection) query.execute();
-        assertEquals(0, result.size());
-
-        query = odmg.newOQLQuery();
-        query.create("select objects from " + ObjectAAAA.class.getName() + " where name like $1");
-        query.bind(name + "%");
-        result = (Collection) query.execute();
-        assertEquals(0, result.size());
-        tx.commit();
-    }
-
-    /**
      * Do manually ordering using {@link TransactionExt#setOrdering(boolean)} to disable
      * OJB's ordering algorithm (and implicit locking).
      */
-    public void testCircularOneToOne_1d() throws Exception
+    public void testCircularOneToOne_1c() throws Exception
     {
         String name = "testCircularOneToOne_1d_" + System.currentTimeMillis();
 
@@ -720,7 +978,7 @@
         */
         tx.setOrdering(false);
         tx.setImplicitLocking(false);
-        
+
         database.makePersistent(aaaa);
         database.makePersistent(aaa);
         database.makePersistent(aa);
@@ -755,7 +1013,7 @@
 
         tx.begin();
         /*
-        the ordering/implicit locking of the tx is still disabled,
+        the ordering/implicit locking of the tx is still disabled (tx instance hasn't changed),
         so we have to take care of correct order of objects while deletion
         */
         database.deletePersistent(a);
@@ -814,9 +1072,8 @@
         /*
         we manually determine the insert object order
         */
-        tx.setOrdering(true);
-        tx.setImplicitLocking(true);
-        tx.setNoteUserOrder(true);
+        tx.setOrdering(false);
+        tx.setImplicitLocking(false);
 
         database.makePersistent(aaaa);
         database.makePersistent(aaa);
@@ -916,15 +1173,12 @@
     }
 
     /**
-     * If the user take care of the ordering itself the test pass.
+     * User take care of the ordering itself.
      */
     public void testCircularOneToOne_2a() throws Exception
     {
         String name = "testCircularOneToOne_2_" + System.currentTimeMillis();
 
-        TransactionExt tx = (TransactionExt) odmg.newTransaction();
-        tx.begin();
-
         ObjectA a = new ObjectA(name + "_ObjectA");
         ObjectAA aa = new ObjectAA(name + "_ObjectAA");
         ObjectAAA aaa = new ObjectAAA(name + "_ObjectAAA");
@@ -932,11 +1186,20 @@
         a.setRefAA(aa);
         aa.setRefAAA(aaa);
         aaa.setRefA(a);
+
+        TransactionExt tx = (TransactionExt) odmg.newTransaction();
+        // we want control object insert order itself, thus
+        // disable implicite locking and ordering
+        tx.setImplicitLocking(false);
+        tx.setOrdering(false);
+        tx.begin();
         database.makePersistent(aaa);
         database.makePersistent(aa);
         database.makePersistent(a);
         tx.commit();
 
+        // we use the same tx again, thus implicite locking
+        // and ordering is still disabled
         tx.begin();
         database.deletePersistent(a);
         database.deletePersistent(aa);
@@ -1113,11 +1376,9 @@
     }
 
     /**
-     * Class Shop has a bidirectional 1:1 reference with ShopDetail and the DB table of Shop
-     * has a foreign key constraint on ShopDetail table. Shop has a m:n relation with Distributor.
-     * <p/>
-     * This test fails!! The ordering isn't able to handle this with causing
-     * a key constraint violation.
+     * Class Shop has a bidirectional 1:1 reference with ShopDetail
+     * (FK constraint from SHOP to SHOP_DETAIL table).
+     * Shop has a m:n relation with Distributor.
      */
     public void testBidirectionalWithConstraint_2a() throws Exception
     {
@@ -1140,6 +1401,8 @@
         // touch the Distributor object
         tx.begin();
         database.deletePersistent(s1);
+        // flush to avoid constraint error
+        tx.flush();
         database.deletePersistent(sd);
         tx.commit();
     }
@@ -1152,62 +1415,30 @@
      */
     public void testBidirectionalWithConstraint_2b() throws Exception
     {
-        String name = "testBidirectionalWithConstraint_2b_" + System.currentTimeMillis();
+        String name = "testBidirectionalWithConstraint_2c_" + System.currentTimeMillis();
 
         Shop s1 = new Shop(name + "_1");
-        ShopDetail sd = new ShopDetail(name + "_1");
-        s1.setDetail(sd);
-        sd.setShop(s1);
         Distributor d1 = new Distributor(name + "_1");
         s1.addDistributor(d1);
         d1.addShop(s1);
 
         TransactionExt tx = (TransactionExt) odmg.newTransaction();
         tx.begin();
-        // mandatory to first persist the referenced object of the
-        // bidirectional 1:1 reference, because of the FK in Shop
-        // the m:n relation will be handled without problems
-        database.makePersistent(sd);
-        // it's not needed to declare all objects in this case,
-        // but it wouldn't affect
-        // database.makePersistent(s1);
-        // database.makePersistent(d1);
-        tx.commit();
-
-        // Now we delete the Shop with ShopDetail, but don't
-        // touch the Distributor object
-        tx.begin();
-        database.deletePersistent(s1);
-        database.deletePersistent(sd);
-        tx.commit();
-    }
-
-    /**
-     * Class Shop has a bidirectional 1:1 reference with ShopDetail and the DB table of Shop
-     * has a foreign key constraint on ShopDetail table. Shop has a m:n relation with Distributor.
-     * <p/>
-     * If the user take care of the ordering itself the test pass.
-     */
-    public void testBidirectionalWithConstraint_2c() throws Exception
-    {
-        String name = "testBidirectionalWithConstraint_2c_" + System.currentTimeMillis();
-        TransactionExt tx = (TransactionExt) odmg.newTransaction();
-        tx.begin();
-        // When using flush() we can use a more "natural" object persisting order
-        Shop s1 = new Shop(name + "_1");
-        Distributor d1 = new Distributor(name + "_1");
-        s1.addDistributor(d1);
-        d1.addShop(s1);
+        // When using flush() we can add objects step by step
         database.makePersistent(s1);
         tx.flush();
 
+        // add the shop detail object to Shop
         ShopDetail sd = new ShopDetail(name + "_1");
         s1.setDetail(sd);
         sd.setShop(s1);
 
         tx.commit();
 
-        // Delete all created objects
+        // Delete all created objects, we disable implicit
+        // locking and ordering to avoid constraint error
+        tx.setImplicitLocking(false);
+        tx.setOrdering(false);
         tx.begin();
         database.deletePersistent(d1);
         database.deletePersistent(s1);
@@ -1414,7 +1645,7 @@
         tx.getBroker().clearCache();
 
         OQLQuery query = odmg.newOQLQuery();
-        query.create("select products from " + Shop.class.getName() + " where name like $1");
+        query.create("select shops from " + Shop.class.getName() + " where name like $1");
         query.bind(name + "%");
         Collection result = (Collection) query.execute();
         tx.commit();
@@ -1428,6 +1659,8 @@
         for(Iterator iterator = newShop.getProducts().iterator(); iterator.hasNext();)
         {
             Product p = (Product) iterator.next();
+            assertNotNull(p.getShop());
+            assertEquals(s.getId(), p.getShop().getId());
             if(p.getSubProducts() != null && p.getSubProducts().size() > 0)
             {
                 match = true;
@@ -1659,6 +1892,10 @@
         TransactionExt tx = (TransactionExt) odmg.newTransaction();
         tx.begin();
         database.makePersistent(s);
+        // before establishing the circular references write
+        // all objects to DB
+        tx.flush();
+        // now close the circular references
         tx.commit();
 
         tx.begin();
@@ -1714,12 +1951,11 @@
         sd.setName(name);
         s.setDetail(sd);
         sd.setShop(s);
+
         TransactionExt tx = (TransactionExt) odmg.newTransaction();
         tx.begin();
-        database.makePersistent(sd);
         database.makePersistent(s);
         database.deletePersistent(s);
-        database.makePersistent(sd);
         database.makePersistent(s);
         tx.commit();
 
@@ -1735,24 +1971,28 @@
         assertNotNull(newShop.getDetail());
 
         tx.begin();
-        // We enable cascading delete for all references of Shop class
-        tx.setCascadingDelete(Shop.class, true);
         database.deletePersistent(newShop);
+        // add object again, we expect that nothing was deleted
         database.makePersistent(newShop);
+        // flush changes to DB, we don't change anything
         tx.flush();
-
         query = odmg.newOQLQuery();
         query.create("select shops from " + Shop.class.getName() + " where name like $1");
         query.bind(name);
         result = (Collection) query.execute();
         tx.commit();
+
         assertEquals(1, result.size());
+        Shop tmp = (Shop) result.iterator().next();
+        assertNotNull(tmp.getDetail());
 
         tx.begin();
         database.deletePersistent(newShop);
         database.makePersistent(newShop);
         database.deletePersistent(newShop);
         tx.flush();
+        database.deletePersistent(newShop.getDetail());
+        tx.flush();
 
         query = odmg.newOQLQuery();
         query.create("select detail from " + ShopDetail.class.getName() + " where name like $1");
@@ -1794,8 +2034,12 @@
 
         TransactionExt tx = (TransactionExt) odmg.newTransaction();
         tx.begin();
-        database.makePersistent(sd);
         database.makePersistent(s);
+        // shop and shopDetail have a bidirectional 1:1 reference
+        // on flush() OJB will insert both objects and set one FK
+        tx.flush();
+        // on commit() OJB will be aware of the bidirectional reference
+        // and set the second FK
         tx.commit();
 
         tx.begin();
@@ -1812,9 +2056,8 @@
         assertNotNull(sdNew.getShop());
 
         tx.begin();
-        // cascading delete should not affect Shop deletion
+        // cascading delete should delete Shop too
         tx.setCascadingDelete(ShopDetail.class, true);
-        database.deletePersistent(sdNew.getShop());
         database.deletePersistent(sdNew);
         tx.flush();
 
@@ -1909,6 +2152,19 @@
             }
             this.distributors.add(d);
         }
+
+        public boolean equals(Object obj)
+        {
+            if(obj instanceof Shop)
+            {
+                Shop tmp = (Shop) obj;
+                return new EqualsBuilder().append(id, tmp.id).append(name, tmp.name).isEquals();
+    }
+            else
+            {
+                return this == obj;
+            }
+        }
     }
 
     public static class Distributor
@@ -2098,9 +2354,22 @@
         {
             this.shop = shop;
         }
+
+        public boolean equals(Object obj)
+        {
+            if(obj instanceof ShopDetail)
+            {
+                ShopDetail tmp = (ShopDetail) obj;
+                return new EqualsBuilder().append(id, tmp.id).append(name, tmp.name).isEquals();
+    }
+            else
+            {
+                return this == obj;
+            }
+        }
     }
 
-    abstract static class BaseObject
+    public abstract static class BaseObject
     {
         private Integer id;
         private String name;

Modified: db/ojb/trunk/src/test/org/apache/ojb/odmg/CollectionsTest.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/test/org/apache/ojb/odmg/CollectionsTest.java?rev=422240&r1=422239&r2=422240&view=diff
==============================================================================
--- db/ojb/trunk/src/test/org/apache/ojb/odmg/CollectionsTest.java (original)
+++ db/ojb/trunk/src/test/org/apache/ojb/odmg/CollectionsTest.java Sat Jul 15 07:49:27 2006
@@ -8,22 +8,24 @@
 import java.util.List;
 import java.util.Vector;
 
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+import org.apache.ojb.broker.Identity;
 import org.apache.ojb.broker.PersistenceBroker;
 import org.apache.ojb.broker.PersistenceBrokerFactory;
+import org.apache.ojb.broker.PersistenceBrokerInternal;
+import org.apache.ojb.broker.core.proxy.CollectionProxy;
 import org.apache.ojb.broker.metadata.CollectionDescriptor;
 import org.apache.ojb.broker.query.Criteria;
 import org.apache.ojb.broker.query.Query;
 import org.apache.ojb.broker.query.QueryFactory;
 import org.apache.ojb.junit.ODMGTestCase;
-import org.apache.commons.lang.builder.ToStringBuilder;
-import org.apache.commons.lang.builder.ToStringStyle;
 import org.odmg.OQLQuery;
 import org.odmg.Transaction;
 
 /**
  * Test case handles with collections.
  *
- * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
  * @version $Id$
  */
 public class CollectionsTest extends ODMGTestCase
@@ -49,6 +51,138 @@
         super.tearDown();
     }
 
+    private PersistenceBrokerInternal getInternalPB(TransactionExt tx)
+    {
+        return (PersistenceBrokerInternal) tx.getBroker();
+    }
+
+    public void testProxiedReferences() throws Exception
+    {
+        ojbChangeReferenceSetting(Gatherer.class, "collectiblesA", true,
+                CollectionDescriptor.CASCADE_NONE, CollectionDescriptor.CASCADE_OBJECT, true);
+
+        String prefix = "testProxiedReferences_" + System.currentTimeMillis();
+        String queryId = "select gatherer from " + Gatherer.class.getName() + " where gatId=$1";
+        String queryName = "select gatherer from " + Gatherer.class.getName() + " where name=$1";
+
+        // prepare test case
+        Gatherer gat = new Gatherer(null, prefix + "_Gatherer");
+        CollectibleA[] cols = prepareCollectibleA(gat, prefix);
+        List colList = Arrays.asList(cols);
+        // set List of CollectiblesA objects
+        gat.setCollectiblesA(colList);
+        TransactionExt tx = (TransactionExt)odmg.newTransaction();
+        tx.begin();
+        database.makePersistent(gat);
+        tx.commit();
+
+        // check if gatherer was stored
+        tx.begin();
+        // to run this test with all cache implementation classes
+        // clear the cache before query
+        tx.getBroker().clearCache();
+        OQLQuery query = odmg.newOQLQuery();
+        query.create(queryId);
+        Integer gatId = gat.getGatId();
+        assertNotNull(gatId);
+        query.bind(gatId);
+        Collection result = (Collection) query.execute();
+        assertNotNull(result);
+        assertEquals(1, result.size());
+        Gatherer newGat = (Gatherer) result.iterator().next();
+        assertTrue(getInternalPB(tx).getProxyFactory().isCollectionProxy(newGat.getCollectiblesA()));
+        CollectionProxy cp = getInternalPB(tx).getProxyFactory().getCollectionProxy(newGat.getCollectiblesA());
+        assertFalse(cp.isLoaded());
+        tx.commit();
+
+        tx.begin();
+        query = odmg.newOQLQuery();
+        query.create(queryName);
+        query.bind(gat.getName());
+        result = (Collection) query.execute();
+        assertNotNull(result);
+        assertEquals(1, result.size());
+        newGat = (Gatherer) result.iterator().next();
+        assertTrue(getInternalPB(tx).getProxyFactory().isCollectionProxy(newGat.getCollectiblesA()));
+        cp = getInternalPB(tx).getProxyFactory().getCollectionProxy(newGat.getCollectiblesA());
+        assertFalse(cp.isLoaded());
+        tx.commit();
+
+        tx.begin();
+        PersistenceBroker pb = tx.getBroker();
+        Identity oid = pb.serviceIdentity().buildIdentity(Gatherer.class, gat.getGatId());
+        newGat = (Gatherer) pb.getObjectByIdentity(oid);
+        assertNotNull(newGat);
+        assertTrue(getInternalPB(tx).getProxyFactory().isCollectionProxy(newGat.getCollectiblesA()));
+        cp = getInternalPB(tx).getProxyFactory().getCollectionProxy(newGat.getCollectiblesA());
+        assertFalse(cp.isLoaded());
+        tx.commit();
+    }
+
+    /**
+     * tests behavior of 1:n references in OJB (odmg-api) when auto-retrieve is disabled.
+     * @throws Exception
+     */
+    public void testSetAutoRetrieveFalse() throws Exception
+    {
+        ojbChangeReferenceSetting(Gatherer.class, "collectiblesA", false,
+                CollectionDescriptor.CASCADE_NONE, CollectionDescriptor.CASCADE_OBJECT, true);
+        ojbChangeReferenceSetting(Gatherer.class, "collectiblesC", false,
+                CollectionDescriptor.CASCADE_NONE, CollectionDescriptor.CASCADE_OBJECT, true);
+
+        String prefix = "testSetAutoRetrieveFalse_" + System.currentTimeMillis();
+
+        // prepare test case
+        Gatherer gat = new Gatherer(null, prefix + "_Gatherer");
+        CollectibleA[] cols = prepareCollectibleA(gat, prefix);
+        List colList = Arrays.asList(cols);
+        // set List of CollectiblesA objects
+        gat.setCollectiblesA(colList);
+        TransactionExt tx = (TransactionExt)odmg.newTransaction();
+        tx.begin();
+        database.makePersistent(gat);
+        tx.commit();
+
+        tx.begin();
+        PersistenceBroker pb = tx.getBroker();
+        pb.clearCache();
+        Identity oid = pb.serviceIdentity().buildIdentity(Gatherer.class, gat.getGatId());
+        Gatherer newGat = (Gatherer) pb.getObjectByIdentity(oid);
+        tx.commit();
+
+        tx.begin();
+        tx.lock(newGat, Transaction.WRITE);
+        newGat.setName(prefix + "_updated");
+        //tx.getBroker().retrieveAllReferences(newGat);
+        tx.commit();
+
+        tx.begin();
+        pb = tx.getBroker();
+        newGat = (Gatherer) pb.getObjectByIdentity(oid);
+        pb.retrieveAllReferences(newGat);
+        assertEquals(cols.length, newGat.getCollectiblesA().size());
+        tx.commit();
+
+        tx.begin();
+        pb = tx.getBroker();
+        pb.clearCache();
+        newGat = (Gatherer) pb.getObjectByIdentity(oid);
+        tx.commit();
+
+        tx.begin();
+        tx.lock(newGat, Transaction.WRITE);
+        newGat.addCollectibleA(new CollectibleA(prefix + "_later_added"));
+        tx.commit();
+
+        tx.begin();
+        pb = tx.getBroker();
+        newGat = (Gatherer) pb.getObjectByIdentity(oid);
+        pb.retrieveAllReferences(newGat);
+        tx.commit();
+        assertNotNull(newGat);
+        assertEquals(cols.length + 1, newGat.getCollectiblesA().size());
+
+    }
 
     public void testStoreDeleteCascadeDelete() throws Exception
     {
@@ -475,8 +609,8 @@
         // we want automatic delete of removed collection objects
         //tx.autoDeleteRemovedCollectionReferences(true);
         // alternative do explicit call Database#deletePersistent for removed objects
-        // fetchedGat.getCollectiblesA().remove(0)
-        // fetchedGat.getCollectiblesB().remove(0);
+        fetchedGat.getCollectiblesA().remove(0);
+        fetchedGat.getCollectiblesB().remove(0);
 tx.getBroker().serviceObjectCache().cache(tx.getBroker().serviceIdentity().buildIdentity(fetchedGat), fetchedGat);
 //System.out.println("remove: " + tx.getBroker().serviceIdentity().buildIdentity(fetchedGat.getCollectiblesA().remove(0)));
 //System.out.println("remove: " + tx.getBroker().serviceIdentity().buildIdentity(fetchedGat.getCollectiblesB().remove(0)));
@@ -538,7 +672,7 @@
         tx.commit();
 
         tx.begin();
-        ((TransactionExt)tx).getBroker().clearCache();
+        tx.getBroker().clearCache();
         OQLQuery query = odmg.newOQLQuery();
         query.create(queryStr);
         query.bind(name);
@@ -558,7 +692,7 @@
         tx.commit();
 
         tx.begin();
-        ((TransactionExt)tx).getBroker().clearCache();
+        tx.getBroker().clearCache();
         query = odmg.newOQLQuery();
         query.create(queryStr);
         query.bind(name);
@@ -581,7 +715,7 @@
         tx.commit();
 
         tx.begin();
-        ((TransactionExt)tx).getBroker().clearCache();
+        tx.getBroker().clearCache();
         query = odmg.newOQLQuery();
         query.create(queryStr);
         query.bind(name);
@@ -1169,9 +1303,9 @@
     {
         private Integer gatId;
         private String name;
-        private List collectiblesA = new Vector();
-        private List collectiblesB = new Vector();
-        private List collectiblesC = new Vector();
+        private List collectiblesA = new ArrayList();
+        private List collectiblesB = new ArrayList();
+        private List collectiblesC = new ArrayList();
 
         public Gatherer()
         {



---------------------------------------------------------------------
To unsubscribe, e-mail: ojb-dev-unsubscribe@db.apache.org
For additional commands, e-mail: ojb-dev-help@db.apache.org