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.
sb.append("a").append("b"); // Not consuming the result
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 (
MERGE) and DDL statements (
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
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
As such, even we, when writing jOOQ’s integration tests, occasionally forget to call this silly little method.
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 “
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.
4 thoughts on “Never Again Forget to Call .execute() in jOOQ”
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”.
You’re welcome! :)
Yes. jOOQ is all about these little details. They matter. A lot! For example, a
Iterable<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.