cayenne-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Andrus Adamchik <>
Subject Re: False Modified
Date Sat, 12 Mar 2011 08:43:52 GMT
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....


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 <> 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

View raw message