Database Transactions
In this chapter, we will introduce how to use database transactions in Kronos.
Transaction
Use Kronos.transact to run multiple database operations in one transaction. The block returns its last expression, commits when it completes, and rolls back when an exception leaves the block.
Kronos.transact transaction entry
Kronos.transact uses the default Kronos.dataSource when no wrapper is passed. Pass a KronosDataSourceWrapper as the first argument when the transaction should run on a specific data source.
kotlin
import com.kotlinorm.Kronos.transact
val result = transact {
User(name = "Ada").insert().execute()
User(id = 1).update().set { it.name = "Ada Lovelace" }.where().execute()
"committed"
}
val customResult = transact(wrapper) {
User(name = "Grace").insert().execute()
"committed on wrapper"
}signature
fun transact(
wrapper: KronosDataSourceWrapper? = null,
isolation: TransactionIsolation? = null,
timeout: Int? = null,
block: TransactionScope.() -> Any?
): Any?Return a typed value by returning the value from the block.
return value
fun createUser(): String {
return transact {
User(name = "Ada").insert().execute()
"ok"
} as String
}Roll back on exception
When the block throws, the JDBC wrapper rolls back the transaction and rethrows the exception.
kotlin
Kronos.transact {
User(name = "Ada").insert().execute()
error("stop this transaction")
}Nested transactions reuse the connection
Nested Kronos.transact blocks on KronosJdbcWrapper reuse the active transaction connection. The outer transaction owns the final commit or rollback.
kotlin
Kronos.transact {
User(name = "Ada").insert().execute()
Kronos.transact {
User(id = 1).update().set { it.name = "Ada Lovelace" }.where().execute()
}
}Isolation and timeout
Pass TransactionIsolation and timeout to configure the transaction. The timeout value is in seconds.
kotlin
import com.kotlinorm.enums.TransactionIsolation
Kronos.transact(
isolation = TransactionIsolation.READ_COMMITTED,
timeout = 30
) {
User(name = "Ada").insert().execute()
}Available isolation values are READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, and SERIALIZABLE.
Android/JVM SQLite transaction behavior is documented in
📚Android SQLite.
Savepoints
TransactionScope is the transaction block receiver. JDBC wrappers can expose the active connection to TransactionScope, which enables savepoint, rollbackToSavepoint, and releaseSavepoint.
kotlin
Kronos.transact {
User(name = "Ada").insert().execute()
val point = savepoint("before_status_update")
try {
User(id = 1).update().set { it.status = "ACTIVE" }.where().execute()
releaseSavepoint(point)
} catch (e: Exception) {
rollbackToSavepoint(point)
throw e
}
}Savepoints require a
TransactionScopebacked by a JDBC connection.KronosJdbcWrapperprovides that connection; custom wrappers should pass their transaction connection when they run the block.