Eek, this was lying around in my inbox too long. Sorry for that. 2014-11-19 0:54 GMT+01:00 Witold Szczerba <[email protected]>:
> OK, I get it now. It is just that for me it was not acceptable to pass > context object around from one layer to another. > Yes, I know, and again, you were right - for your use-case :-) >From the jOOQ perspective, your use-case is a special case - an implementation detail that I chose to defer until later. > Let me show you, with code example, to demonstrate, what was the problem > with jOOQ transactions in my case. > > This post is targeted to anyone interested in this subject, but Lukas, you > can just skip it as there is nothing new for you here :) > > The piece of code below is a generic message handler, part of a shared > common lib inside my project which is made of dozens of microservices with > 3 of them being in JVM. The handler is invoked when messages arrive. Such a > code snippets I had in many other projects, but this one is the first when > I do not use any application server (and I like it that way), so I had to > take care of transactions by myself instead of making it done automagically > by EJB. > > Part of this is a RabbitMQ SPI, but it would be almost the same in any > other scenario like simple HTTP handler, servlet, JAX-RS or the like. > > > // full version there: > // http://pastebin.com/nga6SeZK > // > MQueue mQueue = new MQueue(channel, ...); > [...] > // few lines later > > @Override > public void handleDelivery(...) { > try { > txManager.txChecked(() -> mQueue.onMessage(message)); > > // look how it would be with jOOQ: > // but this tiny message handling project knows nothing about jOOQ > // jooq.transaction(ctx -> mQueue.onMessage(message, ctx)); > Not necessarily. You could've just written jooq.transaction(ctx -> mQueue.onMessage(message)); ... just the same. Your implementation of jOOQ's TransactionProvider could have done all that for you. > if (!p.autoAck) channel.basicAck(...); > } catch (Exception ex) { > if (!p.autoAck) channel.basicNack(...); > } > } > > There are other ways to make it work without storing transaction context > in ThreadLocal, in the future - who knows, maybe I would add other ways of > fetching tx context, but I guess they would have to depend on environment, > like Guice's scopes. > Interesting. I'll have to read up on Guice's scopes. jOOQ's various Configuration/Context objects also act like "scopes" (New type: http://www.jooq.org/javadoc/latest/org/jooq/Scope.html), given that they have a Map<Object, Object> where scope implementors can locally register data. I sometimes wish there was an implicit Map on the JVM stack, in which every stack frame can register things that would be available to all child frames, and that would be automatically cleaned up once the frame is "closed". E.g. an implicit: public void method() { try { scope.init("method()"); // User method body here } finally { scope.close(); } } Given that the JVM bytecode is really a stack machine, I'm surprised that nothing like that exists. The difference to local variables (also on the stack) would be the fact that all methods called by the above "method()" would get (read-only) access to all the values from the "outer" scope. Obviously, resolving ambiguities would be a challenge, but many things would be much easier to implement than via AOP or via ThreadLocal hacks. But that's another discussion :-) Of course I could extend the mQueue#onMessage method, so it would use some > proprietary transaction/configuration object from jOOQ, but this is just a > generic message handler, it knows nothing about who will actually consume > that message, maybe the consumer does not know anything about jOOQ at all > and do not have it on it's classpath. > > Another problem distributing by reference passing some kind of context is > that the MQueue class, which is responsible for routing the exact message > to the correct object, would have pass that context yet again (and then > maybe yet again), so now my tiny and generic message handling library is > not only packed with jOOQ API (which is actually not included there at all) > but it also forces all the target consumers to declare dependency on jOOQ > context on each handler even if they do not use it at all. > > Now, the transaction manager as I asked from the very beginning would work > in a different way. What I did with ULTM was actually a carbon-copy of what > I used to in big and fat environments like EJB. The only exception is that > it is like gazillion times simpler, the only thing that is left is the idea > of transparent transaction management. So, I do not care what kind of JDBC > library my consumers will use. All I want is to make my library wrap > message destination handler within generic transaction (which depends on > raw JDBC DataSource) and do a commit/rollback at the end of processing. And > if there was no JDBC calls at all - then nothing is happening. > Which is why you don't use Spring TX or JavaEE, right? ;-) > And last, but not least, the "transparent" JDBC transaction management is > simple to use, yet universal. It does not add complexity to any JDBC > library. It allows me to use jOOQ, plain JDBC, Hibernate > Your assumptions are obviously true for all non-cached database interactions on a JDBC level, including plain JDBC and jOOQ. But I'm actually very curious about how you'll manage to interact with Hibernate on that level. What happens to the Hibernate second-level cache if a third party chooses to roll back a transaction? Will all entities go stale and revert their state to a well-defined, third-party provided savepoint? And after the UNDO, can I still REDO some changes? > or whatever together or separably and the outer layer of my application > (or microservice) does not care. I heard there are people using jOOQ with > JPA/Hibernate. > So far, we're aware of people who use them in very separate parts of the application, perhaps even running queries against different tables. Because of second-level caching, I doubt that they're using jOOQ and JPA within the same transaction - people tend to not even use views with JPA, when second-level caching is active. For example: JPA / Hibernate is used for persistence of transactional data, and jOOQ for reporting / OLAP. We're going to be working together with Red Hat (in particular with Arun Gupta) to look into various ways to integrate with Java EE. You might be interested to add your own questions here: https://github.com/javaee-samples/webinars/issues/4 Other upcoming webinars by Arun can be seen here: https://github.com/javaee-samples/webinars They would benefit as well, because the transaction would span them all. > > Having said all that, I can see the use case of the transaction management > built into jOOQ. I do not like it, but at least I can see others who do and > it works for them. > Thanks for the feedback :-) Cheers Lukas -- You received this message because you are subscribed to the Google Groups "jOOQ User Group" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
