jOOQ’s DSL, like any fluent API, has one big caveat. It’s very easy to forget to call .execute()
. And when you do, chances are, you’re going to be staring at your code for minutes, because everything looks perfect:
ctx.insertInto(T)
.columns(T.A, T.B)
.values(1, 2);
Staring… staring… staring… Why is it not inserting that row?
“Aaaah, not again!!”
This is how it’s done:
ctx.insertInto(T)
.columns(T.A, T.B)
.values(1, 2)
.execute();
In principle, this kind of mistake can happen with any fluent API. E.g. StringBuilder
sb.append("a").append("b"); // Not consuming the result
Or streams:
Stream.of(1, 2).peek(System.out::println); // Not so much peeking
But it usually doesn’t happen as much, because the difference to jOOQ is that
- jOOQ’s DML statements (
INSERT
,UPDATE
,DELETE
,MERGE
) and DDL statements (CREATE
,ALTER
,DROP
,TRUNCATE
), and a few other produce a side effect - That side effect is the only thing we care about. The result (the update count) is mostly irrelevant
And as such, we don’t care about the result of execute()
, which is an int
. No one forgets to call fetch()
on a jOOQ ResultQuery
:
ctx.select(T.A, T.B)
.from(T); // Well duh
Because without calling fetch()
or something similar, we’re not getting any results, and we want the results, just like with the StringBuilder
or the Stream
. But we don’t want the execute()
result.
As such, even we, when writing jOOQ’s integration tests, occasionally forget to call this silly little method.
No more!
When it happened again this week…
… I finally created an issue to think about it: https://github.com/jOOQ/jOOQ/issues/11718. And I created an issue to wonder if JetBrains could do something about it: https://youtrack.jetbrains.com/issue/IDEA-265263
And they already can! Apart from the org.jetbrains.annotations.Contract
annotation, which is there for precisely this reason, apparently, it’s also possible to mimick the JSR-305 @CheckReturnValue
annotation on every method “whose return value needs to be checked” (i.e. a method that has no side effect, or whose side effect is to mutate only “this
“).
I added this annotation, I added it to all the relevant jOOQ API, which was a bit of yak shaving (https://github.com/jOOQ/jOOQ/commit/f2b529a2305f8c5f8d037776687887a5acd50b11) and voilà

As you can see, IntelliJ now warns the user whenever they forget to consume the result of any of jOOQ’s DSL methods (by calling execute()
, passing it to some method that consumes it, etc.)
Thanks again to Tagir Valeev from JetBrains for walking me through this and even improving the @Contract
annotation, which jOOQ might use in a future version.
It may seem silly at first, but it’s small details like this that make giant achievements possible. It takes millions of them, but they’re still details. That’s why we keep saying that “details matter”.
Thank you.
You’re welcome! :)
Yes. jOOQ is all about these little details. They matter. A lot! For example, a
ResultQuery<R>
extendsIterable<R>
to enable foreach loops: https://blog.jooq.org/2016/09/27/a-hidden-jooq-gem-foreach-loop-over-resultquery/What are your top 5 details of jOOQ?
In no particular order:
DSL.noCondition(), table.antiJoin(), table.seek(), the fact that the number of bind variables in an IN clause are rounded to 2^n to avoid excessive query planning and that for every bug report I opened over all the years you’ve been incredibly fast to respond. Ok, that last one probably doesn’t count as detail ;-)
Ah, excellent! noCondition() and seek() seem obvious. antiJoin() is a pleasant surprise. Bind padding is obvious now that you say so.