什么时候该用编程式事务?TransactionTemplate 和 @Transactional 有什么不同?事务超时是怎么实现的?事务范围放得太宽会怎样?这篇在 Spring Framework 7.0.9、HikariCP 和 MySQL 26.7.0 上逐项量出来。
execute 返回值 → 正常返回 返回值 表里剩 1 行 → 提交 回调里抛 RuntimeException → IllegalStateException 表里剩 0 行 → 回滚 回调里 status.setRollbackOnly() → 正常返回 表里剩 0 行 → 回滚 受检异常包成 RuntimeException → RuntimeException ← IOException 表里剩 0 行 → 回滚
execute 返回回调的值,抛运行时异常或 setRollbackOnly() 都回滚。
@Transactional 方法被同类方法 this 调用 → IllegalStateException 表里剩 1 行 → 提交 TransactionTemplate 在同类方法里使用 → IllegalStateException 表里剩 0 行 → 回滚
TransactionTemplate 直接调事务管理器,不依赖代理。
事务内:隔离级别=8(8 = SERIALIZABLE),只读=true setTimeout(1) 的事务里执行 select sleep(3):QueryTimeoutException ← MySQLTimeoutException: Statement cancelled due to timeout or client request(用时 1025 ms)
隔离级别、只读、超时都可以在模板上设置,超时靠语句超时实现。
@Transactional 包住整个方法(300 ms 的非数据库工作在事务里):8 个线程并发,连接池只有 2 个连接 → 全部完成用了 1225 ms
TransactionTemplate 只包数据库那一步(300 ms 的非数据库工作在事务外):8 个线程并发,连接池只有 2 个连接 → 全部完成用了 308 ms只把数据库操作放进事务,连接占用时间短,连接池吞吐高得多。