Yeah, I totally understand how this can be useful. I am trying to analyze the 
situation and just like with most things in Cayenne core it can get pretty 
deep. Some factors to consider:

1. Performance (I mentioned about that before).

2. "Circular changes" canceling modifications: 

  assertEquals("A", o.getP());
  o.setP("B"); // modified
  o.setP("A"); // modified? (property change is "canceled", but maybe other 
properties are also modified)

3. "Uncommittable changes". An example - objects with modified to-many 
relationships are treated as modified, but during commit their DB records are 
not updated.

So I am leaning towards either a method like 
ObjectContext.hasCommittabledChanges() that would do deeper analysis of the 
object graph, or maybe a more involved changeset API that I started to draft 
under cayenne-lifecycle, that would among other things allow the caller to 
obtain a more nuanced and detailed view of the graph changes (overall changes, 
changes per object, committable/uncommitable changes etc.) 

And there's of course we shouldn't forget ROP....

Andrus


On Mar 11, 2011, at 11:14 PM, Michael Gentry wrote:
> My particular use case in my Tapestry application is there are many
> different pages the user can edit before deciding to save.  I wanted
> to display an "Unsaved Changes" message as they navigate around from
> page to page (combination of JavaScript for the current page and
> dataContext.hasChanges() when they move to a new page).  However,
> Tapestry is always calling the setters when the form is submitted
> (page navigation, but not commitChanges()) and this is causing my
> "Unsaved Changes" message to always appear, even when they don't
> actually change values, because Tapestry has called all the setters
> with the original value.
> 
> mrg
> 
> 
> On Fri, Mar 11, 2011 at 3:54 PM, Andrus Adamchik <[email protected]> 
> wrote:
>> I'd rather we don't introduce a potentially expensive comparison in the 
>> superclass setters. A setter may be called many times before a commit, so 
>> the assumption is that it is much cheaper to only do comparisons once during 
>> commit.
>> 
>> I guess you can generate special setters with a custom template. Or we may 
>> run pre-commit logic from within 'hasChanges'...
>> 
>> Andrus
>> 
>> 
>> 
>> On Mar 11, 2011, at 10:22 PM, Michael Gentry wrote:
>>> I just did a quick test using:
>>> 
>>>        DataContext dataContext = DataContext.createDataContext();
>>> 
>>>        User user = dataContext.newObject(User.class);
>>> 
>>>        user.setFirstName("System");
>>>        user.setLastName("Administrator");
>>>        user.setUsername("admin");
>>> 
>>>        dataContext.commitChanges();
>>> 
>>>        user.setFirstName("System"); // This isn't a real change
>>> 
>>>        System.out.println(dataContext.hasChanges());
>>> 
>>>        dataContext.commitChanges();
>>> 
>>> In a nutshell, create a user, commit it, set a value to the same
>>> value, then check hasChanges().  The output is:
>>> 
>>> 
>>> INFO: INSERT INTO Users (first_name, id, last_name, password,
>>> username) VALUES (?, ?, ?, ?, ?)
>>> INFO: [bind: 1->first_name:'System', 2->id:200,
>>> 3->last_name:'Administrator', 4->password:NULL, 5->username:'admin']
>>> true
>>> 
>>> The "true" is that the dataContext has changes, even though the second
>>> commitChanges() doesn't do anything (there are no real changes).
>>> Should we modify CayenneDataObject's writeProperty() to check if the
>>> old value and new value are equal before calling propertyChanged()?  I
>>> was also noticing Embeddables doing something similar.
>>> 
>>> Thanks,
>>> 
>>> mrg
>>> 
>> 
>> 
> 

Reply via email to