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. 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)); 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. 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. 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 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. 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, Witold Szczerba On Tue, Nov 18, 2014 at 11:03 PM, Andrey Antukh <[email protected]> wrote: > Hi everyone! > > 2014-11-18 16:23 GMT+01:00 Lukas Eder <[email protected]>: > >> Hi Andrey, >> >> 2014-11-17 15:51 GMT+01:00 Andrey Antukh <[email protected]>: >> >>> Andrey. >>> >>> Sent from my Nexus 4 >>> On Nov 17, 2014 11:37 AM, "Lukas Eder" <[email protected]> wrote: >>> > >>> > Yes, I understand that part of the difference - and I remember your >>> criticism. Essentially, jOOQ currently expects you to pass the transaction >>> reference (as contained in a derived Configuration) around, whereas ULTM >>> probably keeps it somewhere in a ThreadLocal. In jOOQ, the "ThreadLocal >>> solution" might emerge in a future version as well, in parallel to the >>> existing explicit version (https://github.com/jOOQ/jOOQ/issues/2732). I >>> can't promise any ETA, as this might introduce significant changes to all >>> of jOOQ's internals. >>> > >>> > But having both models in parallel isn't so uncommon. In Spring, for >>> instance, you can also choose to use "implicit" transactions via >>> @Transactional annotations, or "explicit" ones via a transaction manager >>> that you have to keep around. In the end, there are these two models. I >>> don't see a clear advantage of one over the other, apart from personal >>> taste. But I agree that jOOQ currently doesn't offer the "implicit" model. >>> > >>> >>> Implicit is always better than implicit. Personally I prefer the current >>> tx jOOQ api over any other like spring... >>> It is very flexible and allow implement own implicit tx management >>> without much of problems. >>> >>> Implicit tx management is not suitable for all cases. In my opinion any >>> implicit transaction management should be out of scope of jOOQ (different >>> library is a good place for it...) >>> >> *For the group:* Andrey has implemented Suricatta ( >> http://niwibe.github.io/suricatta). A SQL API built on top of jOOQ for >> Clojure. >> > > Thanks for the presentation. > >> >> *Andrey:* I haven't had a look at the implementation details of your >> TransactionProvider yet. If I'm not mistaken, the sources are these ones >> here, right? >> >> https://github.com/niwibe/suricatta/blob/7af06380bf566edce092091ff3ff2410fd81107e/src/suricatta/core.clj#L109 >> > > Yes, you are right. > >> >> >> Or explicitly: >> >> >> (defn atomic-apply "Execute a function in one transaction or >> subtransaction." [^Context ctx func & args] (let [^Configuration conf (. >> derive (proto/get-configuration ctx)) ^TransactionContext txctx ( >> transaction-context conf) ^TransactionProvider provider (. >> transactionProvider conf)] (doto conf (.data "suricatta.rollback" false) >> (.data "suricatta.transaction" true)) (try (.begin provider txctx) (let [ >> result (apply func (types/->context conf) args) rollback? (.data conf " >> suricatta.rollback")] (if rollback? (.rollback provider txctx) (.commit >> provider txctx)) result) (catch Exception cause (.rollback provider (. >> cause txctx cause)) (if (instance? RuntimeException cause) (throw cause) >> (throw (DataAccessException. "Rollback caused" cause))))))) >> >> What would your TransactionProvider look like in plain Java code, more or >> less? >> > > This code is representing something that jOOQ has already implemented > (very similar, concretely on > https://github.com/jOOQ/jOOQ/blob/master/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java#L299). > Obviously it has different semantics, because it also handles explicit > rollback flag instead of using exceptions. But this uses a default > transaction provider of jOOQ, because it is simple and sufficient for > implement over it any sugar syntax. > > The java code maybe can a little bit verbose, but only for java syntax and > types declaration, but it would be mostly the same. The grace of use > clojure in this case, is its flexible syntax that makes use of explicit > transaction management (without thread locals) very intuitive. Let see an > example: > > (atomic ctx > (do-something-with-ctx ctx) > (atomic ctx > (do-something-other-in-a-subtransaction-with-ctx ctx))) > > (`atomic` is a clojure macro that behind the schenes uses the > `atomic-apply`, see previous code example) > > Something like this in java7 would be very very verbose, because it > implies creating a lot of anonymous clases. > With java8 this verbosity is reduced obviously. > > But with clojure syntax flexibility, it allows use the explicit > transaction management, without any threadlocal in very comfortable way. > Implement something implicit is also very easy, but is less necessary if a > language that are you using allows some facilities for work with that. > > Personally, I don't understand the advantages of using something like > sprint tx. They mostly provides accidental complexity and try to solve > something that language can not solve. jOOQ currently provides simple and > flexible transaction provider interface, that enables that any one can > implement over it the concrete use case of that any one can need. In this > way, I think jOOQ is more agnostic of frameworks, that is good. (It > obviously make things easy for implement something like suricatta with own > transaction semantics on top of the jOOQ transaction provider without much > problems). > > Obviously, is a subjective opinion. ;) > > Greetings. > Andrey > > >> -- >> 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. >> > > > > -- > Andrey Antukh - Андрей Антух - <[email protected]> / < > [email protected]> > http://www.niwi.be <http://www.niwi.be/page/about/> > https://github.com/niwibe > > -- > 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. > -- 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.
