cayenne-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Andrus Adamchik <>
Subject Re: How to execute a stored procedure as part of a commitChanges?
Date Tue, 12 Apr 2016 08:39:39 GMT
I think this is a valid scenario. While most of the time discarding context's objects state
on tx failure is appropriate, I guess this is not always the case. Ideally your SQL call should
execute inside DataDomainFlushAction.flush(..) between runQueries() and postprocess(). Alas,
we don't have public API to insert custom code in there. So you will have to resort to some
hacking similar to what you described. So your options are:

1. Try an approach that you described. It may have some side effects depending on what filters
you have, but generally it sounds like it would work.

2. Serialize your ObjectContext to a byte[] before commit. Then on failure you can restore
it to a new context with all your uncommitted changes preserved. I also think there may be
a solution based on nested contexts, with performance (CPU and memory) probably close to serialization.

3. Fork Cayenne to patch DataDomainFlushAction to allow custom code, and then we can collaborate
to turn it from a hack into a feature. Specifically , I wanted for some time to rewrite Cayenne
backend as a pipeline with explicit stages, where you can insert listeners between any 2 stages,
allowing them to alter, analyze or expand stages behavior (as opposed to the current filters
that can only wrap around a big black box of code. We did something similar for LinkRest pipelines,
and that design proved to be very flexible.


> On Apr 12, 2016, at 5:59 AM, Juan Manuel Diaz Lara <>
> 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 <> 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
> <> 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 <>
>> 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
>> <> 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 <
>>>> wrote:
>>>   See the bottom part of this page starting at "In the second scenario":
>>> On Mon, Apr 11, 2016 at 10:54 AM, Juan Manuel Diaz Lara
>>> <> 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

View raw message