[ 
https://issues.apache.org/jira/browse/CAY-2667?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Matt Watson updated CAY-2667:
-----------------------------
    Description: 
I discovered a couple issues when trying to implement Vertical Inheritance 
using Generic Persistent Classes (Java-less)

*The 1st issue* is that one of the Subclasses will get the wrong Qualifier 
assigned because the wrong ObjEntity is returned during 
BaseContext.injectInitialValue because the MappingCache.objEntitiesByClassName 
is keyed by class-name so it can only hold one of the possible many generic 
ObjEntities.

See Test and DataMap implementing a Generic Student that has sub Entities of a 
Generic Boy and a Generic Girl.

It inserts 2 records into [gen_student], 1 record into [gen_boy], 1 record into 
[gen_girl], but the [type] on both student records is "G", instead of one "B" 
and one "G".

When creating the GenBoy ... the line:
 getEntityResolver().getObjEntity(object.getClass()) its returning the "Girl" 
ObjEntity
 the MappingCache.objEntitiesByClassName holds an entry for "CayenneDataObject" 
with value for Girl ObjEntity. When using Generics there will be many 
ObjEntities that use CayenneDataObject, so we shouldn't be asking for it from 
this cache Map.

instead we should use: getEntityResolver().getObjEntity((Persistent)object) 
because that one is properly getting the correct ObjEntity from the ObjectId, 
in MappingCache.getObjEntity(Persistent object)

*The 2nd issue* is that when querying for GenStudents, it returns the correct 
records, but only the Girl is populated and the Boy is HOLLOW, because the 
query generated does not do a left join to gen_boy.

{code:java}
ObjectSelect.query(DataObject.class, "GenStudent").select(context);
{code}

This happens because PersistentDescriptor.subclassDescriptors is a map keyed by 
a class name, so the 2nd one overwrites the first one. This map should be keyed 
by the ObjEntity name to support Generic Persistent Classes.

There is a test included that will show that with my fixes the 
PersistentDescriptor.subclassDescriptors now can hold the 2 ClassDescriptors 
for the 2 generic Sub ObjEntities.


  was:
When using Generic Classes (Java),

with the following db entities:
{code:xml}
        <db-entity name="student" schema="poly">
                <db-attribute name="name" type="VARCHAR" isMandatory="true" 
length="50"/>
                <db-attribute name="reference" type="VARCHAR" 
isMandatory="true" length="10"/>
                <db-attribute name="type" type="CHAR" isMandatory="true" 
length="1"/>
                <db-attribute name="uuid" type="BINARY" isPrimaryKey="true" 
isMandatory="true" length="16"/>
        </db-entity>

        <db-entity name="boy" schema="poly">
                <db-attribute name="toy_trucks" type="SMALLINT" length="4"/>
                <db-attribute name="uuid" type="BINARY" isPrimaryKey="true" 
isMandatory="true" length="16"/>
        </db-entity>
        <db-entity name="girl" schema="poly">
                <db-attribute name="toy_dolls" type="SMALLINT" length="4"/>
                <db-attribute name="uuid" type="BINARY" isPrimaryKey="true" 
isMandatory="true" length="16"/>
        </db-entity>
{code}
and the following obj entities:
{code:xml}
        <obj-entity name="Student" abstract="true" dbEntityName="student">
                <obj-attribute name="name" type="java.lang.String" 
db-attribute-path="name"/>
                <obj-attribute name="reference" type="java.lang.String" 
db-attribute-path="reference"/>
                <obj-attribute name="type" type="java.lang.String" 
db-attribute-path="type"/>
        </obj-entity>
        <obj-entity name="Boy" superEntityName="Student">
                <qualifier><![CDATA[type = "B"]]></qualifier>
                <obj-attribute name="toyTrucks" type="java.lang.Short" 
db-attribute-path="boy.toy_trucks"/>
        </obj-entity>
        <obj-entity name="Girl" superEntityName="Student">
                <qualifier><![CDATA[type = "G"]]></qualifier>
                <obj-attribute name="toyDolls" type="java.lang.Short" 
db-attribute-path="girl.toy_dolls"/>
        </obj-entity>
{code}
and proper db/obj relationships setup ..

 using the Test:
{code:java}
        final DataContext dataContext = (DataContext) runtime.newContext();
        final DataObject girlEmma = (DataObject) dataContext.newObject("Girl");
        girlEmma.writeProperty("reference", "g1");
        girlEmma.writeProperty("name", "Emma");
        girlEmma.writeProperty("toyDolls", 5);

        final DataObject boyLuke = (DataObject) dataContext.newObject("Boy");
        boyLuke.writeProperty("reference", "b1");
        boyLuke.writeProperty("name", "Luke");
        boyLuke.writeProperty("toyTrucks", 12);

        dataContext.commitChanges();
{code}
{noformat}
15:43:22.418 [main] INFO  org.apache.cayenne.log.JdbcEventLogger - --- 
transaction started.
15:43:22.420 [main] INFO  org.apache.cayenne.log.JdbcEventLogger - INSERT INTO 
poly.student (name, reference, type, uuid) VALUES (?, ?, ?, ?)
15:43:22.429 [main] INFO  org.apache.cayenne.log.JdbcEventLogger - [bind: 
1->name:'Emma', 2->reference:'g1', 3->type:'G', 
4->uuid:3fed46ae-a9d6-45c7-a306-402a0710e5a1]
15:43:22.435 [main] INFO  org.apache.cayenne.log.JdbcEventLogger - === updated 
1 row.
15:43:22.437 [main] INFO  org.apache.cayenne.log.JdbcEventLogger - [bind: 
1->name:'Luke', 2->reference:'b1', 3->type:'G', 
4->uuid:f113e95a-8727-414d-8ed4-ed08ba80c48b]
15:43:22.443 [main] INFO  org.apache.cayenne.log.JdbcEventLogger - === updated 
1 row.
15:43:22.445 [main] INFO  org.apache.cayenne.log.JdbcEventLogger - INSERT INTO 
poly.girl (toy_dolls, uuid) VALUES (?, ?)
15:43:22.450 [main] INFO  org.apache.cayenne.log.JdbcEventLogger - [bind: 
1->toy_dolls:5, 2->uuid:3fed46ae-a9d6-45c7-a306-402a0710e5a1]
15:43:22.454 [main] INFO  org.apache.cayenne.log.JdbcEventLogger - === updated 
1 row.
15:43:22.455 [main] INFO  org.apache.cayenne.log.JdbcEventLogger - INSERT INTO 
poly.boy (toy_trucks, uuid) VALUES (?, ?)
15:43:22.460 [main] INFO  org.apache.cayenne.log.JdbcEventLogger - [bind: 
1->toy_trucks:12, 2->uuid:f113e95a-8727-414d-8ed4-ed08ba80c48b]
15:43:22.464 [main] INFO  org.apache.cayenne.log.JdbcEventLogger - === updated 
1 row.
15:43:22.487 [main] INFO  org.apache.cayenne.log.JdbcEventLogger - +++ 
transaction committed.
{noformat}
 

It inserts 2 records into [student], 1 record into [boy], 1 record into [boy], 
but the [type] on both student records is "G", instead of one "B" and one "G".

Its doing this because of a bug during BaseContext.injectInitialValue when 
creating the Boy ... the line:
 getEntityResolver().getObjEntity(object.getClass()) its returning the "Girl" 
ObjEntity
 the MappingCache.objEntitiesByClassName holds an entry for "CayenneDataObject" 
with value for Girl ObjEntity. When using Generics there will be many 
ObjEntities that use CayenneDataObject, so we shouldn't be asking for it from 
this cache Map.

we should use: getEntityResolver().getObjEntity((Persistent)object) because 
that one is properly getting the correct ObjEntity from the ObjectId, in 
MappingCache.getObjEntity(Persistent object)

I can work on a breaking test, and potential fix

        Summary: Fix Issues Generic Vertical Inheritance  (was: Fix Wrong 
ObjEntity with Generic Vertical Inheritance)

> Fix Issues Generic Vertical Inheritance
> ---------------------------------------
>
>                 Key: CAY-2667
>                 URL: https://issues.apache.org/jira/browse/CAY-2667
>             Project: Cayenne
>          Issue Type: Bug
>          Components: Core Library
>            Reporter: Matt Watson
>            Priority: Major
>              Labels: Generic, inheritance, vertical
>
> I discovered a couple issues when trying to implement Vertical Inheritance 
> using Generic Persistent Classes (Java-less)
> *The 1st issue* is that one of the Subclasses will get the wrong Qualifier 
> assigned because the wrong ObjEntity is returned during 
> BaseContext.injectInitialValue because the 
> MappingCache.objEntitiesByClassName is keyed by class-name so it can only 
> hold one of the possible many generic ObjEntities.
> See Test and DataMap implementing a Generic Student that has sub Entities of 
> a Generic Boy and a Generic Girl.
> It inserts 2 records into [gen_student], 1 record into [gen_boy], 1 record 
> into [gen_girl], but the [type] on both student records is "G", instead of 
> one "B" and one "G".
> When creating the GenBoy ... the line:
>  getEntityResolver().getObjEntity(object.getClass()) its returning the "Girl" 
> ObjEntity
>  the MappingCache.objEntitiesByClassName holds an entry for 
> "CayenneDataObject" with value for Girl ObjEntity. When using Generics there 
> will be many ObjEntities that use CayenneDataObject, so we shouldn't be 
> asking for it from this cache Map.
> instead we should use: getEntityResolver().getObjEntity((Persistent)object) 
> because that one is properly getting the correct ObjEntity from the ObjectId, 
> in MappingCache.getObjEntity(Persistent object)
> *The 2nd issue* is that when querying for GenStudents, it returns the correct 
> records, but only the Girl is populated and the Boy is HOLLOW, because the 
> query generated does not do a left join to gen_boy.
> {code:java}
> ObjectSelect.query(DataObject.class, "GenStudent").select(context);
> {code}
> This happens because PersistentDescriptor.subclassDescriptors is a map keyed 
> by a class name, so the 2nd one overwrites the first one. This map should be 
> keyed by the ObjEntity name to support Generic Persistent Classes.
> There is a test included that will show that with my fixes the 
> PersistentDescriptor.subclassDescriptors now can hold the 2 ClassDescriptors 
> for the 2 generic Sub ObjEntities.



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

Reply via email to