ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SQLDelight 事务编程全指南:transaction、rollback 与提交回调(JVM/MySQL 场景)

SQLDelight 事务编程全指南:transaction、rollback 与提交回调(JVM/MySQL 场景) 后端ORM【免费下载链接】sqldelightSQLDelight - Generates typesafe Kotlin APIs from SQL项目地址https://gitcode.com/gh_mirrors/sq/sqldelight点击查看免费下载本文以 docs/jvm_mysql/transactions.md其正文通过{% include common/transactions.md %}复用 docs/common/transactions.md为骨架结合 SQLDelight 运行时与 JDBC 驱动的源码系统讲解在 JVM MySQL 项目中使用 SQLDelight 事务的完整姿势。读完你将掌握transaction/transactionWithResult两种事务入口、手动与自动回滚、afterCommit/afterRollback回调以及嵌套事务、noEnclosing、线程约束等底层行为并能据此写出可正确提交、可安全回滚的批量写入代码。事务 APItransaction与transactionWithResultSQLDelight 为每个生成的 Queries 类如示例中的playerQueries都注入了事务能力——生成的类实现Transacter接口因此你可以直接在查询对象上开启事务。若希望在一条事务里执行多条 SQL 语句使用transaction函数val players listOfPlayer() database.playerQueries.transaction { players.forEach { player - database.playerQueries.insert( player_number player.number, full_name player.fullName ) } }上述代码中的整批insert要么全部成功提交要么在出现异常时整体回滚不会出现插入一半的中间状态。transactionWithResult从事务中返回值如果事务执行完毕后需要把计算结果带回调用方使用transactionWithResult。它的 lambda 返回值即为整个事务的返回值val players: ListPlayer database.playerQueries.transactionWithResult { database.playerQueries.selectAll().executeAsList() }从源码看这两个入口在 Transacter.kt 中定义fun R transactionWithResult( noEnclosing: Boolean false, bodyWithReturn: TransactionWithReturnR.() - R, ): R fun transaction( noEnclosing: Boolean false, body: TransactionWithoutReturn.() - Unit, )注意两点两个函数都带noEnclosing: Boolean false参数下文详述事务 lambda 的接收者类型不同无返回值事务是TransactionWithoutReturn带返回值事务是TransactionWithReturnR它们决定了你能在 lambda 内调用rollback()还是rollback(returnValue)。嵌套事务与noEnclosing在事务 lambda 内部可以再调用transaction { ... }开启内层事务嵌套事务。其底层语义并非数据库的嵌套事务而是把内层语句并入外层事务一起提交。实现细节见 Transacter.kt 的TransactionWrapper内层transaction实际是委托给transacter.transaction(false, body)即默认允许嵌套。noEnclosing true则用于显式要求当前必须没有正在运行的事务否则抛出IllegalStateException。该约束在TransacterImpl.transactionWithWrapper中通过check(enclosing null || !noEnclosing) { Already in a transaction }强制校验见 Transacter.kt。典型用途某个公共方法需要独立事务不允许被调用方已开启的事务包裹。回滚Rollback异常自动回滚事务体内任何位置抛出异常整个事务都会自动回滚。其机制是TransacterImpl.transactionWithWrapper中try/catch/finally的组合Transacter.ktlambda 抛出异常时不会把transaction.successful置为truefinally中endTransaction()因此走回滚分支postTransactionCleanup随后执行注册的回滚钩子并把异常重新抛出给调用方。手动rollback()无返回值事务你可以在事务任意位置主动回滚。无返回值事务调用rollback()即可database.playerQueries.transaction { players.forEach { player - if (player.number 0) rollback() database.playerQueries.insert( player_number player.number, full_name player.fullName ) } }示例语义遇到number 0的非法数据时中止后续插入并丢弃已做的修改。手动rollback(value)带返回值事务若在transactionWithResult中手动回滚必须同时指定事务要返回的值val numberInserted: Int database.playerQueries.transactionWithResult { players.forEach { player - if (player.number 0) rollback(0) database.playerQueries.insert( player_number player.number, full_name player.fullName ) } players.size }这里rollback(0)表示回滚并把0作为numberInserted的返回值若一切顺利则返回players.size。底层实现上rollback()与rollback(value)都是抛出内部私有异常RollbackException可携带返回值见 Transacter.kt。该异常被外层catch (e: Throwable)捕获后事务标记为失败而postTransactionCleanup会识别出RollbackException并把其携带的值作为返回值抛出路径上的结果Transacter.kt。因此rollback(value)的返回值类型R与transactionWithResult的泛型R必须一致编译器会在调用点强制这一约束。提交与回滚回调Callbacks可以在事务内注册回调让它们在事务提交或回滚完成之后执行常用于日志、指标上报或清理逻辑database.playerQueries.transaction { afterRollback { log(No players were inserted.) } afterCommit { log(${players.size} players were inserted.) } players.forEach { player - database.playerQueries.insert( player_number player.number, full_name player.fullName ) } }afterCommit { ... }事务成功提交后执行afterRollback { ... }事务回滚后执行包括异常回滚与手动回滚。回调的执行时机与合并语义源码级回调不是立即执行的而是被登记进事务的钩子列表。在 Transacter.kt 中Transaction.afterCommit/afterRollback分别把函数追加到postCommitHooks/postRollbackHooks。真正的触发点在BaseTransacterImpl.postTransactionCleanupTransacter.kt提交成功依次执行postCommitHooks并清空钩子列表同时若事务期间有表被修改pendingTables非空会调用driver.notifyListeners(...)通知查询监听器刷新结果集回滚失败依次执行postRollbackHooks若回滚钩子自身抛异常且事务体原本也有异常两者会被合并为一个复合异常抛出避免原始异常信息丢失嵌套事务内层事务的钩子不会立刻执行而是被合并到外层事务的postCommitHooks/postRollbackHooks最终由最外层事务统一触发——这与内层并入外层的提交语义一致。这些行为在 TransacterTest.kt 中有完整测试佐证afterCommitRunsAfterEnclosingTransactionCommits外层提交后内层注册的afterCommit也一并执行counter 累计为 2afterCommitDoesNotRunInNestedTransactionWhenEnclosingRollsBack外层回滚后内层afterCommit不执行afterCommitDoesNotRunAfterTransactionRollbacks手动rollback()后afterCommit不执行anExceptionThrownInPostRollbackFunctionIsCombinedWithTheExceptionInTheMainBody验证复合异常包含事务体与回滚钩子的双重信息。底层实现从 Transacter 到 JDBC 驱动要理解 JVM/MySQL 场景下事务的真实行为需要沿调用链看两层实现。第一层TransacterImpl的transactionWithWrapper所有事务最终汇聚到TransacterImpl.transactionWithWrapperTransacter.ktval transaction driver.newTransaction().value val enclosing transaction.enclosingTransaction() check(enclosing null || !noEnclosing) { Already in a transaction } var thrownException: Throwable? null var returnValue: R? null try { transaction.transacter this returnValue TransactionWrapperR(transaction).wrapperBody() transaction.successful true } catch (e: Throwable) { thrownException e } finally { transaction.endTransaction() return postTransactionCleanup(transaction, enclosing, thrownException, returnValue) }它向底层SqlDriver请求一个新事务driver.newTransaction()执行业务 lambdalambda 正常结束则标记successful true最后无论成功失败都在finally中结束事务并做钩子清理。SqlDriver接口对事务的抽象在 SqlDriver.ktnewTransaction()开启新事务currentTransaction()返回当前线程正在运行的事务。第二层JDBC 驱动的 commit / rollback 映射在 JVM 上包括 JVM MySQL 场景使用的驱动是 JdbcDriver.kt。它把 SQLDelight 的事务抽象映射到java.sql.ConnectionConnection.beginTransaction()校验autoCommit为 true 后将其置为false等效于开启事务JdbcDriver.ktConnection.endTransaction()提交commit()、恢复autoCommit true并关闭连接JdbcDriver.ktConnection.rollbackTransaction()回滚rollback()、恢复autoCommit并关闭连接JdbcDriver.kt。newTransaction()时若已存在外层事务enclosing ! null则复用外层连接、不重复beginTransaction这正是嵌套事务并入外层的 JDBC 侧实现JdbcDriver.kt。事务对象存放在ThreadLocal中JdbcDriver.kt因此事务天然具有线程亲和性。在 MySQL 场景还需注意JDBC 层的commit()/rollback()是否真正生效取决于所连接的 MySQL 存储引擎是否支持事务例如 InnoDB 支持MyISAM 不支持SQLDelight 本身不做额外保证这一点由你的数据库配置决定。线程约束事务对象不得逃逸 lambdaTransacter.Transaction在创建时记录ownerThreadId所有操作rollback、afterCommit、afterRollback、endTransaction都会调用checkThreadConfinement()校验调用线程与创建线程一致Transacter.kt。因此事务对象只能在事务 lambda 作用域内使用不能保存到外部字段后跨线程调用——否则会抛出带提示的IllegalStateException。挂起事务协程场景的补充如果项目使用协程coroutines 扩展可参考 extensions/coroutines-extensions 相关模块SQLDelight 运行时还提供SuspendingTransacter及配套的挂起版transaction/transactionWithResult定义在 Transacter.kt。其语义与非挂起版一致仅 lambda 变为suspend并且支持通过SuspendingTransacter.TransactionDispatcher由驱动实现自定义事务执行的协程上下文。用法上只需把事务体改为挂起函数即可本文的transaction、rollback、回调示例均适用。总结SQLDelight 的事务模型可以用一张调用链概括生成的 Queries 类实现Transacter→transaction/transactionWithResultTransacterImpl包装→SqlDriver.newTransaction()→ JDBC 驱动的commit()/rollback()。实践要点批量写入用transaction需要结果用transactionWithResult异常自动回滚手动回滚时无返回值事务用rollback()带返回值事务必须用rollback(value)用afterCommit/afterRollback做提交后/回滚后的收尾嵌套事务的回调由最外层统一触发需要独立事务时传noEnclosing true否则默认允许嵌套并合并提交事务对象有线程约束切勿跨线程使用。相关源码与测试路径docs/common/transactions.md、runtime/src/commonMain/kotlin/app/cash/sqldelight/Transacter.kt、runtime/src/commonMain/kotlin/app/cash/sqldelight/db/SqlDriver.kt、drivers/jdbc-driver/src/main/kotlin/app/cash/sqldelight/driver/jdbc/JdbcDriver.kt、drivers/driver-test/src/commonMain/kotlin/com/squareup/sqldelight/driver/test/TransacterTest.kt。赞分享后端ORM【免费下载链接】sqldelightSQLDelight - Generates typesafe Kotlin APIs from SQL项目地址https://gitcode.com/gh_mirrors/sq/sqldelight点击查看免费下载相关推荐SQLDelight JVM PostgreSQL 事务指南transaction / transactionWithResult / Rollback 与回调详解SQLDelight JVM PostgreSQL 事务指南transaction / transactionWithResult / Rollback后端ORMMySQL 事务实战用 START TRANSACTION、COMMIT 与 ROLLBACK 安全执行生产环境 UPDATEMySQL 事务实战用 START TRANSACTION、COMMIT 与 ROLLBACK 安全执行生产环境 UPDATE 导读 面对生产环境 MySQL文档教程知识库如何快速配置SQLDelight JVM环境MySQL、PostgreSQL和SQLite驱动完整指南如何快速配置SQLDelight JVM环境MySQL、PostgreSQL和SQLite驱动完整指南 SQLDelight 是一款能从 SQL 生成类型安全后端ORM上一篇FluidNC 链接器脚本深度解析用 vtable_in_dram 将虚函数表搬入 RAM根治 ESP32 ISR 崩溃下一篇原神成就数据导出完整指南备份、迁移、分析一次搞定创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表