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 [EMAIL PROTECTED] 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:[EMAIL PROTECTED]">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: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
