Does jOOQ play nicely with Kotlin coroutines?

Viewed 1108

Kotlin coroutines and 'suspending functions' make it easy for the programmer to await the result of I/O without stopping the thread (the thread is given other work to do until the I/O is complete).

jOOQ is a Java-first product for writing and executing SQL in a typesafe way, but does not itself make explicit use of Kotlin coroutines.

Can jOOQ be called from a Kotlin co-routine scope to get the nice-to-write and thread-is-productive-even-during-IO benefits?

suspend fun myQuery() {
  return dsl.select()
  // .etc()
  .fetch()  // <- could this be a 'suspend' call?
}
2 Answers

A: Yes.

BECAUSE org.jooq.ResultQuery<R extends Record> has a @NotNull CompletionStage<Result<R>> fetchAsync()` method which hooks into Java's (JDK 8+) machinery for 'futures'.

AND kotlinx-coroutines-jdk8 provides a bunch of extension methods for adapting between Kotlin suspending functions and JDK 8+ futures.

THEREFORE we can do:

import kotlinx.coroutines.future.await
...
suspend fun myQuery() {
  return dsl.select()
  //.etc()
  .fetchAsync()
  .await()    // <- IS  a suspending call !!!
}

It should be noted that there are a bunch of overloaded fetchX() methods on ResultQuery which provide lots of utility to synchronous calls, but they are not similarly overloaded for fetchAsync(). That's where the Kotlin programmer may wish to be familiar with the Java futures machinery: any sort of manipulation can be accomplished asynchronously using the thenApply {} method on CompletionStage<T>. For example, mapping the results:

suspend fun myQuery() {
  return dsl.select()
  //.etc()
  .fetchAsync()
  .thenApply { it.map(mapping(::Film)) }  // <- done as part of the 'suspend'
  .await()
}

although it should be fine to do it after the suspend:

suspend fun myQuery() {
  val records = dsl.select()
  //.etc()
  .fetchAsync()
  .await()

  return records.map(mapping(::Film))
}

JOOQ added R2dbc support, the ResultQuery is a ReactiveStreams Publisher.

Currently the simplest approach is using Reactor Kotlin extension to convert the Reactor APIs to Kotlin Coroutines APIs.

  Flux.from(
     ctx.select()
     ...//etc
     ...// do not call fetch
  ) 
  .asFlow()

Update: Jooq guys cancelled the PR of Kotlin Coroutines on DslContext, so now you have to use kotlinx-coroutines-ractor as above.

When switching to 3.17 or later, use fetchAwait directly. There is a series of xxxAwait added.

ctx.select()
     ...//etc
     .fetchAwait()
but I did not find direct support for Kotlin Coroutines,**Jooq 3.17 ships official Koltin Coroutines support in `jooq-kotlin` module**.
Related