Spring 事务传播行为和隔离级别的应用

一言蔽之

对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

分析&思路

  1. 方法saveLogToDB()执行时事务已经被标记为rollback-only
  2. saveLogToDB()使用的事务是在execute()方法执行之前创建的
  3. doSomeThing()执行的某个步骤中,因为触发异常,事务被标记为rollback-only
  4. 那么,为了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。

你可能感兴趣的:(Spring 事务传播行为和隔离级别的应用)