Over a million developers have joined DZone.

Top 5 Hidden jOOQ Features

DZone's Guide to

Top 5 Hidden jOOQ Features

A lot of people don't really need a SQL builder — yet, jOOQ can still be immensely helpful in other situations through its lesser-known features.

· Database Zone ·
Free Resource

Databases are better when they can run themselves. CockroachDB is a SQL database that automates scaling and recovery. Check it out here.

jOOQ's main value proposition is obvious: type-safe embedded SQL in Java.

People who actively look for such a SQL builder will inevitably stumble upon jOOQ and love it, of course. But a lot of people don't really need a SQL builder — yet, jOOQ can still be immensely helpful in other situations through its lesser-known features.

Here's a list of top five "hidden" jOOQ features.

1. Working With JDBC ResultSet

Even if you're otherwise not using jOOQ but JDBC (or Spring JdbcTemplate, etc.) directly, one of the things that's most annoying is working with ResultSet. A JDBC ResultSet models a database cursor, which is essentially a pointer to a collection on the server, which can be positioned anywhere, i.e. to the 50th record via ResultSet.absolute(50) (remember to start counting at 1).

The JDBC ResultSet is optimised for lazy data processing. This means that we don't have to materialize the entire data set produced by the server in the client. This is a great feature for large (and even large-ish) data sets, but in many cases, it's a pain. When we know we're fetching only ten rows and we know that we're going to need them in memory anyway, a List<Record> type would be much more convenient.

jOOQ's org.jooq.Result is such a List, and fortunately, you can easily import any JDBC ResultSet easily as follows by using DSLContext.fetch(ResultSet):

try (ResultSet rs = stmt.executeQuery()) {
    Result<Record> result = DSL.using(connection).fetch(rs);

With that in mind, you can now access all the nice jOOQ utilities, such as formatting a result, i.e. as TEXT (see the second feature for more details):

|  1|        1|1984       |
|  2|        1|Animal Farm|

Of course, the inverse is always possible, as well. Need a JDBC ResultSet from a jOOQ Result? Call Result.intoResultSet() and you can inject dummy results to any application that operates on JDBC ResultSet:

DSLContext ctx = DSL.using(connection);

// Get ready for Java 10 with var!
var result = ctx.newResult(FIRST_NAME, LAST_NAME);
result.add(ctx.newRecord(FIRST_NAME, LAST_NAME)
              .values("John", "Doe"));

// Pretend this is a real ResultSet
try (ResultSet rs = result.intoResultSet()) {
  while (rs.next())
    System.out.println(rs.getString(1) + " " + rs.getString(2));

2. Exporting a Result as XML, CSV, JSON, HTML, TEXT, or ASCII Chart

As we saw in the previous section, jOOQ Result types have nice formatting features. Instead of just text, you can also format as XML, CSV, JSON, HTML, and again, TEXT.

The format can usually be adapted to your needs.

For instance, this text format is possible as well:

 1         1 1984       
 2         1 Animal Farm

When formatting as CSV, you'll get:

2,1,Animal Farm

When formatting as JSON, you might get:

 {"ID":2,"AUTHOR_ID":1,"TITLE":"Animal Farm"}]

Or, depending on your specified formatting options, perhaps you'll prefer the more compact array of array style?

[[1,1,"1984"],[2,1,"Animal Farm"]]

Or XML, again, with various common formatting styles, among which:

    <TITLE>Animal Farm</TITLE>

HTML seems kind of obvious. You'll get:

1 1 1984
2 1 Animal Farm

Or, in code:

<tr><td>2</td><td>1</td><td>Animal Farm</td></tr>

As a bonus, you could even export the Result as an ASCII chart:

Image title

These features are obvious additions to ordinary jOOQ queries, but as I've shown in Section 1, you can get free exports from JDBC results as well!

3. Importing These Text Formats Again

After the previous section's export capabilities, it's natural to think about how to import such data again back into a more usable format. For instance, when you write integration tests, you might expect a database query to return a result like this:

-- --------- -----------
 1         1 1984       
 2         1 Animal Farm

Simply import the above textual representation of your result set into an actual jOOQ Result using Result.fetchFromTXT(String) and you can continue operating on a jOOQ Result (or as illustrated in Section 1, with a JDBC ResultSet!).

Most of the other export formats (except charts, of course) can be imported as well.

Now, don't you wish for a second that Java has multi-line strings (in case of which, this would be very nice looking):

Result<?> result = ctx.fetchFromTXT(
    "ID AUTHOR_ID TITLE      \n" +
    "-- --------- -----------\n" +
    " 1         1 1984       \n" +
    " 2         1 Animal Farm\n"
ResultSet rs = result.intoResultSet();

These types can now be injected anywhere where a service or DAO produces a jOOQ Result or a JDBC ResultSet. The most obvious application for this is mocking. The second most obvious application is testing. You can easily test that a service produces an expected result of the above form.

Let's talk about mocking...

4. Mocking JDBC

Sometimes, mocking is cool. With the above tools, it's only natural for jOOQ to provide a full-fledged, JDBC-based mocking SPI. I've written about this feature before and again here.

Essentially, you can implement a single FunctionalInterface called MockDataProvider. The simplest way to create one is by using the methods, i.e.:

MockDataProvider provider = Mock.of(ctx.fetchFromTXT(
    "ID AUTHOR_ID TITLE      \n" +
    "-- --------- -----------\n" +
    " 1         1 1984       \n" +
    " 2         1 Animal Farm\n"

This provider simply ignores all the input (queries, bind variables, etc.) and always returns the same simple result set. You can now plug this provider into a MockConnection and use it like any ordinary JDBC connection:

try (Connection c = new MockConnection(provider);
     PreparedStatement s = c.prepareStatement("SELECT foo");
     ResultSet rs = s.executeQuery()) {

    while (rs.next()) {
        System.out.println("ID        : " + rs.getInt(1));
        System.out.println("First name: " + rs.getString(2));
        System.out.println("Last name : " + rs.getString(3));

The output being (completely ignoring the SELECT foo statement):

ID        : 1
First name: 1
Last name : 1984
ID        : 2
First name: 1
Last name : Animal Farm

This client code doesn't even use jOOQ (although it could)! This means that you can use jOOQ as a JDBC mocking framework on any JDBC-based application, including a Hibernate-based one.

Of course, you don't always want to return the exact same result. This is why a MockDataProvider offers you an argument with all the query information in it:

try (Connection c = new MockConnection(ctx -> {
    if (ctx.sql().toLowerCase().startsWith("select")) {
        // ...
})) {
    // Do stuff with this connection

You can almost implement an entire JDBC driver with a single lambda expression. Read more here. Cool, eh?

Side note: Don't get me wrong: I don't think you should mock your entire database layer just because you can. My thoughts are available in this tweet storm:

Image title

Speaking of synthetic JDBC connections...

5. Parsing Connections

jOOQ 3.9 introduced a SQL parser whose main use case so far is to parse and reverse-engineer DDL scripts for the code generator.

Another feature that has not been talked about often yet (because still a bit experimental) is the parsing connection, available through DSLContext.parsingConnection(). Again, this is a JDBC Connection implementation that wraps a physical JDBC connection but runs all SQL queries through the jOOQ parser before generating them again.

What's the point?

Let's assume for a moment that we're using SQL Server, which supports the following SQL standard syntax:

SELECT * FROM (VALUES (1), (2), (3)) t(a)

The result is:


Now, let's assume that we are planning to migrate our application to Oracle. We have the following JDBC code that doesn't work on Oracle because Oracle doesn't support the above syntax:

try (Connection c = DriverManager.getConnection("...");
     Statement s = c.createStatement();
     ResultSet rs = s.executeQuery(
         "SELECT * FROM (VALUES (1), (2), (3)) t(a)")) {

    while (rs.next())

Now, we have three options (hint #1 sucks; #2 and #3 are cool):

  1. Tediously migrate all such manually written JDBC-based SQL to Oracle syntax and hope we don't have to migrate back again.
  2. Upgrade our JDBC-based application to use jOOQ instead (that's the best option, of course, but it also takes some time).
  3. Simply use the jOOQ parsing connection as shown below, and a lot of code will work right out of the box! (And then, of course, gradually migrate to jOOQ — see option #2.)
try (DSLContext ctx = DSL.using("...");
     Connection c = ctx.parsingConnection(); // Magic here
     Statement s = c.createStatement();
     ResultSet rs = s.executeQuery(
         "SELECT * FROM (VALUES (1), (2), (3)) t(a)")) {

    while (rs.next())

We haven't touched any of our JDBC-based client logic. We've only introduced a proxy JDBC connection that runs every statement through the jOOQ parser prior to re-generating the statement on the wrapped, physical JDBC connection.

What's really executed on Oracle is this emulation here:

select t.a from (
  (select null a from dual where 1 = 0) union all 
  (select * from (
    (select 1 from dual) union all 
    (select 2 from dual) union all 
    (select 3 from dual)
  ) t)
) t

Looks funky, eh? The rationale for this emulation is described here.

Every SQL feature that jOOQ can represent with its API and that it can emulate between databases will be supported! This includes far more trivial things, like parsing this query:

SELECT substring('abcdefg', 2, 4)

... and running this one on Oracle instead:

select substr('abcdefg', 2, 4) from dual

You're all thinking:

Want to Learn More About jOOQ?

There are many more such nice little things in the jOOQ API that help make you super productive. Some examples include:

Databases should be easy to deploy, easy to use, and easy to scale. If you agree, you should check out CockroachDB, a scalable SQL database built for businesses of every size. Check it out here. 

database ,jooq ,sql ,jdbc ,result sets ,mocking frameworks ,parsing

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}