OK, figured out what was going on. There's a checkbox on Cayenne Modeler that 
toggles optimistic locking for that entity on or off. I only had the attributes 
marked for locking and didn't see that checkbox.

It is now throwing an OL exception as it should.

On Oct 30, 2010, at 3:53 PM, Paulo Andrade wrote:

> So I went ahead and did a bit of testing:
> 
> I created an app that would execute the following code (the model as an 
> entity named "Counter" with a "value" property marked for locking):
> 
> ----------
>       ObjectContext context = DataContext.createDataContext();
>               
>       Counter counter = context.newObject(Counter.class);
>       counter.setValue(3);
>               
>       context.commitChanges();
>               
>        // Create our two peer contexts
>       ObjectContext context1 = DataContext.createDataContext();
>       ObjectContext context2 = DataContext.createDataContext();
>           
>           // Instantiate object on context1
>           Counter counter1 = (Counter) 
> context1.localObject(counter.getObjectId(), null);
>           LOG.debug("Context1 read counter with value "+ counter.getValue());
>           
>           // Instantiate object on context2
>           Counter counter2 = (Counter) 
> context2.localObject(counter.getObjectId(), null);
>           LOG.debug("Context2 read counter with value "+ counter.getValue());
>           
>           // Context1 makes changes
>           counter1.setValue(counter1.getValue() + 1);
>           LOG.debug("Counter1 incremented to value " +counter1.getValue());
>           
>           // Context2 makes changes
>           counter2.setValue(counter2.getValue() + 1);
>           LOG.debug("Counter2 incremented to value " +counter2.getValue());
>           
>           // Context1 commit
>           context1.commitChanges();
>           
>           // Context2 commit
>           context2.commitChanges();
> ----------
> 
> Now what I would like is for context2.commitChanges() to fail, since this 
> commit would write the value 4 which is already on the database thus failling 
> to increment the value.
> 
> If we take a look at the query logging you can see that the last commit has a 
> 4 on the where clause (which I feel should be a 3).
> 
> ----------
> [INFO] access.QueryLogger +++ Connecting: SUCCESS.
> [INFO] access.QueryLogger --- transaction started.
> [INFO] access.QueryLogger Detected and installed adapter: 
> org.apache.cayenne.dba.postgres.PostgresAdapter
> [INFO] access.QueryLogger SELECT nextval('pk_counter')
> [INFO] access.QueryLogger --- will run 1 query.
> [INFO] access.QueryLogger INSERT INTO Counter (id, value) VALUES (?, ?)
> [INFO] access.QueryLogger [batch bind: 1->id:240, 2->value:3]
> [INFO] access.QueryLogger === updated 1 row.
> [INFO] access.QueryLogger +++ transaction committed.
> [DEBUG] pages.Index Context1 read counter with value 3
> [DEBUG] pages.Index Context2 read counter with value 3
> [INFO] access.QueryLogger --- will run 1 query.
> [INFO] access.QueryLogger --- transaction started.
> [INFO] access.QueryLogger SELECT t0.value, t0.id FROM Counter t0 WHERE t0.id 
> = ? [bind: 1->id:240] - prepared in 10 ms.
> [INFO] access.QueryLogger === returned 1 row. - took 18 ms.
> [INFO] access.QueryLogger +++ transaction committed.
> [DEBUG] pages.Index Counter1 incremented to value 4
> [INFO] access.QueryLogger --- will run 1 query.
> [INFO] access.QueryLogger --- transaction started.
> [INFO] access.QueryLogger SELECT t0.value, t0.id FROM Counter t0 WHERE t0.id 
> = ? [bind: 1->id:240]
> [INFO] access.QueryLogger === returned 1 row. - took 1 ms.
> [INFO] access.QueryLogger +++ transaction committed.
> [DEBUG] pages.Index Counter2 incremented to value 4
> [INFO] access.QueryLogger --- will run 1 query.
> [INFO] access.QueryLogger --- transaction started.
> [INFO] access.QueryLogger UPDATE Counter SET value = ? WHERE id = ?
> [INFO] access.QueryLogger [batch bind: 1->value:4, 2->id:240]
> [INFO] access.QueryLogger === updated 1 row.
> [INFO] access.QueryLogger +++ transaction committed.
> [INFO] access.QueryLogger --- will run 1 query.
> [INFO] access.QueryLogger --- transaction started.
> [INFO] access.QueryLogger UPDATE Counter SET value = ? WHERE id = ?
> [INFO] access.QueryLogger [batch bind: 1->value:4, 2->id:240]
> [INFO] access.QueryLogger === updated 1 row.
> [INFO] access.QueryLogger +++ transaction committed.
> ----------
> 
> Even more surprisingly is that even with Level 1 - No Cache Sharing the same 
> behavior applies.
> 
> So do you guys feel this is normal behavior? How can I code this in a way 
> that the second commitChanges would fail?
> 
> Best regards,
> Paulo Andrade
> 
> On Oct 28, 2010, at 11:50 AM, Paulo Andrade wrote:
> 
>> Hello,
>> 
>> I'm new to Cayenne, and coming form EOF I'm find everything very easy to 
>> understand.
>> 
>> Everything is so similar that I'm wondering that what I consider to be flaws 
>> in EOF also happen in Cayenne.
>> 
>> For a long description of the problem here's a blog post:
>> 
>> http://terminalapp.net/dr-optimistic-locking/
>> 
>> The summary is this:
>> 
>> EOF stores snapshots in the ObjectStoreCoordinator which are shared by 
>> EOEditingContexts (ObjectContexts in Cayenne), these snapshots are updated 
>> on fetches (queries in Cayenne) and saves (commits in Cayenne).
>> 
>> So take an Cayenne application with a Level 2 cache (Local VM Caching), two 
>> ObjectStores (oc1 and oc2) and a Counter object with and intValue attribute 
>> marked for optimistic locking.
>> 
>> What should happen in the following code:
>> 
>> -------------
>> Counter oc1Counter, oc2Counter; // assume both exist and refer to the same 
>> entity in the DB, each in their own context
>> 
>> int i = oc1Counter.intValue();
>> int j = oc2Counter.intValue(); 
>> // both i and j have a value of 3
>> 
>> // now we increment oc1's counter
>> oc1Counter.setIntValue( i+1 ); // sets to 4
>> 
>> oc1.commitChanges(); // saves oc1Counter with a value of 4 to disk and 
>> updates the snapshot
>> 
>> // now increment oc2's counter
>> oc1Counter.setIntValue( j+1 ); // sets to 4 again
>> 
>> oc2.commitChanges(); // **
>> ——————
>> 
>> ** now what do you think should happen here?
>> In EOF the save succeeds and the previous change is overwritten without me 
>> knowing about it. Will Cayenne do the same?
>> 
>> Best regards,
>> Paulo Andrade
>> 
> 

Reply via email to