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.