一言蔽之
对Spring事务了解一直处于理论阶段,几个事务传播行为(propagation behaviors),几个隔离级别(isolation levels)看了又忘,忘了又看。近期的开发中遇到了几个有趣的例子,拿出来晒晒。
具体应用
1. 在catch块中,继续进行数据库操作
场景
以下是简化后的Kotlin代码
execute()
在事务内执行
doSomeThing()
可能会抛出RuntimeException
fail()
会对数据库有一些修改操作
@Transactional
override fun execute() {
try {
doSomeThing()
} catch (e: Exception) {
fail()
}
}
override fun fail() {
saveLogToDB()
}
问题
如果doSomeThing()
抛出RuntimeException
,fail()在执行数据库操作时,会抛出异常:
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
分析&思路
- 方法
saveLogToDB()
执行时事务已经被标记为rollback-only
-
saveLogToDB()
使用的事务是在execute()
方法执行之前创建的 - 在
doSomeThing()
执行的某个步骤中,因为触发异常,事务被标记为rollback-only
- 那么,为了
saveLogToDB()
能够保存数据到数据库,就不能够使用execute()
方法的事务了。
回忆以下有哪些事务的传播行为:
// 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
// @Transactional 注解默认采用这个方案
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
// 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
// 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
// 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
// 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
// 以非事务方式运行,如果当前存在事务,则抛出异常。
NEVER(TransactionDefinition.PROPAGATION_NEVER),
// 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于REQUIRED
// 并非所有的TransactionManager都能支持这个传播级别
NESTED(TransactionDefinition.PROPAGATION_NESTED);
作者:whthomas
链接:https://www.jianshu.com/p/e56b440e9eb6
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
显然,这里需要使用REQUIRES_NEW
传播行为。
解决
在fail()
方法上加上事务,并让propagation = REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRES_NEW)
override fun fail() {
saveLogToDB()
}
注意:同一个类中,方法间调用,事务AOP生效的前提条件是使用CGLIB方式实现AOP。
网友评论