A library for interacting with blocking JDBC drivers using Kotlin Coroutines.
Use of this library allows you to offload blocking JDBC calls to a dedicated
CoroutineDispatcher (e.g. Dispatchers.IO), thus suspending your coroutine
and freeing your thread for other work while waiting.
repositories {
mavenCentral()
}
dependencies {
implementation("com.michael-bull.kotlin-coroutines-jdbc:kotlin-coroutines-jdbc:1.1.0")
}The primary higher-order function exposed by the library is the transaction function.
suspend inline fun <T> transaction(crossinline block: suspend () -> T): TCalling this function with a specific suspending block will run the block in the context of a
CoroutineTransaction.
Transactions cannot be nested. Calling transaction (or any of its variants) within an existing transaction will
throw an IllegalStateException.
Starting a transaction will add a CoroutineTransaction to the current
CoroutineContext. The transaction will either commit on success or
rollback if an exception is thrown.
A transaction will establish a new Connection if an open one does not already exist in the active
CoroutineContext. If the transaction does establish a new Connection, it will
attempt to close it upon completion.
An active Connection is accessible via the currentConnection function, which can
be used to prepare statements.
The library provides several transaction variants for different use cases:
transaction- Standard read-write transactionreadOnlyTransaction- Sets the connection to read-only mode before starting the transactionisolatedTransaction- Sets a specific transaction isolation level before starting the transactionisolatedReadOnlyTransaction- Combines read-only mode with a specific isolation level
// Standard transaction
transaction {
// read-write operations
}
// Read-only transaction (useful for queries that don't modify data)
readOnlyTransaction {
// read-only operations
}
// Transaction with specific isolation level
isolatedTransaction(Connection.TRANSACTION_SERIALIZABLE) {
// operations with serializable isolation
}
// Read-only transaction with specific isolation level
isolatedReadOnlyTransaction(Connection.TRANSACTION_READ_COMMITTED) {
// read-only operations with read committed isolation
}import com.github.michaelbull.jdbc.context.CoroutineDataSource
import com.github.michaelbull.jdbc.currentConnection
import com.github.michaelbull.jdbc.transaction
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import javax.sql.DataSource
class Example(dataSource: DataSource) {
private val scope = CoroutineScope(Dispatchers.IO + CoroutineDataSource(dataSource))
private val customers = CustomerRepository()
fun query() {
scope.launchTransaction()
}
private fun CoroutineScope.launchTransaction() = launch {
val customers = addThenFindAllCustomers()
customers.forEach(::println)
}
private suspend fun addThenFindAllCustomers(): List<String> = transaction {
customers.add("John Doe")
customers.findAll()
}
}
class CustomerRepository {
suspend fun add(name: String) {
currentConnection().prepareStatement("INSERT INTO customers VALUES (?)").use { stmt ->
stmt.setString(1, name)
stmt.executeUpdate()
}
}
suspend fun findAll() = buildList<String> {
urrentConnection().prepareStatement("SELECT name FROM customers").use { stmt ->
stmt.executeQuery().use { rs ->
while (rs.next()) {
add(rs.getString("name"))
}
}
}
}
}- andrewoma/coroutine-jdbc
- Coroutine Context and Scope - Roman Elizarov
- Blocking threads, suspending coroutines - Roman Elizarov
Bug reports and pull requests are welcome on GitHub.
This project is available under the terms of the ISC license. See the LICENSE file for the copyright
information and licensing terms.