On Thursday, November 1, 2012 5:58:25 PM UTC+1, Chris Lercher wrote:
>
> I'd like to discuss how to best use (JPA) transactions with RequestFactory.
>
> As a basis, I'm assuming the approach where one user interaction should 
> normally result in one DB transaction, so that the whole interaction can 
> either fail or succeed, atomically.
>
> The first thing to consider with RequestFactory is, that each method call 
> in a RequestContext can succeed or fail individually. Also note, that the 
> fire() call will (usually) succeed as a whole, even if one of the 
> RequestContext method calls fails.
>
> So for example, if you have
>
>   ctx.opA().to(receiverA);
>   ctx.opB().to(receiverB);
>   ctx.fire(receiverX);
>
> Then this may result in the sequence
>
>   receiverA.onSuccess(), 
>   receiverB.onFailure(), 
>   receiverX.onSuccess().
>
> What this means is, that you get atomicity only for the individual 
> RequestContext methods. Therefore you definitely shouldn't wrap the entire 
> RequestFactoryServlet's onPost() method in one transaction, but rather only 
> the individual service method implementations individually. (Otherwise, the 
> client may believe, that opA has succeeded, while in reality the entire 
> transaction was rolled back.)
>
> And if you need a transaction with multiple objects involved, you will 
> have to wrap the entire interaction in one service method call.
>

Absolutely. This is The One and True Way™.

*Side note: Convenience solution*
> *
> *
> If you grow tired of wrapping each service method implementation in a 
> transaction individually, you may want to use your own 
> ServiceLayerDecorator in the RequestFactoryServlet constructor, and 
> override invoke() like this:
>
>   @Override
>   public Object invoke(final Method domainMethod, final Object... args) {
>      
>     // Note: Ideally use dependency injecton for this:
>     final EntityManager em = getRequestScopedEntityManager();
>
>     try {
>       
>       em.getTransaction().begin();
>       final Object result = super.invoke(domainMethod, args);
>       em.getTransaction().commit();
>       
>       return result;
>       
>     } catch (final Error e) {
>       em.getTransaction().rollback();
>       throw e;
>       
>     } catch (final RuntimeException e) {
>       em.getTransaction().rollback();
>       throw e;
>     }
>   }
>
> (Better solutions are welcome!)
>

Guice Persist's @Transactional works great (of course with a 
ServiceLayerDecorator to instantiate your services through Guice): 
http://code.google.com/p/google-guice/wiki/Transactions
I believe it'd work equally well with Spring AOP (though maybe it requires 
using an interface, can't remember, haven't used Spring in a while), or of 
course AspectJ or similar compile-time weaver.

*Question/Discussion:*
>
> There is however a little issue that remains: RequestFactory calls 
> Locator.find() outside of such a transaction:
>
>    1. In RequestState.getBeansForPayload(), which calls 
>    ServiceLayerDecorator.loadDomainObjects()
>    2. In SimpleRequestProcessor.createReturnOperations, which calls 
>    ServiceLayerDecorator.isLive()
>
> I think, it's possible to pretty much ignore this (at least if you don't 
> use isolation levels higher than READ_COMMITTED, and if your DB is ok with 
> autocommit style queries), or is it? Note: My Locator's find method re-uses 
> the same RequestScoped EntityManager instance - so I get the same 
> underlying Hibernate session with the same first-level cache - it's just 
> not in the same transaction, that's all.
>

I believe you MUST use a @RequestScoped EntityManager (again, Guice Persist 
FTW), to make sure you always have a single instance of a given (logical) 
entity. 
See http://code.google.com/p/google-web-toolkit/issues/detail?id=7341
 

> How do you deal with this? What is the intended transaction concept by the 
> RequestFactory designers?
>

They have left Google unfortunately, but I believe what you describe here 
is The One True Way™ of using RF with JPA (or JDO or whatever).
This is how I do it with JPA, and we had to build a tricky caching layer 
for MongoDB/Morphia in another app to mimic that behavior (except mongo has 
no notion of transactions).

-- 
You received this message because you are subscribed to the Google Groups 
"Google Web Toolkit" group.
To view this discussion on the web visit 
https://groups.google.com/d/msg/google-web-toolkit/-/FKHh41RH5OAJ.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/google-web-toolkit?hl=en.

Reply via email to