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 <[email protected]> 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 <[email protected]>: > >> 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 <[email protected]>: >> >>> 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 <[email protected]> >>> wrote: >>>> >>>> Would you mind posting a relevant part of your DataMap XML? >>>> >>>> Andrus >>>> >>>>> On Jul 13, 2016, at 8:40 PM, Dougan Stuart <[email protected]> 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
