CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-app-cash-sqldelight--runtime-iossimulatorarm64

SQLDelight multiplatform runtime library providing Kotlin APIs for type-safe database operations with compile-time SQL verification

Pending
Overview
Eval results
Files

transaction-management.mddocs/

Transaction Management

Comprehensive transaction management system supporting nested transactions, rollback semantics, lifecycle callbacks, and both synchronous and asynchronous execution patterns.

Capabilities

Transacter Interface

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
    )
}

SuspendingTransacter Interface

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
    )
}

Transaction Context Interfaces

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)
}

Transaction Callbacks

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)
}

Transaction Implementation Classes

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()
            }
        }
    }
}

Thread Safety and Lifecycle

  • Transactions are confined to their creation thread and must not escape the transaction lambda scope
  • Nested transactions inherit the parent transaction's thread context
  • Rollback propagation from inner transactions affects the entire transaction tree
  • Callback execution happens synchronously after transaction completion
  • Error handling during callbacks can create composite exceptions that preserve both original and callback errors

Install with Tessl CLI

npx tessl i tessl/maven-app-cash-sqldelight--runtime-iossimulatorarm64

docs

column-adapters.md

database-driver.md

index.md

logging-debugging.md

query-execution.md

schema-management.md

transaction-management.md

tile.json