SQLDelight multiplatform runtime library providing Kotlin APIs for type-safe database operations with compile-time SQL verification
—
Comprehensive transaction management system supporting nested transactions, rollback semantics, lifecycle callbacks, and both synchronous and asynchronous execution patterns.
Main interface for synchronous transaction management.
/**
* A transaction-aware SqlDriver wrapper which can begin a Transaction on the current connection
*/
interface Transacter : TransacterBase {
/**
* Starts a Transaction and runs bodyWithReturn in that transaction
* @param noEnclosing If true, throws IllegalStateException if there's already an active transaction
* @param bodyWithReturn Lambda executed within the transaction context
* @return The result returned by bodyWithReturn
* @throws IllegalStateException if noEnclosing is true and there is already an active Transaction
*/
fun <R> transactionWithResult(
noEnclosing: Boolean = false,
bodyWithReturn: TransactionWithReturn<R>.() -> R
): R
/**
* Starts a Transaction and runs body in that transaction
* @param noEnclosing If true, throws IllegalStateException if there's already an active transaction
* @param body Lambda executed within the transaction context
* @throws IllegalStateException if noEnclosing is true and there is already an active Transaction
*/
fun transaction(
noEnclosing: Boolean = false,
body: TransactionWithoutReturn.() -> Unit
)
}Asynchronous version of Transacter supporting coroutine-based transaction management.
/**
* A transaction-aware SqlDriver wrapper which can begin a Transaction on the current connection
*/
interface SuspendingTransacter : TransacterBase {
/**
* Starts a Transaction and runs bodyWithReturn in that transaction
* @param noEnclosing If true, throws IllegalStateException if there's already an active transaction
* @param bodyWithReturn Suspending lambda executed within the transaction context
* @return The result returned by bodyWithReturn
*/
suspend fun <R> transactionWithResult(
noEnclosing: Boolean = false,
bodyWithReturn: suspend SuspendingTransactionWithReturn<R>.() -> R
): R
/**
* Starts a Transaction and runs body in that transaction
* @param noEnclosing If true, throws IllegalStateException if there's already an active transaction
* @param body Suspending lambda executed within the transaction context
*/
suspend fun transaction(
noEnclosing: Boolean = false,
body: suspend SuspendingTransactionWithoutReturn.() -> Unit
)
}Interfaces available within transaction lambda scopes providing rollback and callback capabilities.
/**
* Transaction context for operations that return values
*/
interface TransactionWithReturn<R> : TransactionCallbacks {
/**
* Rolls back this transaction and returns the specified value
* @param returnValue Value to return after rollback
*/
fun rollback(returnValue: R): Nothing
/**
* Begin an inner transaction
* @param body Lambda executed within the nested transaction context
* @return Result of the nested transaction
*/
fun <R> transaction(body: TransactionWithReturn<R>.() -> R): R
}
/**
* Transaction context for operations that don't return values
*/
interface TransactionWithoutReturn : TransactionCallbacks {
/**
* Rolls back this transaction
*/
fun rollback(): Nothing
/**
* Begin an inner transaction
* @param body Lambda executed within the nested transaction context
*/
fun transaction(body: TransactionWithoutReturn.() -> Unit)
}
/**
* Suspending transaction context for operations that return values
*/
interface SuspendingTransactionWithReturn<R> : TransactionCallbacks {
/**
* Rolls back this transaction and returns the specified value
* @param returnValue Value to return after rollback
*/
fun rollback(returnValue: R): Nothing
/**
* Begin an inner transaction
* @param body Suspending lambda executed within the nested transaction context
* @return Result of the nested transaction
*/
suspend fun <R> transaction(body: suspend SuspendingTransactionWithReturn<R>.() -> R): R
}
/**
* Suspending transaction context for operations that don't return values
*/
interface SuspendingTransactionWithoutReturn : TransactionCallbacks {
/**
* Rolls back this transaction
*/
fun rollback(): Nothing
/**
* Begin an inner transaction
* @param body Suspending lambda executed within the nested transaction context
*/
suspend fun transactionWithResult(body: suspend SuspendingTransactionWithoutReturn.() -> Unit)
}Interface for registering post-commit and post-rollback lifecycle hooks.
/**
* Interface for registering transaction lifecycle callbacks
*/
interface TransactionCallbacks {
/**
* Queues function to be run after this transaction successfully commits
* @param function Lambda to execute after successful commit
*/
fun afterCommit(function: () -> Unit)
/**
* Queues function to be run after this transaction rolls back
* @param function Lambda to execute after rollback
*/
fun afterRollback(function: () -> Unit)
}Base classes for implementing transaction management functionality.
/**
* Base implementation class for synchronous transaction management
*/
abstract class TransacterImpl(driver: SqlDriver) :
BaseTransacterImpl(driver),
Transacter
/**
* Base implementation class for asynchronous transaction management
*/
abstract class SuspendingTransacterImpl(driver: SqlDriver) :
BaseTransacterImpl(driver),
SuspendingTransacter
/**
* Common base functionality for both synchronous and asynchronous transacter implementations
*/
abstract class BaseTransacterImpl(protected val driver: SqlDriver) {
/**
* For internal use, notifies the listeners that their underlying result set has changed
* @param identifier Query identifier for listener notification
* @param tableProvider Lambda that provides table names to notify
*/
protected fun notifyQueries(identifier: Int, tableProvider: ((String) -> Unit) -> Unit)
/**
* For internal use, creates a string in the format (?, ?, ?) where there are count question marks
* @param count Number of parameters
* @return Formatted parameter string
*/
protected fun createArguments(count: Int): String
}Usage Examples:
import app.cash.sqldelight.Transacter
import app.cash.sqldelight.TransacterImpl
import app.cash.sqldelight.db.SqlDriver
// Implement a database class with transaction support
class UserDatabase(driver: SqlDriver) : TransacterImpl(driver) {
// Simple transaction without return value
fun deleteInactiveUsers() {
transaction {
// These operations happen atomically
execute("DELETE FROM user_sessions WHERE user_id IN (SELECT id FROM users WHERE active = 0)")
execute("DELETE FROM users WHERE active = 0")
// Register cleanup after successful commit
afterCommit {
println("Inactive users deleted successfully")
clearCache()
}
// Register error handling after rollback
afterRollback {
println("Failed to delete inactive users")
}
}
}
// Transaction with return value
fun transferMoney(fromUserId: Long, toUserId: Long, amount: Double): Boolean {
return transactionWithResult {
val fromBalance = getBalance(fromUserId)
val toBalance = getBalance(toUserId)
// Check sufficient funds
if (fromBalance < amount) {
rollback(false) // Rollback and return false
}
// Update balances
updateBalance(fromUserId, fromBalance - amount)
updateBalance(toUserId, toBalance + amount)
// Log the transaction
insertTransactionLog(fromUserId, toUserId, amount)
afterCommit {
sendNotification(fromUserId, "Money transferred successfully")
sendNotification(toUserId, "Money received")
}
true // Return success
}
}
// Nested transactions
fun createUserWithProfile(name: String, email: String, profileData: Map<String, String>): Long {
return transactionWithResult {
// Create user in outer transaction
val userId = insertUser(name, email)
// Create profile in nested transaction
transaction {
insertProfile(userId, profileData)
// If profile creation fails, both user and profile creation will be rolled back
// due to nested transaction semantics
}
userId
}
}
// Prevent nested transactions
fun criticalOperation() {
transaction(noEnclosing = true) {
// This will throw IllegalStateException if called within another transaction
performCriticalDatabaseUpdate()
}
}
}
// Asynchronous transaction management
class AsyncUserDatabase(driver: SqlDriver) : SuspendingTransacterImpl(driver) {
suspend fun processUserDataAsync(userId: Long): ProcessingResult {
return transactionWithResult {
val userData = fetchUserData(userId)
val processedData = processData(userData) // Suspending function
updateUserData(userId, processedData)
ProcessingResult.Success(processedData)
}
}
suspend fun batchUpdateUsersAsync(updates: List<UserUpdate>) {
transaction {
for (update in updates) {
updateUser(update.userId, update.data)
delay(10) // Suspending operation
}
afterCommit {
notifyBatchUpdateComplete()
}
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-app-cash-sqldelight--runtime-iossimulatorarm64