Hi Mike, So looks like you are on top of it… Let me know if you need any help.
Andrus On Sep 14, 2013, at 8:22 AM, Mike Kienenberger <mkien...@gmail.com> wrote: > I remembered to update the subject this time. > > So if I replace > > arcSnapshot.put(property.getName(), target); > > with > > if (property.getRelationship().isUsedForLocking()) { > arcSnapshot.put(property.getName(), target); > } > > then the primary key qualifier is correct: "WHERE USER_ID = 2" instead > of "WHERE USER_ID is null." > > Unfortunately, this also causes several unit tests to fail. I haven't > yet investigated why this might be. > > Failed tests: > > testReadToOneRelationship(org.apache.cayenne.access.NestedDataContextReadTest) > testRemoveToMany(org.apache.cayenne.CDOSetRelationshipTest) > testRemove(org.apache.cayenne.CDOMany2OneTest) > testNullifyToOne(org.apache.cayenne.access.NestedDataContextWriteTest) > testMultipleToOneDeletion(org.apache.cayenne.unit.jira.CAY_901Test) > testRemoveToMany(org.apache.cayenne.CDOMapRelationshipTest) > > testPhantomRelationshipModificationValidate(org.apache.cayenne.access.DataContextExtrasTest) > testRemove1(org.apache.cayenne.CDOOne2ManyTest) > testRemove2(org.apache.cayenne.CDOOne2ManyTest) > testIsToOneTargetModified(org.apache.cayenne.access.DataRowUtilsTest) > testRemoveToMany(org.apache.cayenne.CDOCollectionRelationshipTest) > > So far, basic functionality for my app seems working. > > On Fri, Sep 13, 2013 at 4:46 PM, Mike Kienenberger <mkien...@gmail.com> wrote: >> As I mentioned earlier, I'm upgrading my ancient Cayenne project from >> 1.1 to 3.x, currently 3.0.2. >> >> I started by upgrading to 1.2 and 2.0, unfortunately hitting the old >> null-relationship-breaks-optimistic-locking error. >> >> http://mail-archives.apache.org/mod_mbox/cayenne-dev/200803.mbox/%3c8f985b960803271232s5018a5a9hbf0f731f82666...@mail.gmail.com%3E >> >> Since most everything else seemed to be working, and the the >> workaround I had for 1.1 wasn't possible in 1.2/2.0, I decided to skip >> ahead to 3.0 and hope it was fixed there, or that it'd be more >> relevant to fix there. >> >> But the same behavior I see in 1.2 and 2.0 still occurs in 3.0.2. For >> 1.1, the fix was to retain a new snapshot when resolving faults, but >> the problem here seems to be slightly different. >> >> My model has a "User" object and a "PotentialCustomer" object. The >> PotentialCustomer is an optional one-to-one relationship with the >> User, where they both have the same primary key. In the past I have >> left the PotentialCustomer relationship as "Used for Locking", >> although I've set it both ways without changing the resulting error. >> >> Committing an unrelated attribute change to the "User" object when it >> has no corresponding "PotentialCustomer" object generates a "where >> USER_ID is null" clause. >> >> Writing a property change eventually generates an arcSnapshot for all >> to-one relationships, even if they are not marked for locking. >> org.apache.cayenne.access.ObjectDiff.java - line 114: >> >> public boolean visitToOne(ToOneProperty property) { >> >> // eagerly resolve optimistically locked relationships >> Object target = lock ? >> property.readProperty(object) : property >> .readPropertyDirectly(object); >> >> if (target instanceof Persistent) { >> target = ((Persistent) target).getObjectId(); >> } >> // else - null || Fault >> >> arcSnapshot.put(property.getName(), target); >> return true; >> } >> >> The problem is that with a relationship which is optional, the target >> is going to be null. And later on, when we generate optimistic >> locking qualifiers in >> org.apache.cayenne.access.DataNodeSyncQualifierDescriptor, we store >> this null value as the matching value for the record's primary key. >> >> To me, part of the fix would seem to be to not do anything if we're >> not locking on this column. Why do we need to resolve a relationship >> or store a snapshot for a column not involved in optimistic locking? >> >> Second, even if this column is involved with optimistic locking, it >> should not be used as a replacement value for the modified object's >> primary key. It's probably a model error to specify a relationship >> based on the modified object's primary key as a locking column. >> However, I can correct this by removing the "Used for Locking" value. >> >> >> On Thu, Mar 27, 2008 at 3:32 PM, Mike Kienenberger <mkien...@gmail.com> >> wrote: >>> Here's an interesting situation I'm debugging now for Cayenne 1.1. >>> It seems to be related to CAY-213 "NullPointerException in >>> ContextCommit with locking". I suspect that it's true of 1.2 and >>> could very well be true for 3.0 as well, although I don't have that >>> handy to test with. >>> >>> http://issues.apache.org/cayenne/browse/CAY-213 >>> >>> My testing seems to reveal that the same problem occurs when you set a >>> to-one relationship to null. Line 291 in removeToManyTarget() sets >>> the state of the previous to-one relationship object to MODIFIED, but >>> doesn't retain a snapshot for that object. >>> >>> Here's some simple test code that shows the problem. And switching >>> the scalar setter with the relationship setter works around the >>> problem. >>> >>> It seems to me that the the fix is to add >>> >>> dataContext.getObjectStore().retainSnapshot(this); >>> >>> as was done for writeProperty(). >>> >>> >>> public void run() throws Exception >>> { >>> initCayenne("cayenne.xml"); >>> >>> // Set up database >>> createSchemaForObjEntityName(Configuration.getSharedConfiguration(), >>> "PotentialCustomer"); >>> DataContext dc = DataContext.createDataContext(); >>> >>> // Set up test data >>> PotentialCustomer pc = >>> (PotentialCustomer)dc.createAndRegisterNewObject(PotentialCustomer.class); >>> Premise premise = >>> (Premise)dc.createAndRegisterNewObject(Premise.class); >>> pc.setToOneTarget("premise", premise, true); >>> dc.commitChanges(); >>> >>> // Force failure: >>> pc.setToOneTarget("premise", null, true); >>> premise.writeProperty("altitude", new Integer(0)); >>> >>> // On commitChanges(), no snapshot available for building locking >>> // java.lang.NullPointerException >>> // at >>> org.objectstyle.cayenne.access.ContextCommit.appendOptimisticLockingAttributes(ContextCommit.java:564) >>> >>> dc.commitChanges(); >>> } >>> >>> >>> java.lang.NullPointerException >>> at >>> org.objectstyle.cayenne.access.ContextCommit.appendOptimisticLockingAttributes(ContextCommit.java:564) >>> at >>> org.objectstyle.cayenne.access.ContextCommit.prepareUpdateQueries(ContextCommit.java:426) >>> at >>> org.objectstyle.cayenne.access.ContextCommit.commit(ContextCommit.java:156) >>> at >>> org.objectstyle.cayenne.access.DataContext.commitChanges(DataContext.java:1266) >>> at >>> org.objectstyle.cayenne.access.DataContext.commitChanges(DataContext.java:1236) >>> at >>> com.gvea.cayenne.TestOptimisticLockingFailureOnSingleTargetNull.run(TestOptimisticLockingFailureOnSingleTargetNull.java:110) >>> at >>> com.gvea.cayenne.TestOptimisticLockingFailureOnSingleTargetNull.main(TestOptimisticLockingFailureOnSingleTargetNull.java:24) >>> >>> Note that some of these line numbers may vary as my version of Cayenne >>> 1.1 has local mods. >