Spring-dataSource事务案例分析-使用事务嵌套时,一个我们容易忽略的地方

发布于:2024-04-18 ⋅ 阅读:(24) ⋅ 点赞:(0)

场景如下:

  • A_Bean 中的方法a()中调用B_Bean的b();
  • 方法都开启了事务,使用的默认的事务传递机制(即:属于同一事务);

如下两种场景会存在较大的差异:

  1. 在b()方法中出现了异常,在b()中进行捕获并处理且没有抛出新的异常,事务最终会进行提交;
  2. 在b()方法中出现了异常,在a()中进行捕获并处理且没有抛出新的异常,那么事务最终会如何呢?—— 先给结论:事务回滚

这个小差异平时编程的过程比较难留意到,会简单认为:当某个方法上面开启了事务,并且当前方法没有抛出任何异常,最终方法上面的事务一定会提交。其实这里是存在认知错误的

code如下:

@SpringBootTest
public class TransactionTest {

    @Autowired
    private A a;

    @Test
    void testTransaction() throws Exception {
        a.a();
    }

}

@Service
public class A {

    @Autowired
    private B b;

    @Transactional
    public void a() {
        try {
            b.b();
        } catch (Exception e) {
            log.error("b执行异常,进行捕获且不抛出异常");
        }
        // for some db operation
    }

}

@Service
public class B {

    @Transactional
    public void b() {
        // for some db operation
        throw new RuntimeException("b-error");
    }
}}

为何错误?

  • 当 a() 方法调用 b() 方法时,如果两个方法都开启了事务且采用默认的事务传播行为(即事务嵌套),b() 方法的事务会加入到 a() 方法的事务中,成为同一个事务。
  • 那么b()中出现异常,b中没有捕获而在a中捕获,实则已经触发b()的事务处理异常的逻辑。
  • 而a、b方法执行又同属一个事务,在b异常被事务管理器感知到后就会将当前事务标记为rollback,那么即使a最终没有感知到异常,最终a正常执行完毕后,a上面的事务管理逻辑也不会将事务进行提交,而是采取回滚的决定!

源码分析

当一个事务方法执行出现异常时(比如b()执行抛出异常时):

org.springframework.transaction.interceptor.TransactionAspectSupport#completeTransactionAfterThrowing

org.springframework.transaction.support.AbstractPlatformTransactionManager#rollback

org.springframework.transaction.support.AbstractPlatformTransactionManager#processRollback

org.springframework.jdbc.datasource.DataSourceTransactionManager#doSetRollbackOnly

org.springframework.jdbc.datasource.DataSourceTransactionManager.DataSourceTransactionObject#setRollbackOnly

org.springframework.transaction.support.ResourceHolderSupport#setRollbackOnly

当a()正常执行完毕,准备提交事务时:

org.springframework.transaction.interceptor.TransactionInterceptor#invoke

org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction

org.springframework.transaction.interceptor.TransactionAspectSupport#commitTransactionAfterReturning

org.springframework.transaction.support.AbstractPlatformTransactionManager#commit

org.springframework.transaction.support.SmartTransactionObject#isRollbackOnly

org.springframework.jdbc.datasource.DataSourceTransactionManager.DataSourceTransactionObject#isRollbackOnly

a执行完毕会进行判断:

最终还是会进行事务的回滚!

在 Spring 中,当事务被标记为 rollback-only 时,它会通知事务管理器,表示事务应该回滚。即使没有抛出新的异常,一旦事务被标记为 rollback-only,最终事务仍然会回滚。

因此,在你的情况下,如果 b() 方法中出现异常,在 a() 方法中进行了捕获并处理,但是事务在 b() 方法中被标记为 rollback-only,最终会导致 a() 方法的事务回滚。

如果有这种需要该如何处理?

如题:a事务嵌套b事务,不管b事务是否执行成功,只有a中最终没有抛出异常那么就需要将a提交,做到a事务不受内部嵌套事务的影响,该如何?

修改b事务的传播配置:


网站公告

今日签到

点亮在社区的每一天
去签到