Great. If you like, please make a task in our tracking system that we should improve the UI in the modeler to disable the attribute lock options (or show a warning) if the entity is not set to optimistic locking.
Thanks Ari On 1/11/10 3:22 AM, Paulo Andrade wrote:
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
-- --------------------------> Aristedes Maniatis GPG fingerprint CBFB 84B4 738D 4E87 5E5C 5EFA EF6A 7D2E 3E49 102A
