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, <[email protected]> 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: [email protected] ; Juan Manuel Diaz Lara ; [email protected]
>
> 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
> <[email protected]> wrote:
>
>
> Thanks, I will try it this later, report results.
> Atte. Juan Manuel Díaz Lara
>
> On Tuesday, April 12, 2016 5:16 AM, "[email protected]" <
> [email protected]> 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: [email protected]
> 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 <[email protected]>
> 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
> <[email protected]> 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 <[email protected]>
>> 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
>> <[email protected]> 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 <
>> > [email protected]> 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
>> > <[email protected]> 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
>> > >
>> > >
>> > >
>> > >
>> >
>> >
>>
>>
>>
>
>
>
>
>