You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@cayenne.apache.org by Bryan Lewis <br...@maine.rr.com> on 2006/04/28 18:40:25 UTC

how to catch updates just before commit

We have a time-when-last-modified timestamp column in most of our tables
to serve as an optimistic-locking attribute.  At present we're setting
it with a bit of inelegant code that imitates the way we used to do it
in our old WebObjects days... we have a saveChanges() method that wraps
dc.commitChanges().  Before the commit we can timesstamp the newObjects
and modifiedObjects.

The trouble is, this sets the timestamp on objects that haven't really
been modified... an attribute was set to the same value it already had,
or a to-many relationship was added to.  Cayenne figures out later in
the pipeline that those other changes aren't real and doesn't generate
SQL for them.  How can I hook into that later point, and set the
timestamp only if the row is about to be updated?  I looked at
DataContextDelegate, but I guess that's for external changes.  The event
mechanism looks closer, although the docs say that dc.onSync() isn't
intended for direct use.  Worst case, I could compare the object's
current values to the snapshot but that seems reinventing the plumbing.

Thanks.


Re: how to catch updates just before commit

Posted by Mike Kienenberger <mk...@gmail.com>.
Take a look at this issue (and the attached thread).

http://issues.apache.org/cayenne/browse/CAY-414

I think all of the pieces for this are in place now, but I haven't
gotten back to try to implement it using the new methodology.

On 4/28/06, Bryan Lewis <br...@maine.rr.com> wrote:
> We have a time-when-last-modified timestamp column in most of our tables
> to serve as an optimistic-locking attribute.  At present we're setting
> it with a bit of inelegant code that imitates the way we used to do it
> in our old WebObjects days... we have a saveChanges() method that wraps
> dc.commitChanges().  Before the commit we can timesstamp the newObjects
> and modifiedObjects.
>
> The trouble is, this sets the timestamp on objects that haven't really
> been modified... an attribute was set to the same value it already had,
> or a to-many relationship was added to.  Cayenne figures out later in
> the pipeline that those other changes aren't real and doesn't generate
> SQL for them.  How can I hook into that later point, and set the
> timestamp only if the row is about to be updated?  I looked at
> DataContextDelegate, but I guess that's for external changes.  The event
> mechanism looks closer, although the docs say that dc.onSync() isn't
> intended for direct use.  Worst case, I could compare the object's
> current values to the snapshot but that seems reinventing the plumbing.
>
> Thanks.
>
>

Re: how to catch updates just before commit

Posted by Mike Kienenberger <mk...@gmail.com>.
On 4/28/06, Bryan Lewis <br...@maine.rr.com> wrote:
> Hmm, I must be missing something.  I looked at CAY-414 and looked for
> related changes in the latest code, but didn't grok it.  I tried the
> validateFor methods, but they still get called for phantom changes... I
> tried adding to a to-many and setting an attribute to its current
> value.  Tried the latest nightly jar.
>
> This isn't an urgent thing for me.  I'll wait a while and check again.
> Thanks.

Sorry.  I didn't try to follow the link, and threads appears to break
across month boundaries.

This was the specific posting I thought you might benefit from:

http://objectstyle.org/cayenne/lists/cayenne-devel/2006/01/0003.html

Re: how to catch updates just before commit

Posted by Andrus Adamchik <an...@objectstyle.org>.
Just checked in a fix for that:

http://issues.apache.org/cayenne/browse/CAY-544

As far as I can tell to-many relationship modifications no longer  
cause validateFor* invocations either.

Andrus


On Apr 30, 2006, at 11:02 AM, Bryan Lewis wrote:

> Alrighty then, I believe I found the difference.  My Company entity  
> has
> optimistic locking disabled.  ObjectDiff's constructor doesn't take
> singleArcSnapshots in that case, which leaves arcSnapshot an empty  
> map.
> The isNoop() method's visitSingleObjectArc() finds oldValue == null  
> and
> thinks there was a modification.
>
>
>
>
> Andrus Adamchik wrote:
>
>> We have unit tests that check this case, and they succeed, meaning
>> that somehow your Company object is different.
>>
>> If you have an opportunity to debug this on your own, validation
>> decisions are made inside a non-public
>> ObjectStoreGraphDiff.validateAndCheckNoop() which in turn calls
>> ObjectDiff.isNoop(). ObjectDiff should detect a phantom modification
>> and return "true".
>>
>> Otherwise please open a bug report attaching the Company object  
>> mapping.
>>
>> Andrus
>>
>>
>> On Apr 28, 2006, at 9:43 PM, Bryan Lewis wrote:
>>
>>> Yep, I understand that your suggestion was unrelated to CAY-414.   
>>> I  was
>>> answering two replies at once.
>>>
>>> I re-tested the phantom changes with the validateFor methods.  I   
>>> have a
>>> validating dataContext and my DataObject wedge class has a
>>> validateForUpdate() method that merely logs a message.  I  
>>> executed  this
>>> code:
>>>
>>>     Company company = (Company) DataObjectUtils.objectForPK(dc,
>>> "Company", new Integer(134987));
>>>     // Set the same value back again.
>>>     Boolean isActive = company.getIsActiveClient();
>>>     company.setIsActiveClient(isActive);
>>>     log.debug("B4 commitChanges");
>>>     dc.commitChanges();
>>>     log.debug("AF commitChanges");
>>>
>>> That produced this log:
>>>
>>>     B4 commitChanges
>>>     enter validateForUpdate(), object <ObjectId:Company,   
>>> NIC_ID=134987>
>>>     AF commitChanges
>>>
>>> So validateForUpdate() was called for a phantom change.  Note  
>>> that no
>>> SQL was generated, correctly.  This is with version 1.2B2.
>>>
>>> Then I thought perhaps my use of a Boolean property might be  
>>> confusing
>>> things because I have an extended type adapter to convert  
>>> Booleans to
>>> Integers.  I tried a String attribute:
>>>
>>>     String name = company.getName();
>>>     company.setName(name);
>>>
>>> Same result, validateForUpdate() was called but no SQL.  As a sanity
>>> check, I added a character to the name and did get SQL generated,  
>>> with
>>> the validate method called again of course.
>>>
>>>
>>> Andrus Adamchik wrote:
>>>
>>>>
>>>> On Apr 28, 2006, at 3:49 PM, Bryan Lewis wrote:
>>>>
>>>>> Hmm, I must be missing something.  I looked at CAY-414
>>>>
>>>>
>>>>
>>>> not ready to comment on that now. The validateFor* suggestion is
>>>> actually not related at all.
>>>>
>>>>> and looked for
>>>>> related changes in the latest code, but didn't grok it.  I  
>>>>> tried the
>>>>> validateFor methods, but they still get called for phantom
>>>>> changes... I
>>>>> tried adding to a to-many
>>>>
>>>>
>>>>
>>>> I think this one is a bug in the algorithm. Will need to  
>>>> investigate.
>>>>
>>>>
>>>>> and setting an attribute to its current value.
>>>>
>>>>
>>>>
>>>> Now this is strange. If you can confirm this behavior, then this  
>>>> is a
>>>> bug. I think you have other changes to the object in question  
>>>> though.
>>>>
>>>> Andrus
>>>
>>
>
>


Re: how to catch updates just before commit

Posted by Bryan Lewis <br...@maine.rr.com>.
Alrighty then, I believe I found the difference.  My Company entity has
optimistic locking disabled.  ObjectDiff's constructor doesn't take
singleArcSnapshots in that case, which leaves arcSnapshot an empty map.
The isNoop() method's visitSingleObjectArc() finds oldValue == null and
thinks there was a modification.




Andrus Adamchik wrote:

> We have unit tests that check this case, and they succeed, meaning 
> that somehow your Company object is different.
>
> If you have an opportunity to debug this on your own, validation 
> decisions are made inside a non-public 
> ObjectStoreGraphDiff.validateAndCheckNoop() which in turn calls 
> ObjectDiff.isNoop(). ObjectDiff should detect a phantom modification 
> and return "true".
>
> Otherwise please open a bug report attaching the Company object mapping.
>
> Andrus
>
>
> On Apr 28, 2006, at 9:43 PM, Bryan Lewis wrote:
>
>> Yep, I understand that your suggestion was unrelated to CAY-414.  I  was
>> answering two replies at once.
>>
>> I re-tested the phantom changes with the validateFor methods.  I  have a
>> validating dataContext and my DataObject wedge class has a
>> validateForUpdate() method that merely logs a message.  I executed  this
>> code:
>>
>>     Company company = (Company) DataObjectUtils.objectForPK(dc,
>> "Company", new Integer(134987));
>>     // Set the same value back again.
>>     Boolean isActive = company.getIsActiveClient();
>>     company.setIsActiveClient(isActive);
>>     log.debug("B4 commitChanges");
>>     dc.commitChanges();
>>     log.debug("AF commitChanges");
>>
>> That produced this log:
>>
>>     B4 commitChanges
>>     enter validateForUpdate(), object <ObjectId:Company,  NIC_ID=134987>
>>     AF commitChanges
>>
>> So validateForUpdate() was called for a phantom change.  Note that no
>> SQL was generated, correctly.  This is with version 1.2B2.
>>
>> Then I thought perhaps my use of a Boolean property might be confusing
>> things because I have an extended type adapter to convert Booleans to
>> Integers.  I tried a String attribute:
>>
>>     String name = company.getName();
>>     company.setName(name);
>>
>> Same result, validateForUpdate() was called but no SQL.  As a sanity
>> check, I added a character to the name and did get SQL generated, with
>> the validate method called again of course.
>>
>>
>> Andrus Adamchik wrote:
>>
>>>
>>> On Apr 28, 2006, at 3:49 PM, Bryan Lewis wrote:
>>>
>>>> Hmm, I must be missing something.  I looked at CAY-414
>>>
>>>
>>>
>>> not ready to comment on that now. The validateFor* suggestion is
>>> actually not related at all.
>>>
>>>> and looked for
>>>> related changes in the latest code, but didn't grok it.  I tried the
>>>> validateFor methods, but they still get called for phantom  
>>>> changes... I
>>>> tried adding to a to-many
>>>
>>>
>>>
>>> I think this one is a bug in the algorithm. Will need to investigate.
>>>
>>>
>>>> and setting an attribute to its current value.
>>>
>>>
>>>
>>> Now this is strange. If you can confirm this behavior, then this is a
>>> bug. I think you have other changes to the object in question though.
>>>
>>> Andrus
>>
>


Re: how to catch updates just before commit

Posted by Andrus Adamchik <an...@objectstyle.org>.
We have unit tests that check this case, and they succeed, meaning  
that somehow your Company object is different.

If you have an opportunity to debug this on your own, validation  
decisions are made inside a non-public  
ObjectStoreGraphDiff.validateAndCheckNoop() which in turn calls  
ObjectDiff.isNoop(). ObjectDiff should detect a phantom modification  
and return "true".

Otherwise please open a bug report attaching the Company object mapping.

Andrus


On Apr 28, 2006, at 9:43 PM, Bryan Lewis wrote:

> Yep, I understand that your suggestion was unrelated to CAY-414.  I  
> was
> answering two replies at once.
>
> I re-tested the phantom changes with the validateFor methods.  I  
> have a
> validating dataContext and my DataObject wedge class has a
> validateForUpdate() method that merely logs a message.  I executed  
> this
> code:
>
>     Company company = (Company) DataObjectUtils.objectForPK(dc,
> "Company", new Integer(134987));
>     // Set the same value back again.
>     Boolean isActive = company.getIsActiveClient();
>     company.setIsActiveClient(isActive);
>     log.debug("B4 commitChanges");
>     dc.commitChanges();
>     log.debug("AF commitChanges");
>
> That produced this log:
>
>     B4 commitChanges
>     enter validateForUpdate(), object <ObjectId:Company,  
> NIC_ID=134987>
>     AF commitChanges
>
> So validateForUpdate() was called for a phantom change.  Note that no
> SQL was generated, correctly.  This is with version 1.2B2.
>
> Then I thought perhaps my use of a Boolean property might be confusing
> things because I have an extended type adapter to convert Booleans to
> Integers.  I tried a String attribute:
>
>     String name = company.getName();
>     company.setName(name);
>
> Same result, validateForUpdate() was called but no SQL.  As a sanity
> check, I added a character to the name and did get SQL generated, with
> the validate method called again of course.
>
>
> Andrus Adamchik wrote:
>
>>
>> On Apr 28, 2006, at 3:49 PM, Bryan Lewis wrote:
>>
>>> Hmm, I must be missing something.  I looked at CAY-414
>>
>>
>> not ready to comment on that now. The validateFor* suggestion is
>> actually not related at all.
>>
>>> and looked for
>>> related changes in the latest code, but didn't grok it.  I tried the
>>> validateFor methods, but they still get called for phantom   
>>> changes... I
>>> tried adding to a to-many
>>
>>
>> I think this one is a bug in the algorithm. Will need to investigate.
>>
>>
>>> and setting an attribute to its current value.
>>
>>
>> Now this is strange. If you can confirm this behavior, then this is a
>> bug. I think you have other changes to the object in question though.
>>
>> Andrus


Re: how to catch updates just before commit

Posted by Bryan Lewis <br...@maine.rr.com>.
Yep, I understand that your suggestion was unrelated to CAY-414.  I was
answering two replies at once.

I re-tested the phantom changes with the validateFor methods.  I have a
validating dataContext and my DataObject wedge class has a
validateForUpdate() method that merely logs a message.  I executed this
code:

    Company company = (Company) DataObjectUtils.objectForPK(dc,
"Company", new Integer(134987));
    // Set the same value back again.
    Boolean isActive = company.getIsActiveClient();
    company.setIsActiveClient(isActive);
    log.debug("B4 commitChanges");
    dc.commitChanges();
    log.debug("AF commitChanges");

That produced this log:

    B4 commitChanges
    enter validateForUpdate(), object <ObjectId:Company, NIC_ID=134987>
    AF commitChanges

So validateForUpdate() was called for a phantom change.  Note that no
SQL was generated, correctly.  This is with version 1.2B2.

Then I thought perhaps my use of a Boolean property might be confusing
things because I have an extended type adapter to convert Booleans to
Integers.  I tried a String attribute:

    String name = company.getName();
    company.setName(name);

Same result, validateForUpdate() was called but no SQL.  As a sanity
check, I added a character to the name and did get SQL generated, with
the validate method called again of course.


Andrus Adamchik wrote:

>
> On Apr 28, 2006, at 3:49 PM, Bryan Lewis wrote:
>
>> Hmm, I must be missing something.  I looked at CAY-414
>
>
> not ready to comment on that now. The validateFor* suggestion is 
> actually not related at all.
>
>> and looked for
>> related changes in the latest code, but didn't grok it.  I tried the
>> validateFor methods, but they still get called for phantom  changes... I
>> tried adding to a to-many
>
>
> I think this one is a bug in the algorithm. Will need to investigate.
>
>
>> and setting an attribute to its current value.
>
>
> Now this is strange. If you can confirm this behavior, then this is a 
> bug. I think you have other changes to the object in question though.
>
> Andrus
>


Re: how to catch updates just before commit

Posted by Andrus Adamchik <an...@objectstyle.org>.
On Apr 28, 2006, at 3:49 PM, Bryan Lewis wrote:

> Hmm, I must be missing something.  I looked at CAY-414

not ready to comment on that now. The validateFor* suggestion is  
actually not related at all.

> and looked for
> related changes in the latest code, but didn't grok it.  I tried the
> validateFor methods, but they still get called for phantom  
> changes... I
> tried adding to a to-many

I think this one is a bug in the algorithm. Will need to investigate.


> and setting an attribute to its current value.

Now this is strange. If you can confirm this behavior, then this is a  
bug. I think you have other changes to the object in question though.

Andrus


Re: how to catch updates just before commit

Posted by Bryan Lewis <br...@maine.rr.com>.
Hmm, I must be missing something.  I looked at CAY-414 and looked for
related changes in the latest code, but didn't grok it.  I tried the
validateFor methods, but they still get called for phantom changes... I
tried adding to a to-many and setting an attribute to its current
value.  Tried the latest nightly jar.

This isn't an urgent thing for me.  I'll wait a while and check again. 
Thanks.



Andrus Adamchik wrote:

> I suggest overriding the validateFor* methods. This was problematic 
> in the past, but now Cayenne guarantees that validation methods are 
> called only for the objects that will be committed (i.e. those with 
> "phantom" modifications are not validated).
>
> Andrus
>
>
> On Apr 28, 2006, at 12:40 PM, Bryan Lewis wrote:
>
>> We have a time-when-last-modified timestamp column in most of our 
>> tables
>> to serve as an optimistic-locking attribute.  At present we're setting
>> it with a bit of inelegant code that imitates the way we used to do it
>> in our old WebObjects days... we have a saveChanges() method that  wraps
>> dc.commitChanges().  Before the commit we can timesstamp the  newObjects
>> and modifiedObjects.
>>
>> The trouble is, this sets the timestamp on objects that haven't really
>> been modified... an attribute was set to the same value it already  had,
>> or a to-many relationship was added to.  Cayenne figures out later in
>> the pipeline that those other changes aren't real and doesn't generate
>> SQL for them.  How can I hook into that later point, and set the
>> timestamp only if the row is about to be updated?  I looked at
>> DataContextDelegate, but I guess that's for external changes.  The 
>> event
>> mechanism looks closer, although the docs say that dc.onSync() isn't
>> intended for direct use.  Worst case, I could compare the object's
>> current values to the snapshot but that seems reinventing the  plumbing.
>>
>> Thanks.
>>
>>
>


Re: how to catch updates just before commit

Posted by Andrus Adamchik <an...@objectstyle.org>.
I suggest overriding the validateFor* methods. This was problematic  
in the past, but now Cayenne guarantees that validation methods are  
called only for the objects that will be committed (i.e. those with  
"phantom" modifications are not validated).

Andrus


On Apr 28, 2006, at 12:40 PM, Bryan Lewis wrote:

> We have a time-when-last-modified timestamp column in most of our  
> tables
> to serve as an optimistic-locking attribute.  At present we're setting
> it with a bit of inelegant code that imitates the way we used to do it
> in our old WebObjects days... we have a saveChanges() method that  
> wraps
> dc.commitChanges().  Before the commit we can timesstamp the  
> newObjects
> and modifiedObjects.
>
> The trouble is, this sets the timestamp on objects that haven't really
> been modified... an attribute was set to the same value it already  
> had,
> or a to-many relationship was added to.  Cayenne figures out later in
> the pipeline that those other changes aren't real and doesn't generate
> SQL for them.  How can I hook into that later point, and set the
> timestamp only if the row is about to be updated?  I looked at
> DataContextDelegate, but I guess that's for external changes.  The  
> event
> mechanism looks closer, although the docs say that dc.onSync() isn't
> intended for direct use.  Worst case, I could compare the object's
> current values to the snapshot but that seems reinventing the  
> plumbing.
>
> Thanks.
>
>