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 Vadim Gritsenko <va...@reverycodes.com> on 2004/12/30 01:59:08 UTC

Is "Mapping Classes on Multiple Joined Tables" supported?

Hey All,

[Using OJB_1_0_RELEASE branch from CVS]

As other users here, I'm also struggling with mapping class hierarchy on 
multiple joined tables, and can't figure out what's wrong and whether it is 
working at all or not.

Consider this slightly modified (added relation to the class C) example from the 
OJB manual page [1]:

/**
  * @ojb.class
  * @ojb.extent-class class-ref="B"
  */
public class A {
     /** @ojb.field primarykey="true" autoincrement="ojb" */
     Integer id;
     /** @ojb.field */
     Integer cId;
     /** @ojb.reference foreignkey="cId" */
     C c;
}

/**
  * @ojb.class include-inherited="false"
  * @ojb.reference class-ref="A" foreignkey="aId"
  *     auto-retrieve="true" auto-update="object" auto-delete="object"
  */
public class B extends A
{
     /** @ojb.field primarykey="true" autoincrement="ojb" */
     Integer id;
     /** @ojb.field */
     Integer aId;
     /** @ojb.field length="254" */
     String value;
}

/**
  * @ojb.class
  */
public class C {
     /** @ojb.field primarykey="true" autoincrement="ojb" */
     Integer id;
     /** @ojb.field length="30" */
     String name;
}


Ok, once we have that, problems start. First problem is that xdoclet does not 
like include-inherited="false", and when include-inherited="true", it generates 
all inherited attributes into repository.xml - but we don't want them there! 
After some patching of xdoclet [2], we can get desired result:

<class-descriptor class="A" table="A_TABLE">
   <extent-class class-ref="B"/>
   <field-descriptor name="id" column="id" jdbc-type="INTEGER"
                     primarykey="true" autoincrement="true"/>
   <field-descriptor name="cId" column="cId" jdbc-type="INTEGER"/>
   <reference-descriptor name="c" class-ref="C">
     <foreignkey field-ref="cId"/>
   </reference-descriptor>
</class-descriptor>

<class-descriptor class="B" table="B_TABLE">
   <field-descriptor name="id" column="id" jdbc-type="INTEGER"
                     primarykey="true" autoincrement="true"/>
   <field-descriptor name="aId" column="aId" jdbc-type="INTEGER"/>
   <field-descriptor name="value" column="value"
                     jdbc-type="VARCHAR" length="254"/>
   <reference-descriptor name="super" class-ref="A"
        auto-retrieve="true" auto-update="object" auto-delete="object">
     <foreignkey field-ref="aId"/>
   </reference-descriptor>
</class-descriptor>

<class-descriptor class="C" table="C_TABLE">
   <field-descriptor name="id" column="id" jdbc-type="INTEGER"
                     primarykey="true" autoincrement="true"/>
   <field-descriptor name="value" column="value"
                     jdbc-type="VARCHAR" length="30"/>
</class-descriptor>


Assuming that all of the above is correct, I'm getting following problems on run 
time:

1. First problem happens when trying to create instance of B (and assuming that 
there is a record for C) with the code:
     B b = new B();
     b.cId = 1;
     b.value = "dummy";
     broker.store(b);
This fails with KeyConstraintViolatedException. Seems it is happening because 
B's ClassDescriptor.getSuperClass() returns null and 
PersistentBrokerImpl.storeToDb does not store base class. So what's wrong, and 
can somebody clarify what's the difference between getBaseClass and 
getSuperClass methods? At least getBaseClass() is populated correctly.

2. Second problem happens when trying to load existing instance from the DB. 
Fails with OJBRuntimeException "Incorrect or not found field reference name 
'cId' in descriptor <here comes A's CollectionDescriptor> for class-descriptor 'B'.
Seems like ObjectReferenceDescriptor does not take into account B's super class 
at all.

Any ideas what I'm doing wrong?

Thanks,
Vadim


[1] 
http://db.apache.org/ojb/docu/guides/advanced-technique.html#Mapping+Classes+on+Multiple+Joined+Tables
See class B after "Now let us consider a case where B_TABLE contains an 
additional foreign key column B_TABLE.A_ID referencing A_TABLE.ID".

[2] Patch to ModelConstraints:
@@ -238,7 +238,13 @@

              for (Iterator it = elementClass.getExtentClasses(); it.hasNext();)
              {
-                queue.add(it.next());
+                ClassDescriptorDef extentClass = (ClassDescriptorDef) it.next();
+                // Exclude classes which belong to the "Mapping Classes on 
Multiple Joined Tables" scenario.
+                if 
(!extentClass.getBooleanProperty(PropertyHelper.OJB_PROPERTY_INCLUDE_INHERITED, 
true))
+                {
+                    continue;
+                }
+                queue.add(extentClass);
              }


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


Re: Is "Mapping Classes on Multiple Joined Tables" supported?

Posted by Thomas Dudziak <to...@gmail.com>.
> I'm sorry, I forgot to add Collection of A's in C (see above). With this
> collection, xdoclet fails - for both 1.0.1 release and 1.0.x branch (I'm working
> off the branch as mentioned above).

Ok, right, in this case I too get the error that you described.
 
> Yes, it is not: first, you have to create record in table C, before trying
> inserting record into table A. More on this below...

Please add an issue in OJB's Scarab for this problem (which is not
XDoclet related, but rather a bug in the handling of inheritance
mapped to multiple tables).
 
> But it's not desirable - fk constraints are a good thing to have.

Yup, it is just a workaround to make it work and get the above error.
 
> Well, it's getting more strange still... I ran your code (without modifying C
> class) and got (starting with first insert):

<snip> 
 
> As you can see, first insert happens for A, not for C - which is, IIUC, wrong.
> So, what's the difference - why it was working for you?

It worked for me because I did disable the foreingkey-generation (see above).

It is an error in the XDoclet module, but the fix is a bit more
complicated, I think. I'll keep you updated.

Tom

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


Re: Is "Mapping Classes on Multiple Joined Tables" supported?

Posted by Vadim Gritsenko <va...@reverycodes.com>.
Thomas Dudziak wrote:
> Vadim Gritsenko wrote:
> 
>> Hey All,
>>
>> [Using OJB_1_0_RELEASE branch from CVS]
>>
>> As other users here, I'm also struggling with mapping class hierarchy 
>> on multiple joined tables, and can't figure out what's wrong and 
>> whether it is working at all or not.
>>
>> Consider this slightly modified (added relation to the class C) 
>> example from the OJB manual page [1]:
>>
>> /**
>>  * @ojb.class
>>  * @ojb.extent-class class-ref="B"
>>  */
>> public class A {
>>     /** @ojb.field primarykey="true" autoincrement="ojb" */
>>     Integer id;
>>     /** @ojb.field */
>>     Integer cId;
>>     /** @ojb.reference foreignkey="cId" */
>>     C c;
>> }
>>
>> /**
>>  * @ojb.class include-inherited="false"
>>  * @ojb.reference class-ref="A" foreignkey="aId"
>>  *     auto-retrieve="true" auto-update="object" auto-delete="object"
>>  */
>> public class B extends A
>> {
>>     /** @ojb.field primarykey="true" autoincrement="ojb" */
>>     Integer id;
>>     /** @ojb.field */
>>     Integer aId;
>>     /** @ojb.field length="254" */
>>     String value;
>> }
>>
>> /**
>>  * @ojb.class
>>  */
>> public class C {
>>     /** @ojb.field primarykey="true" autoincrement="ojb" */
>>     Integer id;
>>     /** @ojb.field length="30" */
>>     String name;

     /** @ojb.collection element-class-ref="A" foreignkey="cId" */
     Collection a;

>> }
>>
>>
>> Ok, once we have that, problems start. First problem is that xdoclet 
>> does not like include-inherited="false", and when 
>> include-inherited="true", it generates all inherited attributes into 
>> repository.xml - but we don't want them there! After some patching of 
>> xdoclet [2], we can get desired result:
> 
> 
> Mhmm, don't know what version of the XDoclet module you're using but 
> with the classes that you posted it works for me out-of-the-box (current 
> version that comes with 1.0.1)

I'm sorry, I forgot to add Collection of A's in C (see above). With this 
collection, xdoclet fails - for both 1.0.1 release and 1.0.x branch (I'm working 
off the branch as mentioned above).


> and generates exactly this repository file:
> 
>>
>> <class-descriptor class="A" table="A_TABLE">
>>   <extent-class class-ref="B"/>
>>   <field-descriptor name="id" column="id" jdbc-type="INTEGER"
>>                     primarykey="true" autoincrement="true"/>
>>   <field-descriptor name="cId" column="cId" jdbc-type="INTEGER"/>
>>   <reference-descriptor name="c" class-ref="C">
>>     <foreignkey field-ref="cId"/>
>>   </reference-descriptor>
>> </class-descriptor>
>>
>> <class-descriptor class="B" table="B_TABLE">
>>   <field-descriptor name="id" column="id" jdbc-type="INTEGER"
>>                     primarykey="true" autoincrement="true"/>
>>   <field-descriptor name="aId" column="aId" jdbc-type="INTEGER"/>
>>   <field-descriptor name="value" column="value"
>>                     jdbc-type="VARCHAR" length="254"/>
>>   <reference-descriptor name="super" class-ref="A"
>>        auto-retrieve="true" auto-update="object" auto-delete="object">
>>     <foreignkey field-ref="aId"/>
>>   </reference-descriptor>
>> </class-descriptor>
>>
>> <class-descriptor class="C" table="C_TABLE">
>>   <field-descriptor name="id" column="id" jdbc-type="INTEGER"
>>                     primarykey="true" autoincrement="true"/>
>>   <field-descriptor name="value" column="value"
>>                     jdbc-type="VARCHAR" length="30"/>

     <collection-descriptor name="a" element-class-ref="A">
         <inverse-foreignkey field-ref="cId"/>
     </collection-descriptor>

>> </class-descriptor>
>>
>>
>> Assuming that all of the above is correct, I'm getting following 
>> problems on run time:
>>
>> 1. First problem happens when trying to create instance of B (and 
>> assuming that there is a record for C) with the code:
>>     B b = new B();
>>     b.cId = 1;
>>     b.value = "dummy";
>>     broker.store(b);
>> This fails with KeyConstraintViolatedException. Seems it is happening 
>> because B's ClassDescriptor.getSuperClass() returns null and 
>> PersistentBrokerImpl.storeToDb does not store base class. So what's 
>> wrong, and can somebody clarify what's the difference between 
>> getBaseClass and getSuperClass methods? At least getBaseClass() is 
>> populated correctly.
> 
> 
> I'm no expert on the mapping to multiple tables, but this is actually a 
> SQL exception coming from the database (I suspect Hsqldb in your case) 
> saying that the foreignkey constraint is violated while inserting a row 
> into the A table. This can only mean that the referenced C object is not 
> present.

Yes, it is not: first, you have to create record in table C, before trying 
inserting record into table A. More on this below...


> You can turn off the generation of the foreign key constraints via the 
> generate-foreignkey attribute of the torqueschema Ant task

But it's not desirable - fk constraints are a good thing to have.


>> 2. Second problem happens when trying to load existing instance from 
>> the DB. Fails with OJBRuntimeException "Incorrect or not found field 
>> reference name 'cId' in descriptor <here comes A's 
>> CollectionDescriptor> for class-descriptor 'B'.
>> Seems like ObjectReferenceDescriptor does not take into account B's 
>> super class at all.
> 
> 
> Strange, this code works just fine for me:
> 
>        B bObj = new B();
>        C cObj = new C();
> 
>        cObj.name  = "The C instance";
>        bObj.value = "The B instance";
>        bObj.c     = cObj;
> 
>        PersistenceBroker broker = 
> PersistenceBrokerFactory.defaultPersistenceBroker();
> 
>        broker.beginTransaction();
>        broker.store(bObj);
>        broker.commitTransaction();
>        broker.close();
> 
>        bObj = null;
>        cObj = null;
> 
>        Criteria criteria = new Criteria();
> 
>        criteria.addEqualTo("value", "The B instance");
> 
>        QueryByCriteria query = new QueryByCriteria(B.class, criteria);
> 
>        broker = PersistenceBrokerFactory.defaultPersistenceBroker();
>        broker.beginTransaction();
>        bObj = (B)broker.getObjectByQuery(query);
>        broker.abortTransaction();
>        System.out.println("Loaded B: '"+bObj.value+"' referencing C: 
> '"+bObj.c.name+"'");

Well, it's getting more strange still... I ran your code (without modifying C 
class) and got (starting with first insert):

[org.apache.ojb.broker.accesslayer.sql.SqlGeneratorDefaultImpl] DEBUG: 
SQL:INSERT INTO A (id,cId) VALUES (?,?)
[org.apache.ojb.broker.accesslayer.JdbcAccessImpl] DEBUG: executeInsert: 
org.hsqldb.jdbcPreparedStatement@110003
Exception in thread "main" org.apache.ojb.broker.KeyConstraintViolatedException: 
SQL failure while insert object data fo
r class A, PK of the given object is [ id=42], object was A@12d96f2, exception 
message is [Integrity constraint violatio
n: A_FK_1 table: C in statement [INSERT INTO A (id,cId) VALUES (42,41) ]]
         at 
org.apache.ojb.broker.accesslayer.JdbcAccessImpl.executeInsert(JdbcAccessImpl.java:239)
         at 
org.apache.ojb.broker.core.PersistenceBrokerImpl.storeToDb(PersistenceBrokerImpl.java:1642)
         at 
org.apache.ojb.broker.core.PersistenceBrokerImpl.store(PersistenceBrokerImpl.java:1557)
         at 
org.apache.ojb.broker.core.PersistenceBrokerImpl.store(PersistenceBrokerImpl.java:715)
         at 
org.apache.ojb.broker.core.PersistenceBrokerImpl.storeAndLinkOneToOne(PersistenceBrokerImpl.java:772)
         at 
org.apache.ojb.broker.core.PersistenceBrokerImpl.storeReferences(PersistenceBrokerImpl.java:755)
         at 
org.apache.ojb.broker.core.PersistenceBrokerImpl.storeToDb(PersistenceBrokerImpl.java:1594)
         at 
org.apache.ojb.broker.core.PersistenceBrokerImpl.store(PersistenceBrokerImpl.java:1557)
         at 
org.apache.ojb.broker.core.PersistenceBrokerImpl.store(PersistenceBrokerImpl.java:715)
         at 
org.apache.ojb.broker.core.DelegatingPersistenceBroker.store(DelegatingPersistenceBroker.java:175)
         at 
org.apache.ojb.broker.core.DelegatingPersistenceBroker.store(DelegatingPersistenceBroker.java:175)
         at Test.main(Unknown Source)

As you can see, first insert happens for A, not for C - which is, IIUC, wrong. 
So, what's the difference - why it was working for you?

Using ojb-blank, with hsqldb (just for testing). If it helps, you can get all 
code here:
    http://reverycodes.com/apache/ojb/ojb-blank-abc.zip


Thanks,
Vadim


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


Re: Is "Mapping Classes on Multiple Joined Tables" supported?

Posted by Thomas Dudziak <to...@gmail.com>.
Vadim Gritsenko wrote:

> Hey All,
>
> [Using OJB_1_0_RELEASE branch from CVS]
>
> As other users here, I'm also struggling with mapping class hierarchy 
> on multiple joined tables, and can't figure out what's wrong and 
> whether it is working at all or not.
>
> Consider this slightly modified (added relation to the class C) 
> example from the OJB manual page [1]:
>
> /**
>  * @ojb.class
>  * @ojb.extent-class class-ref="B"
>  */
> public class A {
>     /** @ojb.field primarykey="true" autoincrement="ojb" */
>     Integer id;
>     /** @ojb.field */
>     Integer cId;
>     /** @ojb.reference foreignkey="cId" */
>     C c;
> }
>
> /**
>  * @ojb.class include-inherited="false"
>  * @ojb.reference class-ref="A" foreignkey="aId"
>  *     auto-retrieve="true" auto-update="object" auto-delete="object"
>  */
> public class B extends A
> {
>     /** @ojb.field primarykey="true" autoincrement="ojb" */
>     Integer id;
>     /** @ojb.field */
>     Integer aId;
>     /** @ojb.field length="254" */
>     String value;
> }
>
> /**
>  * @ojb.class
>  */
> public class C {
>     /** @ojb.field primarykey="true" autoincrement="ojb" */
>     Integer id;
>     /** @ojb.field length="30" */
>     String name;
> }
>
>
> Ok, once we have that, problems start. First problem is that xdoclet 
> does not like include-inherited="false", and when 
> include-inherited="true", it generates all inherited attributes into 
> repository.xml - but we don't want them there! After some patching of 
> xdoclet [2], we can get desired result:

Mhmm, don't know what version of the XDoclet module you're using but 
with the classes that you posted it works for me out-of-the-box (current 
version that comes with 1.0.1) and generates exactly this repository file:

>
> <class-descriptor class="A" table="A_TABLE">
>   <extent-class class-ref="B"/>
>   <field-descriptor name="id" column="id" jdbc-type="INTEGER"
>                     primarykey="true" autoincrement="true"/>
>   <field-descriptor name="cId" column="cId" jdbc-type="INTEGER"/>
>   <reference-descriptor name="c" class-ref="C">
>     <foreignkey field-ref="cId"/>
>   </reference-descriptor>
> </class-descriptor>
>
> <class-descriptor class="B" table="B_TABLE">
>   <field-descriptor name="id" column="id" jdbc-type="INTEGER"
>                     primarykey="true" autoincrement="true"/>
>   <field-descriptor name="aId" column="aId" jdbc-type="INTEGER"/>
>   <field-descriptor name="value" column="value"
>                     jdbc-type="VARCHAR" length="254"/>
>   <reference-descriptor name="super" class-ref="A"
>        auto-retrieve="true" auto-update="object" auto-delete="object">
>     <foreignkey field-ref="aId"/>
>   </reference-descriptor>
> </class-descriptor>
>
> <class-descriptor class="C" table="C_TABLE">
>   <field-descriptor name="id" column="id" jdbc-type="INTEGER"
>                     primarykey="true" autoincrement="true"/>
>   <field-descriptor name="value" column="value"
>                     jdbc-type="VARCHAR" length="30"/>
> </class-descriptor>
>
>
> Assuming that all of the above is correct, I'm getting following 
> problems on run time:
>
> 1. First problem happens when trying to create instance of B (and 
> assuming that there is a record for C) with the code:
>     B b = new B();
>     b.cId = 1;
>     b.value = "dummy";
>     broker.store(b);
> This fails with KeyConstraintViolatedException. Seems it is happening 
> because B's ClassDescriptor.getSuperClass() returns null and 
> PersistentBrokerImpl.storeToDb does not store base class. So what's 
> wrong, and can somebody clarify what's the difference between 
> getBaseClass and getSuperClass methods? At least getBaseClass() is 
> populated correctly.

I'm no expert on the mapping to multiple tables, but this is actually a 
SQL exception coming from the database (I suspect Hsqldb in your case) 
saying that the foreignkey constraint is violated while inserting a row 
into the A table. This can only mean that the referenced C object is not 
present.
You can turn off the generation of the foreign key constraints via the 
generate-foreignkey attribute of the torqueschema Ant task

>
> 2. Second problem happens when trying to load existing instance from 
> the DB. Fails with OJBRuntimeException "Incorrect or not found field 
> reference name 'cId' in descriptor <here comes A's 
> CollectionDescriptor> for class-descriptor 'B'.
> Seems like ObjectReferenceDescriptor does not take into account B's 
> super class at all.

Strange, this code works just fine for me:

        B bObj = new B();
        C cObj = new C();

        cObj.name  = "The C instance";
        bObj.value = "The B instance";
        bObj.c     = cObj;

        PersistenceBroker broker = 
PersistenceBrokerFactory.defaultPersistenceBroker();

        broker.beginTransaction();
        broker.store(bObj);
        broker.commitTransaction();
        broker.close();

        bObj = null;
        cObj = null;

        Criteria criteria = new Criteria();

        criteria.addEqualTo("value", "The B instance");

        QueryByCriteria query = new QueryByCriteria(B.class, criteria);

        broker = PersistenceBrokerFactory.defaultPersistenceBroker();
        broker.beginTransaction();
        bObj = (B)broker.getObjectByQuery(query);
        broker.abortTransaction();
        System.out.println("Loaded B: '"+bObj.value+"' referencing C: 
'"+bObj.c.name+"'");

It prints (as expected):

Loaded B: 'The B instance' referencing C: 'The C instance'

Tom


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


Re: Is "Mapping Classes on Multiple Joined Tables" supported?

Posted by Vadim Gritsenko <va...@reverycodes.com>.
Nils Liebelt wrote:
> Hi Vadim,
> 
> Stick to the Repository file I posted earlier! If you don't use the abstract
> modifier for the super class it works. 

I don't have abstract classes anywhere - my case is a bit different. It's about 
mapping classes A and B, where B extends A, to two tables, and class C has 
collection of A.

Vadim


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


RE: Is "Mapping Classes on Multiple Joined Tables" supported?

Posted by Nils Liebelt <ni...@candor.co.za>.
Hi Vadim,

Stick to the Repository file I posted earlier! If you don't use the abstract
modifier for the super class it works. 


Regards,

Nils



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