Skip to content

SmartAccess 事务

SmartAccess 使用 Rain Hook 实现 @Transactional。它不是围绕 Bean 创建代理,而是在完整启动器的类加载阶段增强目标方法。

基本使用

kotlin
class TransferService(
    private val accountAccess: AccountAccess,
) {
    @Transactional
    fun transfer(from: Long, to: Long, amount: BigDecimal) {
        accountAccess.debit(from, amount)
        accountAccess.credit(to, amount)
    }
}

注解通过 @HookBy("smartaccess.db.transaction.TransactionHook") 关联 Hook。调用增强后的方法时:

text
TransactionHook.preRun
  → 为数据库创建 DBTransaction
  → 执行业务方法
成功:TransactionHook.postRun → commit
异常:TransactionHook.onError → rollback

指定多个数据库

kotlin
@Transactional(dbList = ["default", "audit"])
fun updateAndAudit() = Unit

TransactionService 会依次从每个 DBService 的 DBContext 创建事务,封装为 TransactionContext。提交和回滚同样逐个调用。

这不是 XA 或两阶段提交:若第一个数据库提交成功、第二个提交失败,SmartAccess 无法自动恢复第一个数据库。跨库强一致业务需要专门的分布式事务或补偿设计。

必须使用完整启动器

事务依赖 Hook Transformer,因此必须使用:

kotlin
FullStackApplicationLauncher.launch()

使用 BasicApplicationLauncher、直接 Application().start(),或在 Rain 类加载器建立前加载目标类,都可能导致注解存在但事务完全不执行。

当前源码限制

当前实现需要特别注意:

  • 同步事务使用 ThreadLocal 设计,但源码中的上下文写入/清理尚不完整,嵌套事务语义不能视为已实现。
  • postRun 在 commit 抛错时尝试 rollback,但没有重新抛出提交异常,调用方可能无法感知提交失败。
  • 异步事务入口存在于 TransactionService,当前 TransactionHook 走的是同步 begin/commit/rollback。
  • 多数据库只是顺序提交,不提供原子性。
  • 事务传播、只读、隔离级别、超时等 Spring 常见选项尚不存在。

因此当前版本适合单数据库、同步、边界明确的方法事务,并且必须用集成测试验证提交和回滚。关键资金或跨库业务不应在未补强实现前依赖现状。

事务边界建议

  • @Transactional 放在业务服务方法,不要散落在每个 Access 方法上。
  • 事务方法保持同步,除非所选模块明确支持并测试过协程上下文传播。
  • 不要在事务中执行不可回滚的外部操作;使用 outbox 或提交后事件。
  • 捕获异常后若希望回滚,应重新抛出;吞掉异常会让 Hook 走成功提交路径。
  • 锁查询和写操作必须处于同一事务方法调用链中。

与 Spring @Transactional 的区别

Spring 通常通过 AOP 代理和 PlatformTransactionManager 实现,拥有传播级别、回滚规则、隔离级别、只读和多种资源管理器。SmartAccess 用一个短小的 Hook:进入方法 begin,正常退出 commit,异常 rollback。

Rain 的实现更容易读懂,也避开“同类内部调用绕过代理”的传统代理问题;代价是成熟事务语义和异常处理策略需要继续实现。

基于 Apache License 2.0 发布