You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ojb-user@db.apache.org by Laurie Harper <zo...@holoweb.net> on 2002/12/18 08:33:39 UTC

[resolved] Re: Problems with m:n mappings

OK, I got there. Here is the list of things I had to fix (maybe incomplete):

- collection properties MUST be statically initialized to an empy 
collection otherwise OJB marks them as null and ignores assignments

- collection descriptor elements in repository_user.xml MUST occur in 
the right order with respect to other field-descriptor child elements 
(as defined by the DTD). The simple test case below generates over 6000 
(!!!!) lines of log info, so I'm not surprised I missed this error :(

- I was calling methods on my object model to materialise the members of 
the collection, which were implicitly using transactions to query the 
database. Unfortunately, the first query picks up the current 
transactoin (the one my code created) and closes it on completion. 
Subsequent queries implicitly grab new transactions. When control 
returns to my code, my transaction is no longer valid (due to the first 
query). Hence the mysterious 'no transaction in progress' error. [At 
least that's my theory; doing the queries before obtaining a transaction 
fixed the problem, anyway).

The first issue looks like a bug; why does it matter when the collection 
property is initialised?

The final issue is probably a side effect of not supporting nested 
transactions; if I create a transaction, then create and close another 
transaction, does that invalidate the first transaction?

L.

Laurie Harper wrote:

> This is driving me slightly nuts :-) I ran into a strange error tring to
>  add a collection property modelled by an indirection table in the
> database. If I do this (much abreviated):
>
> tx = odmg.newTransaction();
> tx.begin();
> tx.lock(project, WRITE);
> project.getList().add(foo);
> tx.commit();
>
> where the getList() method initialises the collection property to 'new
> LinkedList()' if it's not already set, I get an error about there being
> no transaction in progress. That's the problem I want to solve.
>
>
> In trying to understand what's going on, I tried to boil it down to a
> really simple test case. The test case consists of two classes and three
> tables modelling an n:m relationship. The problem is, it breaks in a
> different way than the above: when I add items to the collection
> property and commit, the indirection table isn't updated (so the
> collection property doesn't get persisted).
>
> The test case involves two simple classes, A and B. A has a collection
> of B. The database contains tables A and B and an indirection table A_B.
> I can load an instance of A from the database and OJB uses rows from A_B
> to populate A's collection property. But if I try and modify the
> collection property on A, nothing is written to the A_B table. Here's
> the various files that constitute my test case. Any suggestions what I'm
> doing wrong may help shed light on real problem, described above.
>
> One oddity is that the query to load the AB instances only works if I
> use the SQL table/column names in the OQL query! (see lines marked XXX
> in the test case below). I'm unclear about why that should be...
>
> Schema:
>
> CREATE SEQUENCE A_SEQ;
> CREATE SEQUENCE B_SEQ;
> CREATE SEQUENCE A_B_SEQ;
>
> CREATE TABLE A
> (
>     ID INTEGER DEFAULT nextval('A_SEQ') NOT NULL,
>     PRIMARY KEY (ID)
> );
>
> CREATE TABLE B
> (
>     ID INTEGER DEFAULT nextval('B_SEQ') NOT NULL,
>     PRIMARY KEY (ID)
> );
>
>
> CREATE TABLE A_B
> (
>     A_ID INTEGER DEFAULT nextval('A_B_SEQ') NOT NULL,
>     B_ID INTEGER DEFAULT nextval('A_B_SEQ') NOT NULL,
>     PRIMARY KEY (A_ID,B_ID)
> );
>
> ALTER TABLE A_B
>     ADD CONSTRAINT A_FK_1 FOREIGN KEY (A_ID) REFERENCES A (ID)
> ;
> ALTER TABLE A_B
>     ADD CONSTRAINT A_FK_2 FOREIGN KEY (B_ID) REFERENCES B (ID)
> ;
>
> Repository_user.xml entries:
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
> Object model:
>
> import java.util.List;
> import java.util.LinkedList;
>
> /**
>  * A has n:m relationship with B
>  */
> public class  A {
>     private int id;
>     public int getID() {
>         return id;
>     }
>
>     private List listOfB;
>     public List getListOfB() {
>         return (null == listOfB) ? new LinkedList() : listOfB;
>     }
> }
>
> /* --- */
>
> import java.util.List;
>
> /**
>  * B has m:n relationship with A
>  */
> public class B {
>     private int id;
>     public int getID() {
>         return id;
>     }
> }
>
> /* --- */
>
> import java.util.List;
> import java.util.LinkedList;
>
> /**
>  * Expose the A_B table so we can query it in the test cases.
>  */
> public class  AB {
>     private int aID, bID;
>     public int getAID() {
>         return aID;
>     }
>     public int getBID() {
>         return bID;
>     }
> }
>
> Test case:
>
> import java.util.List;
>
> import org.odmg.*;
> import org.apache.ojb.odmg.OJB;
> import junit.framework.TestCase;
> import junit.framework.TestSuite;
> import junit.framework.Test;
> import junit.textui.TestRunner;
>
> /**
>  * Creates instances of A and B that reference each other and persists 
> them.
>  */
> public class RelationshipTest extends TestCase {
>     private Implementation odmg;
>     private Database db;
>
>     public static void main(String[] args) {
>         TestRunner.run(RelationshipTest.class);
>     }
>     public static Test suite() {
>         return new TestSuite(RelationshipTest.class);
>     }
>     public RelationshipTest(String name) {
>         super(name);
>     }
>
>     public void setUp() throws ODMGException {
>         System.out.println("setup");
>         odmg = OJB.getInstance();
>         db = odmg.newDatabase();
>         db.open("repository.xml", Database.OPEN_READ_WRITE);
>     }
>
>     public void testCreate() throws Exception {
>         A a1;
>         B b1, b2;
>
>         // Create instances of B to be associated with an A
>         {
>         Transaction tx1 = odmg.newTransaction();
>         tx1.begin();
>         b1 = new B();
>         b2 = new B();
>         tx1.lock(b1, Transaction.WRITE);
>         tx1.lock(b2, Transaction.WRITE);
>         tx1.commit();
>         }
>
>         // Create an instance of A
>         {
>         Transaction tx2 = odmg.newTransaction();
>         tx2.begin();
>         a1 = new A();
>         tx2.lock(a1, Transaction.WRITE);
>         tx2.commit();
>         }
>
>         // Modify A's collection of Bs
>         {
>         Transaction tx3 = odmg.newTransaction();
>         tx3.begin();
>         List bs = a1.getListOfB(); // creates new list in a1
>         bs.add(b1);
>         bs.add(b2);
>         tx3.commit();
>         }
>
>         // Check that the A_B table was populated
>         {
>         OQLQuery q = odmg.newOQLQuery();
>         //q.create("select AB from AB where AB.aID = "+a1.getID());//XXX
>         q.create("select AB from AB where A_B.A_ID = "+a1.getID());//XXX
>         List abs = (DList) q.execute();
>         assertNotNull("abs", abs);
>         assertEquals("abs.size", 2, abs.size());
>         }
>     }
>
>     public void testLoad() throws Exception {
>         OQLQuery q = odmg.newOQLQuery();
>         q.create("select A from A where A.ID = 1");
>         A a = (A) ((DList) q.execute()).get(0);
>         List listOfB = a.getListOfB();
>
>         assertEquals("a.id", 1, a.getID());
>         assertNotNull("a.listOfB", listOfB);
>         assertEquals("a.bs.size", 2, listOfB.size());
>         assertEquals("a.bs[0].id", 1, ((B) listOfB.get(0)).getID());
>         assertEquals("a.bs[1].id", 2, ((B) listOfB.get(1)).getID());
>     }
> }
>
>
> --
> To unsubscribe, e-mail:
> For additional commands, e-mail: