cayenne-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Mike Kienenberger <mkien...@gmail.com>
Subject optimistic locking exception due to locking on not "Used For Locking" null to-one relationship
Date Sat, 14 Sep 2013 05:22:05 GMT
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 <mkienenb@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/%3C8f985b960803271232s5018a5a9hbf0f731f82666e6a@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 <mkienenb@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.

Mime
View raw message