A tidal wave is rippling through the Java ecosystem. It is the renaming of
jakarta package names. Now, while we’ve all been whining and complaining and shaking our heads due the clash between corporate legal and engineering interests, eventually it’s time to move on and learn what this means specifically, for jOOQ.
jOOQ has 3 Java EE dependencies in total:
- JAXB – This is quite the ubiquitous dependency in jOOQ, and it’s the one we’ll discuss mostly in this blog post. It is used by the jOOQ runtime and code generation modules, and it currently isn’t optional (at least the API needs to be present).
- JPA – This is an optional dependency in jOOQ. The jOOQ runtime can map to JPA entities to some extent, and the code generator can generate entity annotations to some extent.
- Bean validation – This is not a formal dependency. The code generator can generate bean validation annotations, and that’s it.
All of these 3 dependencies were migrated to Jakarta EE in jOOQ 3.16 with Issue #9641. The change was inevitable at some point, but given that Spring Boot 3.0 will migrate as well, and temporarily removed jOOQ from their development builds (see spring-boot#28821), I thought we might as well migrate now.
What does the migration mean for jOOQ, specifically?
JAXB is the biggest Java EE dependency, and also the one we can’t get rid of easily. At least not of the API. There are 3 popular XML formats that are currently marshalled and unmarshalled using JAXB:
Specifically, the code generation configuration’s JAXB annotations are very useful, as users can merge XML files with programmatic code generation configuration easily. Also, the popular
gradle-jooq-plugin third party contribution by Etienne Studer works by introspecting the annotations.
Due to the mess created by the JDK 11’s removal of the JAXB API and implementation, we’ve already implemented our own “MiniJAXB” implementation that satisfies our limited JAXB needs. This allows for working with jOOQ even if there’s no JAXB implementation on the classpath. But the API is still mandatory, and as discussed here, it seems we can’t get rid of it easily.
DefaultRecordMapper can map to JPA entities to some limited extent. This allows for easier migrations from JPA to jOOQ as well as use annotation based mapping for those who like that, without re-inventing our own custom annotations.
At the same time, the code generator can be configured to generate JPA annotations on generated POJO classes, mostly for the same
DefaultRecordMapper usage purposes, much more than to use the generated code as actual entities.
Both of these features have always been controversial and this whole new Java EE vs Jakarta EE hassle may be a tipping point in favour of removing them eventually. The decision has not been made yet, but this area is certainly not where jOOQ generates most value.
We don’t implement bean validation, but it has always been a low hanging fruit to generate a few bean validation annotations on generated POJO classes in jOOQ, just to enrich your POJOs with some well known meta data, such as e.g.
@Size for a string. Since we don’t have a hard dependency on this Jakarta EE project (only your generated code does, and only if you opt in to this), this issue isn’t really affected much by the migration to Jakarta EE
Backwards compatible or not?
So, given that we had to take action in jOOQ 3.16 or at the latest in jOOQ 3.17 (given Spring Boot’s understandable stance), the question was:
- Should we offer support for both Java EE and Jakarta EE APIs for a while?
- Should we move on and forget about Java EE?
- Should we drop support for the integrations?
Clearly, dropping support wasn’t an option for JAXB (though it was for JPA and Bean Validation). Supporting both in parallel could work, with two sub-options:
- Ship a single artifact with both dependencies
- Ship multiple artifacts, one for each dependency
The former is just asking for trouble. Not for jOOQ. It would be simple for jOOQ to support both APIs, but having both APIs on all jOOQ user classpaths is just terrible and would cause so many issues in client projects.
Shipping multiple artifacts would be clean and allow for users to decide on their own what to do. This is what many other projects did, e.g. Hibernate. But those who did this had a strong dependency on Jakarta EE projects, e.g. Hibernate on JPA. It makes sense for Hibernate to split into two distributions. Not so much for jOOQ, which makes only marginal use of these APIs (even of JAXB).
Besides, jOOQ already has a ton of distributions, which you can see on the downloads page: https://www.jooq.org/download/versions.
- The jOOQ Open Source Edition
- The jOOQ Trial Edition for Java 17
- The jOOQ Trial Edition for Java 11
- The jOOQ Trial Edition for Java 8
- The jOOQ Pro Edition for Java 17
- The jOOQ Pro Edition for Java 11
- The jOOQ Pro Edition for Java 8
(Note there were good reasons to not use multi-release jars, in case you were wondering.)
Now imagine the mess if all of these editions needed another dimension for the Jakarta EE dependency. That’s not practical.
So, we bit the bullet and moved on. Starting from jOOQ 3.16, Java EE has gone and Jakarta EE is our dependency, where needed.
What all this mess has taught us once more is that dependencies are a pain. They’re an even bigger pain for a library like jOOQ (because of the pain it causes in user projects). We have an (almost) zero dependency policy, which we cannot follow ourselves thoroughly, including because the JDK decided to ditch JAXB again in Java 11, after adding it in Java 6 (I think?). JAXB didn’t seem to be a dependency when jOOQ started out, but it effectively was.
So, the future of jOOQ will eventually get rid of these dependencies where possible, or move them into optional plugin modules.