cayenne-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Andrus Adamchik <and...@objectstyle.org>
Subject Re: optimistic locking exception due to locking on not "Used For Locking" null to-one relationship
Date Sun, 15 Sep 2013 07:59:21 GMT
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 <mkienenb@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 <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