I am a dev with close to zero experience writing Java who currently works
on a Kotlin web application that uses jOOQ - my first significant
application in a JVM language. As such, I frequently find myself surprised
when I encounter interfaces or behaviours in Java libraries that seen to
make little sense from a Kotlin perspective. This message is about such an
experience with jOOQ.
The Kotlin web application has a global exception handler; it has some
special exception types that trigger special behavior in the global error
handler; and at various places the application does stuff in transactions
using code like this:
dsl.transaction() { config ->
// Logic that might throw exceptions
}
As it happens, some of the types of exception we have special handling for
in the global error handler extend from `Exception`, while others extend
from `RuntimeException` - a fact I was not especially conscious of and that
is mostly of no significance in Kotlin, where unchecked exceptions don't
exist and `throw Exception("some exception message")` is a perfectly
acceptable idiom. Imagine my surprise, then, to discover that SOME of those
special exceptions, if I throw them from within a
`DSLContext.transaction()` callback, don't trigger their special behaviours
in the global exception handler, which instead receives a
DataAccessException. Some digging revealed that this code in
`transactionResult0` was to blame, which handles exceptions of types that
inherit from `RuntimeException` differently from those whose type inherits
directly from `Exception` - specifically by wrapping the latter in a
`DataAccessException` instead of rethrowing them directly:
// [#6608] [#7167] Errors are no longer handled differently
if (cause instanceof RuntimeException e)
throw e;
else if (cause instanceof Error e)
throw e;
else
throw new DataAccessException(committed
? "Exception after commit"
: "Rollback caused"
, cause
);
No doubt this behavior seems intuitive and reasonable to Java programmers,
who work in a world with checked exceptions. To a dev using jOOQ from
Kotlin and unfamiliar with Java, though, it felt rather like an arbitrary
trap that didn't need to exist, and my first instinct was to file a bug
report. After all, jOOQ officially supports Kotlin, so shouldn't it refrain
from doing this arbitrary wrapping of (some) exceptions that has no reason
to happen from a Kotlin perspective?
On reflection, though, I'm struggling to figure out how I ought to feel
about this. What's the "correct" behavior for jOOQ to have here? Should
anything change?
I thought for a while about whether jOOQ should behave differently when
being used "from Kotlin" than when being used "from Java", but I think that
- given that Java code can call functions defined Kotlin code that calls
functions defined in Java code and so on ad infinitum - the very concept of
jOOQ being used "from Kotlin" or "from Java" is probably too ill-defined
for this to make sense. Doing something like having behavior change based
on the inclusion of the jOOQ-kotlin package also seems like a disgusting
trap in its own right.
That leaves me with a couple of thoughts on things that jOOQ could do that
might be good ideas:
1. Maybe that block I quoted above should just unconditionally throw `e`?
Obviously just `throw e` won't even compile, but there are hacks (see e.g.
https://stackoverflow.com/q/31316581/1709587) to let you throw exceptions
of checked types without listing them in a `throws` clause in the method
declaration, so it is at least POSSIBLE for jOOQ to behave this way.
Ignoring backwards compatibility, would this indeed be the better behavior
for jOOQ to have? If so, is it worth breaking backwards compatibility over?
2. Maybe having some Kotlin-specific alternative to
`DSLContext.transaction` (and whatever other methods are affected by this
same issue) in jOOQ-kotlin? This could probably be a simple wrapper around
`DSLContext.transaction` that "unwraps" exceptions that are wrapped in
`DataAccessException`.
Alternatively, perhaps the right perspective on this is that we Kotlin
programmers are newcomers in Java lands, and need to assimilate into the
local culture - including understanding Java practices like handling
Exceptions and RuntimeExceptions differently, and anticipating their use.
>From this perspective, perhaps jOOQ is doing nothing wrong or ill-advised
whatsoever, and I've simply learned a necessary lesson about Java.
Certainly a workaround is possible: I can implement the wrapper around
`DSLContext.transaction` that I contemplated above in my application and
use it instead of using `DSLContext.transaction` directly.
What's the right way to think about this, do you reckon? Should anything in
jOOQ change?
Cheers,
Mark
--
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].
To view this discussion on the web visit
https://groups.google.com/d/msgid/jooq-user/c7a318bb-4f76-4734-ba2a-eb5afba20533n%40googlegroups.com.