You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@cayenne.apache.org by Dougan Stuart <do...@swarmbox.com> on 2016/07/13 17:40:31 UTC

Extended class issues

I have a class Company with a one-to-many relationship to other Companies (parentCompany <- subCompanies). I’m able to set a Company’s parent when creating one, but upon update I’m getting a NullPointerException in DefaultQuotingStrategy.quotedName because it’s been sent a null attribute. Company extends the abstract class Entity; on the DB side Entity and Company have a one-to-one relationship, and Entity has a parentEntity reference (which is modeled as parentCompany). The obj-relationship parentCompany is set up with the target Company and path parentEntity.

The null attribute is being added in org.apache.cayenne.access.DataDomainUpdateBucket.updatedAttributes. The method is called first with the dbEntity Entity and the an updatedSnapshot containing parentEntity, which is what I would expect. However, after this updatedAttributes is called with Company and an updatedSnapshot containing parentEntity, so the following line

attributes.add(entityAttributes.get(name))

will return a null attribute when trying to get the name “parentEntity” off Company’s attributes; that attribute only exists on Entity. I’d appreciate any help on preventing it from trying to add invalid attributes.

I’m not sure if this is the root cause, but  DataDomainUpdateBucket.appendQueriesInternal calls updatedAttributes based off the bucket’s dbEntities, which in this case are set in DataDomainSyncBucket.groupObjEntitiesBySpannedDbEntities; this method is only adding Company as a “secondary DbEntity” (as referred to in the comments) because Company has a flattened attribute “name" that does not exist on the Entity.

Here’s the stack trace:

java.lang.NullPointerException
	at org.apache.cayenne.dba.DefaultQuotingStrategy.quotedName(DefaultQuotingStrategy.java:62)
	at org.apache.cayenne.access.translator.batch.UpdateBatchTranslator.createSql(UpdateBatchTranslator.java:60)
	at org.apache.cayenne.access.translator.batch.DefaultBatchTranslator.ensureTranslated(DefaultBatchTranslator.java:52)
	at org.apache.cayenne.access.translator.batch.DefaultBatchTranslator.getSql(DefaultBatchTranslator.java:64)
	at org.apache.cayenne.access.jdbc.BatchAction.runAsBatch(BatchAction.java:103)
	at org.apache.cayenne.access.jdbc.BatchAction.performAction(BatchAction.java:90)
	at org.apache.cayenne.access.DataNodeQueryAction.runQuery(DataNodeQueryAction.java:97)
	at org.apache.cayenne.access.DataNode.performQueries(DataNode.java:293)
	at org.apache.cayenne.access.DataDomainFlushAction.runQueries(DataDomainFlushAction.java:233)
	at org.apache.cayenne.access.DataDomainFlushAction.flush(DataDomainFlushAction.java:154)
	at org.apache.cayenne.access.DataDomain.onSyncFlush(DataDomain.java:693)
	at org.apache.cayenne.access.DataDomain$2.transform(DataDomain.java:659)
	at org.apache.cayenne.access.DataDomain.runInTransaction(DataDomain.java:720)
	at org.apache.cayenne.access.DataDomain.onSyncNoFilters(DataDomain.java:655)
	at org.apache.cayenne.access.DataDomain$DataDomainSyncFilterChain.onSync(DataDomain.java:863)
	at org.apache.cayenne.access.DataDomain.onSync(DataDomain.java:636)
	at org.apache.cayenne.access.DataContext.flushToParent(DataContext.java:727)
	at org.apache.cayenne.access.DataContext.commitChanges(DataContext.java:676)

Thanks,
Doug


Re: Extended class issues

Posted by Dougan Stuart <do...@swarmbox.com>.
Your fix is working as expected for my original use case. Thanks for working on this!

Doug

> On Aug 5, 2016, at 6:47 AM, Savva Kolbachev <s....@gmail.com> wrote:
> 
> I've created a JIRA ticket for the issue CAY-2097
> <https://issues.apache.org/jira/browse/CAY-2097> and provided a fix
> https://github.com/apache/cayenne/commit/697f38e5127852a144b13e7640787e
> 57ac3ebba1
> 
> It's a little different from your. It's similar to how we check for
> properties in case of flattened attributes DataDomainDBDiffBuilder.java#L95
> <https://github.com/apache/cayenne/blob/master/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDBDiffBuilder.java#L95>
> 
> BTW, not sure that we should check it here. Perhaps, we should check it
> while filling currentPropertyDiff and currentArcDiff. Looks like in this
> case performance will be slightly better.
> 
> 2016-08-03 21:24 GMT+03:00 Savva Kolbachev <s....@gmail.com>:
> 
>> Hi Dougan,
>> 
>> I'm able to reproduce the problem you provided. I'll come back to you
>> after some investigation.
>> 
>> 2016-07-29 2:47 GMT+03:00 Dougan Stuart <do...@swarmbox.com>:
>> 
>>> Sure, here’s the relevant parts:
>>> 
>>> <db-entity name="company" schema="public">
>>>        <db-attribute name="name" type="VARCHAR" length="50”/>
>>>        <db-attribute name="uuid" type="OTHER" isPrimaryKey="true"
>>> isMandatory="true" length="2147483647"/>
>>> </db-entity>
>>> <db-entity name="entity" schema="public”>
>>>        <db-attribute name="audit_uuid" type="OTHER" length="2147483647
>>> ”/>
>>>        <db-attribute name="reference" type="VARCHAR" length="30"/>
>>>        <db-attribute name="type" type="CHAR" isMandatory="true"
>>> length="1”/>
>>>        <db-attribute name="uuid" type="OTHER" isPrimaryKey="true"
>>> isMandatory="true" length="2147483647”/>
>>>        <db-attribute name="parent_entity_uuid" type="OTHER" length="
>>> 2147483647"/>
>>> </db-entity>
>>> 
>>> <obj-entity name="Company" superEntityName="Entity"
>>> className=“...data.models.Company”>
>>>        <qualifier><![CDATA[type = "C"]]></qualifier>
>>>        <obj-attribute name="name" type="java.lang.String"
>>> db-attribute-path="company.name”/>
>>> </obj-entity>
>>> <obj-entity name="Entity" abstract="true" className=“...data.models.Entity"
>>> dbEntityName="entity" superClassName=“...data.CayenneBaseDataObject">
>>>        <obj-attribute name="reference" type="java.lang.String"
>>> db-attribute-path="reference”/>
>>>        <obj-attribute name="type" type=“...data.util.EntityType"
>>> db-attribute-path="type”/>
>>> </obj-entity>
>>> 
>>> <db-relationship name="entity" source="company" target="entity"
>>> toMany="false”>
>>>        <db-attribute-pair source="uuid" target="uuid"/>
>>> </db-relationship>
>>> <db-relationship name="company" source="entity" target="company"
>>> toDependentPK="true" toMany="false">
>>>        <db-attribute-pair source="uuid" target="uuid"/>
>>> </db-relationship>
>>> <db-relationship name="parentEntity" source="entity" target="entity"
>>> toMany="false”>
>>>        <db-attribute-pair source="parent_entity_uuid" target="uuid”/>
>>> </db-relationship>
>>> <db-relationship name="subEntities" source="entity" target="entity"
>>> toMany="true”>
>>>        <db-attribute-pair source="uuid" target="parent_entity_uuid”/>
>>> </db-relationship>
>>> 
>>> <obj-relationship name="parentCompany" source="Company" target="Company"
>>> deleteRule="Nullify" db-relationship-path="parentEntity"/>
>>> <obj-relationship name="subCompanies" source="Company" target="Company"
>>> deleteRule="Deny" db-relationship-path="subEntities"/>
>>> 
>>> 
>>> I’ve created a fix for this issue in DataDomainDBDiffBuilder.appendForeignKeys
>>> on line 133 (within the nested loop):
>>> 
>>> String joinSourceName = join.getSourceName();
>>> if (dbDiff.get(joinSourceName) == null && dbEntity.getAttribute(joinSourceName)
>>> != null) {
>>>        dbDiff.put(joinSourceName, value);
>>> }
>>> 
>>> The only change is that I’m checking if the dbEntity actually has the
>>> attribute it’s trying to add from the join. If it doesn’t have the
>>> attribute, then it doesn’t make sense to put it on the dbDiff. In
>>> DataDomainUpdateBucket.updatedAttribtues (mentioned in my prior
>>> message), it expects everything in the dbDiff to be an attribute that
>>> exists on the dbEntity. The fix passed Cayenne’s tests and didn’t appear to
>>> cause any side effects. Of course, this is all moot if I just botched the
>>> data map. Let me know what you think.
>>> 
>>> Thanks,
>>> Doug
>>> 
>>> 
>>> 
>>> 
>>> 
>>>> On Jul 28, 2016, at 3:47 AM, Andrus Adamchik <an...@objectstyle.org>
>>> wrote:
>>>> 
>>>> Would you mind posting a relevant part of your DataMap XML?
>>>> 
>>>> Andrus
>>>> 
>>>>> On Jul 13, 2016, at 8:40 PM, Dougan Stuart <do...@swarmbox.com> wrote:
>>>>> 
>>>>> I have a class Company with a one-to-many relationship to other
>>> Companies (parentCompany <- subCompanies). I’m able to set a Company’s
>>> parent when creating one, but upon update I’m getting a
>>> NullPointerException in DefaultQuotingStrategy.quotedName because it’s
>>> been sent a null attribute. Company extends the abstract class Entity; on
>>> the DB side Entity and Company have a one-to-one relationship, and Entity
>>> has a parentEntity reference (which is modeled as parentCompany). The
>>> obj-relationship parentCompany is set up with the target Company and path
>>> parentEntity.
>>>>> 
>>>>> The null attribute is being added in org.apache.cayenne.access.
>>> DataDomainUpdateBucket.updatedAttributes. The method is called first
>>> with the dbEntity Entity and the an updatedSnapshot containing
>>> parentEntity, which is what I would expect. However, after this
>>> updatedAttributes is called with Company and an updatedSnapshot containing
>>> parentEntity, so the following line
>>>>> 
>>>>> attributes.add(entityAttributes.get(name))
>>>>> 
>>>>> will return a null attribute when trying to get the name
>>> “parentEntity” off Company’s attributes; that attribute only exists on
>>> Entity. I’d appreciate any help on preventing it from trying to add invalid
>>> attributes.
>>>>> 
>>>>> I’m not sure if this is the root cause, but  DataDomainUpdateBucket.appendQueriesInternal
>>> calls updatedAttributes based off the bucket’s dbEntities, which in this
>>> case are set in DataDomainSyncBucket.groupObjEntitiesBySpannedDbEntities;
>>> this method is only adding Company as a “secondary DbEntity” (as referred
>>> to in the comments) because Company has a flattened attribute “name" that
>>> does not exist on the Entity.
>>>>> 
>>>>> Here’s the stack trace:
>>>>> 
>>>>> java.lang.NullPointerException
>>>>>     at org.apache.cayenne.dba.DefaultQuotingStrategy.quotedName(
>>> DefaultQuotingStrategy.java:62)
>>>>>     at org.apache.cayenne.access.translator.batch.
>>> UpdateBatchTranslator.createSql(UpdateBatchTranslator.java:60)
>>>>>     at org.apache.cayenne.access.translator.batch.
>>> DefaultBatchTranslator.ensureTranslated(DefaultBatchTranslator.java:52)
>>>>>     at org.apache.cayenne.access.translator.batch.
>>> DefaultBatchTranslator.getSql(DefaultBatchTranslator.java:64)
>>>>>     at org.apache.cayenne.access.jdbc.BatchAction.runAsBatch(
>>> BatchAction.java:103)
>>>>>     at org.apache.cayenne.access.jdbc.BatchAction.
>>> performAction(BatchAction.java:90)
>>>>>     at org.apache.cayenne.access.DataNodeQueryAction.runQuery(
>>> DataNodeQueryAction.java:97)
>>>>>     at org.apache.cayenne.access.DataNode.performQueries(
>>> DataNode.java:293)
>>>>>     at org.apache.cayenne.access.DataDomainFlushAction.runQueries(
>>> DataDomainFlushAction.java:233)
>>>>>     at org.apache.cayenne.access.DataDomainFlushAction.flush(
>>> DataDomainFlushAction.java:154)
>>>>>     at org.apache.cayenne.access.DataDomain.onSyncFlush(
>>> DataDomain.java:693)
>>>>>     at org.apache.cayenne.access.DataDomain$2.transform(
>>> DataDomain.java:659)
>>>>>     at org.apache.cayenne.access.DataDomain.runInTransaction(
>>> DataDomain.java:720)
>>>>>     at org.apache.cayenne.access.DataDomain.onSyncNoFilters(
>>> DataDomain.java:655)
>>>>>     at org.apache.cayenne.access.DataDomain$
>>> DataDomainSyncFilterChain.onSync(DataDomain.java:863)
>>>>>     at org.apache.cayenne.access.DataDomain.onSync(DataDomain.
>>> java:636)
>>>>>     at org.apache.cayenne.access.DataContext.flushToParent(
>>> DataContext.java:727)
>>>>>     at org.apache.cayenne.access.DataContext.commitChanges(
>>> DataContext.java:676)
>>>>> 
>>>>> Thanks,
>>>>> Doug
>>>>> 
>>>> 
>>> 
>>> 
>> 
>> 
>> --
>> Best Regards,
>> Savva Kolbachev
>> 
> 
> 
> 
> -- 
> Best Regards,
> Savva Kolbachev


Re: Extended class issues

Posted by Savva Kolbachev <s....@gmail.com>.
I've created a JIRA ticket for the issue CAY-2097
<https://issues.apache.org/jira/browse/CAY-2097> and provided a fix
https://github.com/apache/cayenne/commit/697f38e5127852a144b13e7640787e
57ac3ebba1

It's a little different from your. It's similar to how we check for
properties in case of flattened attributes DataDomainDBDiffBuilder.java#L95
<https://github.com/apache/cayenne/blob/master/cayenne-server/src/main/java/org/apache/cayenne/access/DataDomainDBDiffBuilder.java#L95>

BTW, not sure that we should check it here. Perhaps, we should check it
while filling currentPropertyDiff and currentArcDiff. Looks like in this
case performance will be slightly better.

2016-08-03 21:24 GMT+03:00 Savva Kolbachev <s....@gmail.com>:

> Hi Dougan,
>
> I'm able to reproduce the problem you provided. I'll come back to you
> after some investigation.
>
> 2016-07-29 2:47 GMT+03:00 Dougan Stuart <do...@swarmbox.com>:
>
>> Sure, here’s the relevant parts:
>>
>> <db-entity name="company" schema="public">
>>         <db-attribute name="name" type="VARCHAR" length="50”/>
>>         <db-attribute name="uuid" type="OTHER" isPrimaryKey="true"
>> isMandatory="true" length="2147483647"/>
>> </db-entity>
>> <db-entity name="entity" schema="public”>
>>         <db-attribute name="audit_uuid" type="OTHER" length="2147483647
>> ”/>
>>         <db-attribute name="reference" type="VARCHAR" length="30"/>
>>         <db-attribute name="type" type="CHAR" isMandatory="true"
>> length="1”/>
>>         <db-attribute name="uuid" type="OTHER" isPrimaryKey="true"
>> isMandatory="true" length="2147483647”/>
>>         <db-attribute name="parent_entity_uuid" type="OTHER" length="
>> 2147483647"/>
>> </db-entity>
>>
>> <obj-entity name="Company" superEntityName="Entity"
>> className=“...data.models.Company”>
>>         <qualifier><![CDATA[type = "C"]]></qualifier>
>>         <obj-attribute name="name" type="java.lang.String"
>> db-attribute-path="company.name”/>
>> </obj-entity>
>> <obj-entity name="Entity" abstract="true" className=“...data.models.Entity"
>> dbEntityName="entity" superClassName=“...data.CayenneBaseDataObject">
>>         <obj-attribute name="reference" type="java.lang.String"
>> db-attribute-path="reference”/>
>>         <obj-attribute name="type" type=“...data.util.EntityType"
>> db-attribute-path="type”/>
>> </obj-entity>
>>
>> <db-relationship name="entity" source="company" target="entity"
>> toMany="false”>
>>         <db-attribute-pair source="uuid" target="uuid"/>
>> </db-relationship>
>> <db-relationship name="company" source="entity" target="company"
>> toDependentPK="true" toMany="false">
>>         <db-attribute-pair source="uuid" target="uuid"/>
>> </db-relationship>
>> <db-relationship name="parentEntity" source="entity" target="entity"
>> toMany="false”>
>>         <db-attribute-pair source="parent_entity_uuid" target="uuid”/>
>> </db-relationship>
>> <db-relationship name="subEntities" source="entity" target="entity"
>> toMany="true”>
>>         <db-attribute-pair source="uuid" target="parent_entity_uuid”/>
>> </db-relationship>
>>
>> <obj-relationship name="parentCompany" source="Company" target="Company"
>> deleteRule="Nullify" db-relationship-path="parentEntity"/>
>> <obj-relationship name="subCompanies" source="Company" target="Company"
>> deleteRule="Deny" db-relationship-path="subEntities"/>
>>
>>
>> I’ve created a fix for this issue in DataDomainDBDiffBuilder.appendForeignKeys
>> on line 133 (within the nested loop):
>>
>> String joinSourceName = join.getSourceName();
>> if (dbDiff.get(joinSourceName) == null && dbEntity.getAttribute(joinSourceName)
>> != null) {
>>         dbDiff.put(joinSourceName, value);
>> }
>>
>> The only change is that I’m checking if the dbEntity actually has the
>> attribute it’s trying to add from the join. If it doesn’t have the
>> attribute, then it doesn’t make sense to put it on the dbDiff. In
>> DataDomainUpdateBucket.updatedAttribtues (mentioned in my prior
>> message), it expects everything in the dbDiff to be an attribute that
>> exists on the dbEntity. The fix passed Cayenne’s tests and didn’t appear to
>> cause any side effects. Of course, this is all moot if I just botched the
>> data map. Let me know what you think.
>>
>> Thanks,
>> Doug
>>
>>
>>
>>
>>
>> > On Jul 28, 2016, at 3:47 AM, Andrus Adamchik <an...@objectstyle.org>
>> wrote:
>> >
>> > Would you mind posting a relevant part of your DataMap XML?
>> >
>> > Andrus
>> >
>> >> On Jul 13, 2016, at 8:40 PM, Dougan Stuart <do...@swarmbox.com> wrote:
>> >>
>> >> I have a class Company with a one-to-many relationship to other
>> Companies (parentCompany <- subCompanies). I’m able to set a Company’s
>> parent when creating one, but upon update I’m getting a
>> NullPointerException in DefaultQuotingStrategy.quotedName because it’s
>> been sent a null attribute. Company extends the abstract class Entity; on
>> the DB side Entity and Company have a one-to-one relationship, and Entity
>> has a parentEntity reference (which is modeled as parentCompany). The
>> obj-relationship parentCompany is set up with the target Company and path
>> parentEntity.
>> >>
>> >> The null attribute is being added in org.apache.cayenne.access.
>> DataDomainUpdateBucket.updatedAttributes. The method is called first
>> with the dbEntity Entity and the an updatedSnapshot containing
>> parentEntity, which is what I would expect. However, after this
>> updatedAttributes is called with Company and an updatedSnapshot containing
>> parentEntity, so the following line
>> >>
>> >> attributes.add(entityAttributes.get(name))
>> >>
>> >> will return a null attribute when trying to get the name
>> “parentEntity” off Company’s attributes; that attribute only exists on
>> Entity. I’d appreciate any help on preventing it from trying to add invalid
>> attributes.
>> >>
>> >> I’m not sure if this is the root cause, but  DataDomainUpdateBucket.appendQueriesInternal
>> calls updatedAttributes based off the bucket’s dbEntities, which in this
>> case are set in DataDomainSyncBucket.groupObjEntitiesBySpannedDbEntities;
>> this method is only adding Company as a “secondary DbEntity” (as referred
>> to in the comments) because Company has a flattened attribute “name" that
>> does not exist on the Entity.
>> >>
>> >> Here’s the stack trace:
>> >>
>> >> java.lang.NullPointerException
>> >>      at org.apache.cayenne.dba.DefaultQuotingStrategy.quotedName(
>> DefaultQuotingStrategy.java:62)
>> >>      at org.apache.cayenne.access.translator.batch.
>> UpdateBatchTranslator.createSql(UpdateBatchTranslator.java:60)
>> >>      at org.apache.cayenne.access.translator.batch.
>> DefaultBatchTranslator.ensureTranslated(DefaultBatchTranslator.java:52)
>> >>      at org.apache.cayenne.access.translator.batch.
>> DefaultBatchTranslator.getSql(DefaultBatchTranslator.java:64)
>> >>      at org.apache.cayenne.access.jdbc.BatchAction.runAsBatch(
>> BatchAction.java:103)
>> >>      at org.apache.cayenne.access.jdbc.BatchAction.
>> performAction(BatchAction.java:90)
>> >>      at org.apache.cayenne.access.DataNodeQueryAction.runQuery(
>> DataNodeQueryAction.java:97)
>> >>      at org.apache.cayenne.access.DataNode.performQueries(
>> DataNode.java:293)
>> >>      at org.apache.cayenne.access.DataDomainFlushAction.runQueries(
>> DataDomainFlushAction.java:233)
>> >>      at org.apache.cayenne.access.DataDomainFlushAction.flush(
>> DataDomainFlushAction.java:154)
>> >>      at org.apache.cayenne.access.DataDomain.onSyncFlush(
>> DataDomain.java:693)
>> >>      at org.apache.cayenne.access.DataDomain$2.transform(
>> DataDomain.java:659)
>> >>      at org.apache.cayenne.access.DataDomain.runInTransaction(
>> DataDomain.java:720)
>> >>      at org.apache.cayenne.access.DataDomain.onSyncNoFilters(
>> DataDomain.java:655)
>> >>      at org.apache.cayenne.access.DataDomain$
>> DataDomainSyncFilterChain.onSync(DataDomain.java:863)
>> >>      at org.apache.cayenne.access.DataDomain.onSync(DataDomain.
>> java:636)
>> >>      at org.apache.cayenne.access.DataContext.flushToParent(
>> DataContext.java:727)
>> >>      at org.apache.cayenne.access.DataContext.commitChanges(
>> DataContext.java:676)
>> >>
>> >> Thanks,
>> >> Doug
>> >>
>> >
>>
>>
>
>
> --
> Best Regards,
> Savva Kolbachev
>



-- 
Best Regards,
Savva Kolbachev

Re: Extended class issues

Posted by Savva Kolbachev <s....@gmail.com>.
Hi Dougan,

I'm able to reproduce the problem you provided. I'll come back to you after
some investigation.

2016-07-29 2:47 GMT+03:00 Dougan Stuart <do...@swarmbox.com>:

> Sure, here’s the relevant parts:
>
> <db-entity name="company" schema="public">
>         <db-attribute name="name" type="VARCHAR" length="50”/>
>         <db-attribute name="uuid" type="OTHER" isPrimaryKey="true"
> isMandatory="true" length="2147483647"/>
> </db-entity>
> <db-entity name="entity" schema="public”>
>         <db-attribute name="audit_uuid" type="OTHER" length="2147483647”/>
>         <db-attribute name="reference" type="VARCHAR" length="30"/>
>         <db-attribute name="type" type="CHAR" isMandatory="true"
> length="1”/>
>         <db-attribute name="uuid" type="OTHER" isPrimaryKey="true"
> isMandatory="true" length="2147483647”/>
>         <db-attribute name="parent_entity_uuid" type="OTHER" length="
> 2147483647"/>
> </db-entity>
>
> <obj-entity name="Company" superEntityName="Entity"
> className=“...data.models.Company”>
>         <qualifier><![CDATA[type = "C"]]></qualifier>
>         <obj-attribute name="name" type="java.lang.String"
> db-attribute-path="company.name”/>
> </obj-entity>
> <obj-entity name="Entity" abstract="true"
> className=“...data.models.Entity" dbEntityName="entity"
> superClassName=“...data.CayenneBaseDataObject">
>         <obj-attribute name="reference" type="java.lang.String"
> db-attribute-path="reference”/>
>         <obj-attribute name="type" type=“...data.util.EntityType"
> db-attribute-path="type”/>
> </obj-entity>
>
> <db-relationship name="entity" source="company" target="entity"
> toMany="false”>
>         <db-attribute-pair source="uuid" target="uuid"/>
> </db-relationship>
> <db-relationship name="company" source="entity" target="company"
> toDependentPK="true" toMany="false">
>         <db-attribute-pair source="uuid" target="uuid"/>
> </db-relationship>
> <db-relationship name="parentEntity" source="entity" target="entity"
> toMany="false”>
>         <db-attribute-pair source="parent_entity_uuid" target="uuid”/>
> </db-relationship>
> <db-relationship name="subEntities" source="entity" target="entity"
> toMany="true”>
>         <db-attribute-pair source="uuid" target="parent_entity_uuid”/>
> </db-relationship>
>
> <obj-relationship name="parentCompany" source="Company" target="Company"
> deleteRule="Nullify" db-relationship-path="parentEntity"/>
> <obj-relationship name="subCompanies" source="Company" target="Company"
> deleteRule="Deny" db-relationship-path="subEntities"/>
>
>
> I’ve created a fix for this issue in
> DataDomainDBDiffBuilder.appendForeignKeys on line 133 (within the nested
> loop):
>
> String joinSourceName = join.getSourceName();
> if (dbDiff.get(joinSourceName) == null &&
> dbEntity.getAttribute(joinSourceName) != null) {
>         dbDiff.put(joinSourceName, value);
> }
>
> The only change is that I’m checking if the dbEntity actually has the
> attribute it’s trying to add from the join. If it doesn’t have the
> attribute, then it doesn’t make sense to put it on the dbDiff. In
> DataDomainUpdateBucket.updatedAttribtues (mentioned in my prior message),
> it expects everything in the dbDiff to be an attribute that exists on the
> dbEntity. The fix passed Cayenne’s tests and didn’t appear to cause any
> side effects. Of course, this is all moot if I just botched the data map.
> Let me know what you think.
>
> Thanks,
> Doug
>
>
>
>
>
> > On Jul 28, 2016, at 3:47 AM, Andrus Adamchik <an...@objectstyle.org>
> wrote:
> >
> > Would you mind posting a relevant part of your DataMap XML?
> >
> > Andrus
> >
> >> On Jul 13, 2016, at 8:40 PM, Dougan Stuart <do...@swarmbox.com> wrote:
> >>
> >> I have a class Company with a one-to-many relationship to other
> Companies (parentCompany <- subCompanies). I’m able to set a Company’s
> parent when creating one, but upon update I’m getting a
> NullPointerException in DefaultQuotingStrategy.quotedName because it’s been
> sent a null attribute. Company extends the abstract class Entity; on the DB
> side Entity and Company have a one-to-one relationship, and Entity has a
> parentEntity reference (which is modeled as parentCompany). The
> obj-relationship parentCompany is set up with the target Company and path
> parentEntity.
> >>
> >> The null attribute is being added in
> org.apache.cayenne.access.DataDomainUpdateBucket.updatedAttributes. The
> method is called first with the dbEntity Entity and the an updatedSnapshot
> containing parentEntity, which is what I would expect. However, after this
> updatedAttributes is called with Company and an updatedSnapshot containing
> parentEntity, so the following line
> >>
> >> attributes.add(entityAttributes.get(name))
> >>
> >> will return a null attribute when trying to get the name “parentEntity”
> off Company’s attributes; that attribute only exists on Entity. I’d
> appreciate any help on preventing it from trying to add invalid attributes.
> >>
> >> I’m not sure if this is the root cause, but
> DataDomainUpdateBucket.appendQueriesInternal calls updatedAttributes based
> off the bucket’s dbEntities, which in this case are set in
> DataDomainSyncBucket.groupObjEntitiesBySpannedDbEntities; this method is
> only adding Company as a “secondary DbEntity” (as referred to in the
> comments) because Company has a flattened attribute “name" that does not
> exist on the Entity.
> >>
> >> Here’s the stack trace:
> >>
> >> java.lang.NullPointerException
> >>      at
> org.apache.cayenne.dba.DefaultQuotingStrategy.quotedName(DefaultQuotingStrategy.java:62)
> >>      at
> org.apache.cayenne.access.translator.batch.UpdateBatchTranslator.createSql(UpdateBatchTranslator.java:60)
> >>      at
> org.apache.cayenne.access.translator.batch.DefaultBatchTranslator.ensureTranslated(DefaultBatchTranslator.java:52)
> >>      at
> org.apache.cayenne.access.translator.batch.DefaultBatchTranslator.getSql(DefaultBatchTranslator.java:64)
> >>      at
> org.apache.cayenne.access.jdbc.BatchAction.runAsBatch(BatchAction.java:103)
> >>      at
> org.apache.cayenne.access.jdbc.BatchAction.performAction(BatchAction.java:90)
> >>      at
> org.apache.cayenne.access.DataNodeQueryAction.runQuery(DataNodeQueryAction.java:97)
> >>      at
> org.apache.cayenne.access.DataNode.performQueries(DataNode.java:293)
> >>      at
> org.apache.cayenne.access.DataDomainFlushAction.runQueries(DataDomainFlushAction.java:233)
> >>      at
> org.apache.cayenne.access.DataDomainFlushAction.flush(DataDomainFlushAction.java:154)
> >>      at
> org.apache.cayenne.access.DataDomain.onSyncFlush(DataDomain.java:693)
> >>      at
> org.apache.cayenne.access.DataDomain$2.transform(DataDomain.java:659)
> >>      at
> org.apache.cayenne.access.DataDomain.runInTransaction(DataDomain.java:720)
> >>      at
> org.apache.cayenne.access.DataDomain.onSyncNoFilters(DataDomain.java:655)
> >>      at
> org.apache.cayenne.access.DataDomain$DataDomainSyncFilterChain.onSync(DataDomain.java:863)
> >>      at org.apache.cayenne.access.DataDomain.onSync(DataDomain.java:636)
> >>      at
> org.apache.cayenne.access.DataContext.flushToParent(DataContext.java:727)
> >>      at
> org.apache.cayenne.access.DataContext.commitChanges(DataContext.java:676)
> >>
> >> Thanks,
> >> Doug
> >>
> >
>
>


-- 
Best Regards,
Savva Kolbachev

Re: Extended class issues

Posted by Dougan Stuart <do...@swarmbox.com>.
Sure, here’s the relevant parts:

<db-entity name="company" schema="public">

	<db-attribute name="name" type="VARCHAR" length="50”/>

	<db-attribute name="uuid" type="OTHER" isPrimaryKey="true" isMandatory="true" length="2147483647"/>

</db-entity>
<db-entity name="entity" schema="public”>

	<db-attribute name="audit_uuid" type="OTHER" length="2147483647”/>

	<db-attribute name="reference" type="VARCHAR" length="30"/>

	<db-attribute name="type" type="CHAR" isMandatory="true" length="1”/>

	<db-attribute name="uuid" type="OTHER" isPrimaryKey="true" isMandatory="true" length="2147483647”/>

	<db-attribute name="parent_entity_uuid" type="OTHER" length="2147483647"/>

</db-entity>

<obj-entity name="Company" superEntityName="Entity" className=“...data.models.Company”>

	<qualifier><![CDATA[type = "C"]]></qualifier>

	<obj-attribute name="name" type="java.lang.String" db-attribute-path="company.name”/>

</obj-entity>
<obj-entity name="Entity" abstract="true" className=“...data.models.Entity" dbEntityName="entity" superClassName=“...data.CayenneBaseDataObject">

	<obj-attribute name="reference" type="java.lang.String" db-attribute-path="reference”/>

	<obj-attribute name="type" type=“...data.util.EntityType" db-attribute-path="type”/>

</obj-entity>

<db-relationship name="entity" source="company" target="entity" toMany="false”>

	<db-attribute-pair source="uuid" target="uuid"/>

</db-relationship>
<db-relationship name="company" source="entity" target="company" toDependentPK="true" toMany="false">

	<db-attribute-pair source="uuid" target="uuid"/>

</db-relationship>

<db-relationship name="parentEntity" source="entity" target="entity" toMany="false”>

	<db-attribute-pair source="parent_entity_uuid" target="uuid”/>

</db-relationship>

<db-relationship name="subEntities" source="entity" target="entity" toMany="true”>

	<db-attribute-pair source="uuid" target="parent_entity_uuid”/>

</db-relationship>


<obj-relationship name="parentCompany" source="Company" target="Company" deleteRule="Nullify" db-relationship-path="parentEntity"/>

<obj-relationship name="subCompanies" source="Company" target="Company" deleteRule="Deny" db-relationship-path="subEntities"/>


I’ve created a fix for this issue in DataDomainDBDiffBuilder.appendForeignKeys on line 133 (within the nested loop):

String joinSourceName = join.getSourceName();
if (dbDiff.get(joinSourceName) == null && dbEntity.getAttribute(joinSourceName) != null) {
	dbDiff.put(joinSourceName, value);
}

The only change is that I’m checking if the dbEntity actually has the attribute it’s trying to add from the join. If it doesn’t have the attribute, then it doesn’t make sense to put it on the dbDiff. In DataDomainUpdateBucket.updatedAttribtues (mentioned in my prior message), it expects everything in the dbDiff to be an attribute that exists on the dbEntity. The fix passed Cayenne’s tests and didn’t appear to cause any side effects. Of course, this is all moot if I just botched the data map. Let me know what you think.

Thanks,
Doug





> On Jul 28, 2016, at 3:47 AM, Andrus Adamchik <an...@objectstyle.org> wrote:
> 
> Would you mind posting a relevant part of your DataMap XML?
> 
> Andrus
> 
>> On Jul 13, 2016, at 8:40 PM, Dougan Stuart <do...@swarmbox.com> wrote:
>> 
>> I have a class Company with a one-to-many relationship to other Companies (parentCompany <- subCompanies). I’m able to set a Company’s parent when creating one, but upon update I’m getting a NullPointerException in DefaultQuotingStrategy.quotedName because it’s been sent a null attribute. Company extends the abstract class Entity; on the DB side Entity and Company have a one-to-one relationship, and Entity has a parentEntity reference (which is modeled as parentCompany). The obj-relationship parentCompany is set up with the target Company and path parentEntity.
>> 
>> The null attribute is being added in org.apache.cayenne.access.DataDomainUpdateBucket.updatedAttributes. The method is called first with the dbEntity Entity and the an updatedSnapshot containing parentEntity, which is what I would expect. However, after this updatedAttributes is called with Company and an updatedSnapshot containing parentEntity, so the following line
>> 
>> attributes.add(entityAttributes.get(name))
>> 
>> will return a null attribute when trying to get the name “parentEntity” off Company’s attributes; that attribute only exists on Entity. I’d appreciate any help on preventing it from trying to add invalid attributes.
>> 
>> I’m not sure if this is the root cause, but  DataDomainUpdateBucket.appendQueriesInternal calls updatedAttributes based off the bucket’s dbEntities, which in this case are set in DataDomainSyncBucket.groupObjEntitiesBySpannedDbEntities; this method is only adding Company as a “secondary DbEntity” (as referred to in the comments) because Company has a flattened attribute “name" that does not exist on the Entity.
>> 
>> Here’s the stack trace:
>> 
>> java.lang.NullPointerException
>> 	at org.apache.cayenne.dba.DefaultQuotingStrategy.quotedName(DefaultQuotingStrategy.java:62)
>> 	at org.apache.cayenne.access.translator.batch.UpdateBatchTranslator.createSql(UpdateBatchTranslator.java:60)
>> 	at org.apache.cayenne.access.translator.batch.DefaultBatchTranslator.ensureTranslated(DefaultBatchTranslator.java:52)
>> 	at org.apache.cayenne.access.translator.batch.DefaultBatchTranslator.getSql(DefaultBatchTranslator.java:64)
>> 	at org.apache.cayenne.access.jdbc.BatchAction.runAsBatch(BatchAction.java:103)
>> 	at org.apache.cayenne.access.jdbc.BatchAction.performAction(BatchAction.java:90)
>> 	at org.apache.cayenne.access.DataNodeQueryAction.runQuery(DataNodeQueryAction.java:97)
>> 	at org.apache.cayenne.access.DataNode.performQueries(DataNode.java:293)
>> 	at org.apache.cayenne.access.DataDomainFlushAction.runQueries(DataDomainFlushAction.java:233)
>> 	at org.apache.cayenne.access.DataDomainFlushAction.flush(DataDomainFlushAction.java:154)
>> 	at org.apache.cayenne.access.DataDomain.onSyncFlush(DataDomain.java:693)
>> 	at org.apache.cayenne.access.DataDomain$2.transform(DataDomain.java:659)
>> 	at org.apache.cayenne.access.DataDomain.runInTransaction(DataDomain.java:720)
>> 	at org.apache.cayenne.access.DataDomain.onSyncNoFilters(DataDomain.java:655)
>> 	at org.apache.cayenne.access.DataDomain$DataDomainSyncFilterChain.onSync(DataDomain.java:863)
>> 	at org.apache.cayenne.access.DataDomain.onSync(DataDomain.java:636)
>> 	at org.apache.cayenne.access.DataContext.flushToParent(DataContext.java:727)
>> 	at org.apache.cayenne.access.DataContext.commitChanges(DataContext.java:676)
>> 
>> Thanks,
>> Doug
>> 
> 


Re: Extended class issues

Posted by Andrus Adamchik <an...@objectstyle.org>.
Would you mind posting a relevant part of your DataMap XML?

Andrus

> On Jul 13, 2016, at 8:40 PM, Dougan Stuart <do...@swarmbox.com> wrote:
> 
> I have a class Company with a one-to-many relationship to other Companies (parentCompany <- subCompanies). I’m able to set a Company’s parent when creating one, but upon update I’m getting a NullPointerException in DefaultQuotingStrategy.quotedName because it’s been sent a null attribute. Company extends the abstract class Entity; on the DB side Entity and Company have a one-to-one relationship, and Entity has a parentEntity reference (which is modeled as parentCompany). The obj-relationship parentCompany is set up with the target Company and path parentEntity.
> 
> The null attribute is being added in org.apache.cayenne.access.DataDomainUpdateBucket.updatedAttributes. The method is called first with the dbEntity Entity and the an updatedSnapshot containing parentEntity, which is what I would expect. However, after this updatedAttributes is called with Company and an updatedSnapshot containing parentEntity, so the following line
> 
> attributes.add(entityAttributes.get(name))
> 
> will return a null attribute when trying to get the name “parentEntity” off Company’s attributes; that attribute only exists on Entity. I’d appreciate any help on preventing it from trying to add invalid attributes.
> 
> I’m not sure if this is the root cause, but  DataDomainUpdateBucket.appendQueriesInternal calls updatedAttributes based off the bucket’s dbEntities, which in this case are set in DataDomainSyncBucket.groupObjEntitiesBySpannedDbEntities; this method is only adding Company as a “secondary DbEntity” (as referred to in the comments) because Company has a flattened attribute “name" that does not exist on the Entity.
> 
> Here’s the stack trace:
> 
> java.lang.NullPointerException
> 	at org.apache.cayenne.dba.DefaultQuotingStrategy.quotedName(DefaultQuotingStrategy.java:62)
> 	at org.apache.cayenne.access.translator.batch.UpdateBatchTranslator.createSql(UpdateBatchTranslator.java:60)
> 	at org.apache.cayenne.access.translator.batch.DefaultBatchTranslator.ensureTranslated(DefaultBatchTranslator.java:52)
> 	at org.apache.cayenne.access.translator.batch.DefaultBatchTranslator.getSql(DefaultBatchTranslator.java:64)
> 	at org.apache.cayenne.access.jdbc.BatchAction.runAsBatch(BatchAction.java:103)
> 	at org.apache.cayenne.access.jdbc.BatchAction.performAction(BatchAction.java:90)
> 	at org.apache.cayenne.access.DataNodeQueryAction.runQuery(DataNodeQueryAction.java:97)
> 	at org.apache.cayenne.access.DataNode.performQueries(DataNode.java:293)
> 	at org.apache.cayenne.access.DataDomainFlushAction.runQueries(DataDomainFlushAction.java:233)
> 	at org.apache.cayenne.access.DataDomainFlushAction.flush(DataDomainFlushAction.java:154)
> 	at org.apache.cayenne.access.DataDomain.onSyncFlush(DataDomain.java:693)
> 	at org.apache.cayenne.access.DataDomain$2.transform(DataDomain.java:659)
> 	at org.apache.cayenne.access.DataDomain.runInTransaction(DataDomain.java:720)
> 	at org.apache.cayenne.access.DataDomain.onSyncNoFilters(DataDomain.java:655)
> 	at org.apache.cayenne.access.DataDomain$DataDomainSyncFilterChain.onSync(DataDomain.java:863)
> 	at org.apache.cayenne.access.DataDomain.onSync(DataDomain.java:636)
> 	at org.apache.cayenne.access.DataContext.flushToParent(DataContext.java:727)
> 	at org.apache.cayenne.access.DataContext.commitChanges(DataContext.java:676)
> 
> Thanks,
> Doug
>