数据库事务
本章将介绍如何在Kronos中使用数据库事务。
事务
使用 Kronos.transact 可以把多次数据库操作放入同一个事务。block 返回最后一个表达式,正常结束时提交,异常离开 block 时回滚。
Kronos.transact 事务入口
不传 wrapper 时,Kronos.transact 使用默认的 Kronos.dataSource。需要指定数据源时,把 KronosDataSourceWrapper 作为第一个参数传入。
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?需要返回类型化结果时,直接从 block 返回值。
return value
fun createUser(): String {
return transact {
User(name = "Ada").insert().execute()
"ok"
} as String
}异常时回滚
block 抛出异常时,JDBC wrapper 会回滚事务,并继续抛出该异常。
kotlin
Kronos.transact {
User(name = "Ada").insert().execute()
error("stop this transaction")
}嵌套事务复用连接
KronosJdbcWrapper 上的嵌套 Kronos.transact 会复用当前事务连接。最终提交或回滚由外层事务负责。
kotlin
Kronos.transact {
User(name = "Ada").insert().execute()
Kronos.transact {
User(id = 1).update().set { it.name = "Ada Lovelace" }.where().execute()
}
}隔离级别和超时
通过 TransactionIsolation 和 timeout 配置事务。timeout 的单位是秒。
kotlin
import com.kotlinorm.enums.TransactionIsolation
Kronos.transact(
isolation = TransactionIsolation.READ_COMMITTED,
timeout = 30
) {
User(name = "Ada").insert().execute()
}可用隔离级别包括 READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ 和 SERIALIZABLE。
Android/JVM SQLite 的事务行为统一见
📚Android SQLite。
保存点
TransactionScope 是事务 block 的 receiver。JDBC wrapper 可以把当前事务连接传给 TransactionScope,从而使用 savepoint、rollbackToSavepoint 和 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
}
}保存点需要
TransactionScope持有 JDBC connection。KronosJdbcWrapper会提供该 connection;自定义 wrapper 执行 block 时也应传入自己的事务 connection。