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() = UnitTransactionService 会依次从每个 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 的实现更容易读懂,也避开“同类内部调用绕过代理”的传统代理问题;代价是成熟事务语义和异常处理策略需要继续实现。