Not completely germane to the discussion, but keep in mind that modifiedObjects() can return far more objects than you expect since it includes all phantom changes:
user.gerFirstName() => "John" user.setFirstName("John") => Modified Object/Phantom Change These types of phantom changes happen quite frequently in web-applications during FORM submissions as the value is blindly set again while processing the FORM, even if the value didn't change. It is a CPU/RAM trade-off. If you think you might have a lot of phantom changes and RAM usage is more of a concern than CPU usage, you'll want to prune out the phantom changes. mrg On Wed, Apr 13, 2016 at 4:29 AM, <do...@xsinet.co.za> wrote: > Hi Juan > > You can use context.modifiedObjects() to get a list of objects that have > changed and then process each one, something like: > > Map<CayenneDataObject,DataRow> changeMap = new HashMap<>(); > > for ( CayenneDataObject dataObj : (List<CayenneDataObject>) > context.modifiedObjects() ) > { > DataRow changes = context.currentSnapshot( dataObj ).createDiff > ( > context.getObjectStore().getSnapshot( dataObj.getObjectId() ) > ); > > changeMap.put( dataObj, changes ); > } > > > And then on failure: > > > for ( Entry<CayenneDataObject,DataRow> changEntry : changeMap.entrySet() ) > { > context.rollbackChanges(); > context.currentSnapshot( changEntry.getKey() ).applyDiff( > changEntry.getValue() ); > } > > > You can also get a list of new and deleted objects in a context with: > context.deletedObjects(); > context.newObjects(); > > Regards > Jurgen > > > -----Original Message----- From: Juan Manuel Diaz Lara > Sent: Wednesday, April 13, 2016 1:31 AM > To: user@cayenne.apache.org ; Juan Manuel Diaz Lara ; do...@xsinet.co.za > > Subject: Re: How to execute a stored procedure as part of a commitChanges? > > Jurgen, your code is great for one dataobject, but how to do it for the > entire context automatically ? > Atte. Juan Manuel Díaz Lara > > On Tuesday, April 12, 2016 2:04 PM, Juan Manuel Diaz Lara > <jmdia...@yahoo.com.INVALID> wrote: > > > Thanks, I will try it this later, report results. > Atte. Juan Manuel Díaz Lara > > On Tuesday, April 12, 2016 5:16 AM, "do...@xsinet.co.za" < > do...@xsinet.co.za> wrote: > > > Hi Juan > > With regards to your question: is there some way to get a diff of the > failed context and apply that to the new context ? > > You could try the following: > > 1. Before context.commitChanges() do something like: > > DataRow changes = context.currentSnapshot( this ).createDiff > ( > context.getObjectStore().getSnapshot( getObjectId() ) > ); > > 2. On failure do: > > context.rollbackChanges(); > context.currentSnapshot( this ).applyDiff( changes ); > > Then you should be back to where you started before the attempted commit. > > Regards > Jurgen > > > > > -----Original Message----- From: Juan Manuel Diaz Lara > Sent: Tuesday, April 12, 2016 4:59 AM > To: user@cayenne.apache.org > Subject: Re: How to execute a stored procedure as part of a commitChanges? > > The fact is that the persistenceState of dataobjects is being set to > COMMITTED before the actual transaction (db transaction or whatever) is > committed, specially when running commitChanges() inside > performInTransaction() for the purpose to run additional code inside the > same transaction and after commitChanges(). Think of a rich client > application with many changes, additions and deletions before we can > commit, > it is not easy to replay this operations on a new context to retry all > after > making some corrections (or is there some way to get the a diff of the > failed context and apply then to the new context ?). > > Elaborating on hacking cayenne, I found we can inject a new > TransactionFactory and new ObjecStoreFactory on ServerRuntime, so it is > possible to extend cayenne. I think i can install a new ObjectStore with a > modified postprocessAfterCommit(GraphDiff) that runs its code after > currentTransactionCommits. My hope is that if the transaction fails after > commitChanges() then we can prevent postprocessAfterCommit to run and so > the context wil remain in the state it was before commitChanges(), after > that we can retry the transaction (on the same context) after making some > more changes. > > I need help with: > 1. is this the only change needed considering other things like > DataChannelFilters ?2. what about the interaction with nested context? how > to care of this ?3. what do i need to do on rollback ? > > Thanks. > Atte. Juan Manuel Díaz Lara > > On Monday, April 11, 2016 3:21 PM, John Huss <johnth...@gmail.com> > wrote: > > > I think changing Cayenne is the wrong solution here. > > The problem is that your commit failed. You have to rollback the context if > you want to keep using it. > > On Mon, Apr 11, 2016 at 2:22 PM Juan Manuel Diaz Lara > <jmdia...@yahoo.com.invalid> wrote: > > Calling rollbackChanges will lost all changes, and create a new context >> and start over is complex with many and variables changes to replicate in >> the new context... >> I think the problem is that sync with db state is lost if the dataobjects >> are market commited before the real transaction commits, in fact, this >> what >> the code in ObjectStore that change the persistentceState: >> * Internal unsynchronized method to process objects state after >> commit. >> * >> * @since 1.2 >> */ >> void postprocessAfterCommit(GraphDiff parentChanges) { >> but that is not true, because transaction commits happen after this code >> runs no before as implied. >> >> I am considering some hacking along the lines: >> 1. Allow Transaction to execute arbitrary code after commit.2. ObjectStore >> now should register with current transaction to run postprocessAfterCommit >> after the transaction commits; >> Well this is the idea, but I need more guide to do this, maybe not the >> right classes to hack o derive, how to install my changes in a modular >> way, >> etc., I am really new to cayenne. >> Atte. Juan Manuel Díaz Lara >> >> On Monday, April 11, 2016 11:04 AM, John Huss <johnth...@gmail.com> >> wrote: >> >> >> Try calling context.rollbackChanges() or just create a new context and >> start over. >> >> On Mon, Apr 11, 2016 at 10:31 AM Juan Manuel Diaz Lara >> <jmdia...@yahoo.com.invalid> wrote: >> >> > >> > >> > I am using 4.0.M3. >> > I used the following solution, but the problem is that after >> > commitChanges() the dataobjetcs are set to PersistenceState.COMMITED, if >> > the stored procedure fails Cayenne does a DB rollback (that's ok), but >> > dataobjects stay COMMITED, so I can not retry the this transaction after >> > errors are solved. >> > Is there any way to revert the persistenceState to their values before >> > commitChanges if I detect the stored procedure failed ? >> > >> > cayenneRuntime.performInTransaction(new >> > TransactionalOperation<Integer>() >> > { >> > >> > @Override >> > public Integer perform() { >> > context.commitChanges(); >> > //... >> > SQLTemplate s = new SQLTemplate("SELECT >> > pkg_inventario_fisico.apply( #bind($idAlmacen, 'VARCHAR')) AS id", >> > true); >> > s.setParamsArray(a); >> > @SuppressWarnings("unchecked") >> > List<DataRow> rows = >> > CayenneDao.instance.context.performQuery(s); >> > DataRow row = rows.get(0); >> > //... >> > return null; >> > } >> > >> > } >> > ); >> > Atte. Juan Manuel Díaz Lara >> > >> > On Monday, April 11, 2016 10:06 AM, Mike Kienenberger < >> > mkien...@gmail.com> wrote: >> > >> > >> > See the bottom part of this page starting at "In the second scenario": >> > >> > >> > >> >> https://cayenne.apache.org/docs/4.0/cayenne-guide/persistent-objects-objectcontext.html#transactions >> > >> > >> > On Mon, Apr 11, 2016 at 10:54 AM, Juan Manuel Diaz Lara >> > <jmdia...@yahoo.com.invalid> wrote: >> > > >> > > >> > > I have a dataobjet graph with some changes, and a db stored procedure >> > that should run after this changes are in the db, but must run in the >> same >> > transaction because it make some more changes to other data, this >> > changes >> > can fail so I want the commitChanges fail if the stored procedure fails. >> > > Atte. Juan Manuel Díaz Lara >> > > >> > > >> > > >> > > >> > >> > >> >> >> > > > > >