With jOOQ 3.7, we have finally added formal support for Java 8 features. This opened the door to a lot of nice improvements, such as:

Creating result streams

try (Stream<Record2<String, String>> stream = DSL.using(configuration) .select(FIRST_NAME, LAST_NAME) .from(PERSON) .stream()) { List<String> people = stream.map(p -> p.value1() + " " + p.value2()) .collect(Collectors.toList()); }

Calling statements asynchronously (jOOQ 3.8+)

CompletionStage<Record> result = DSL.using(configuration) .select(...) .from(COMPLEX_TABLE) .fetchAsync(); result.thenComposing(r -> ...);

But obviously, we didn’t want to disappoint our paying customers who are stuck with Java 6 because of their using an older application server, etc.

How to support several Java versions in a single API

This is why we continue publishing a Java 6 version of jOOQ for our commercial customers. How did we do it? Very easily. Our commercial code base (which is our main code base) contains tons of “flags” as in the following example:

public interface Query extends QueryPart, Attachable /* [java-8] */, AutoCloseable /* [/java-8] */ { int execute() throws DataAccessException; /* [java-8] */ CompletionStage<Integer> executeAsync(); CompletionStage<Integer> executeAsync(Executor executor); /* [/java-8] */ }

(Sure, AutoCloseable was available already in Java 7, but we don’t have a Java 7 version).

When we build jOOQ, we build it several times after using a preprocessor to strip logic from the source files:

The commercial Java 8 version is built first as is

The commercial Java 6 version is built second by stripping all the code between [java-8] and [/java-8] markers

and markers The commercial free trial version is built by adding some code to the commercial version

The open source version is built third by stripping all the code between [pro] and [/pro] markers

Advantages of this approach

There are several advantages of this approach compared to others:

We only have a single source of truth, the original commercial source code.

The line numbers are the same in all different versions

The APIs are compatible to a certain extent

No magic is involved via class loading or reflection

The disadvantages are:

Committing to repositories is a bit slower as we have several repositories.

Publishing releases takes longer as the different versions need to be built and integration tested several times

Sometimes, we simply forget adding a marker and have to re-build again when the Java-6 nightly build crashes

We still cannot use lambda expressions in ordinary code that is contained in the Java 6 version (most code)

In our opinion, the advantages outweigh clearly. It’s OK if we can’t implement top-notch Java features as long as our customers can, and as long as those customers who are stuck with old versions can still upgrade to the latest jOOQ version.

We’re looking forward to supporting JDK 9 features, like modularity and the new Flow API without any compromise to existing users.

What about you?

How do you approach cross JDK version compatibility?