写点什么

Spring 事务失效场景

作者:TaurusCode
  • 2023-04-01
    广东
  • 本文字数:2248 字

    阅读完需:约 7 分钟

Spring事务失效场景

同一个类的不同方法之间的调用(以方法 A 调用方法 B 为例)

如果方法 A 开启事务,则事务都可以生效

因为此时方法 A 走的是代理对象,所以事务会生效。并且方法 B 的默认传播机制是 REQUIRED,即方法 B 会加入到方法 A 的事务中,他们便处于同一个事务当中,双方都会进行回滚。

@Transactional(rollbackFor = Exception.class)@Overridepublic void methodA() {    // ...业务操作    methodB();    // 当A异常时,双方都会进行回滚。    int i = 10 / 0;}
// 即使B没有事务,也会进行回滚。@Transactional(rollbackFor = Exception.class)@Overridepublic void methodB() { // ...业务操作 // 同理,B异常时,A也会回滚,因为处于同一个事务中 // int i = 10 / 0;}
复制代码
如果方法 A 没有事务,则方法 B 无论怎样,事务都会失效

主要原因就是 Spring 的声明式事务是通过 AOP 代理实现的,所以必须由代理类调用对应的方法才能进行增强生效。由于方法 A 没有事务,则不会被代理,则其调用方法 B 时就是通过 this 自身来调用本类的方法,而不是代理类来调用。

@Overridepublic void methodA() {    // ...业务操作    methodB();    // 当A异常时,没有开启事务,不是走代理类,所以A和B都不会回滚。    int i = 10 / 0;}
@Transactional(rollbackFor = Exception.class)@Overridepublic void methodB() { // ...业务操作 // 同理,B异常时,因为本身不是代理类进行调用,所以也不会回滚。 // int i = 10 / 0;}
复制代码
如何解决上述的问题呢

方法一:既然是同类操作引起的,则将方法 A 和方法 B 进行抽离即可解决,这样就能让代理类去进行调用。

@Servicepublic class ServiceA {    @Resource    private ServiceB serviceB;
@Override public void methodA() { // ...业务操作 serviceB.methodB(); }}
@Servicepublic class ServiceB {
@Transactional(rollbackFor = Exception.class) @Override public void methodB() { // ...业务操作 // 当这里发生异常时,B会进行回滚。 int i = 10 / 0; }}
复制代码


方法二:不要让他进行 this 调用,通过代理类调用即可。

@Service public class ServiceA {    @Resource    private ServiceA serviceA;        @Override    public void methodA() {        // ...业务操作        serviceA.methodB();    }        @Transactional(rollbackFor = Exception.class)    @Override    public void methodB() {        // ...业务操作        // 当B发生异常时,因为通过代理类进行调用,所以自身会进行回滚。        int i = 10 / 0;    }}
复制代码

方法三:也是跟方法二类似,不过是直接从 IOC 容器获取 bean,这样也可以走代理。

@Service public class ServiceA {    @Resource    private ApplicationContext applicationContext;        @Override    public void methodA() {        // ...业务操作        ServiceA serviceA = applicationContext.getBean(ServiceA.class);    	  serviceA.methodB();    }        @Transactional(rollbackFor = Exception.class)    @Override    public void methodB() {        // ...业务操作        // 当B发生异常时,因为通过代理类进行调用,所以自身会进行回滚。        int i = 10 / 0;    }}
复制代码

不同类的不同方法之间的调用(以方法 A 调用方法 B 为例)

如果方法 A 开启事务,则事务都可以生效

因为方法 A 和方法 B 都会被代理,同时根据默认的传播机制,此时方法 A 和 B 处于同一个事务中,只要 A 或者 B 回滚,他们都会进行回滚。

@Servicepublic class ServiceA {    @Resource    private ServiceB serviceB;
@Transactional(rollbackFor = Exception.class) @Override public void methodA() { // ...业务操作 serviceB.methodB(); // 当这里发生异常时,A和B都会回滚。 int i = 10 / 0; }}
@Servicepublic class ServiceB {
// 即使B没有事务,也会进行回滚。 @Transactional(rollbackFor = Exception.class) @Override public void methodB() { // ...业务操作 // 同理,当只有B异常时,A和B都会回滚。因为处于同一个事务中。 //int i = 10 / 0; }}
复制代码
如果方法 A 没有事务,则方法 B 只有自身发生异常时才会回滚
@Servicepublic class ServiceA {    @Resource    private ServiceB serviceB;
@Override public void methodA() { // ...业务操作 serviceB.methodB(); // 当这里发生异常时,A和B都不会回滚。 int i = 10 / 0; }}
@Servicepublic class ServiceB {
@Transactional(rollbackFor = Exception.class) @Override public void methodB() { // ...业务操作 // 只有B自己发生异常时,才会自行回滚。 //int i = 10 / 0; }}
复制代码

补充说明

以上仅在单体架构中有效,实际的微服务场景,涉及到跨服务间调用的时候,就没办法生效,此时必须引入分布式事务来解决。另外其他可能导致 @Transaction 失效的场景还有:

  • 数据库引擎不支持事务。

  • 异常被捕获时,事务也会失效。

  • 方法是非 public 或者 final 的,事务会失效。

  • 传播机制设置成非事务运行时也会失效。

  • 未配置事务管理器或者没有开启事务管理。

  • 没有设置 rollbackFor=Exception.class,默认只会处理 RuntimeException。

发布于: 刚刚阅读数: 4
用户头像

TaurusCode

关注

非宁静无以致远 2020-09-17 加入

还未添加个人简介

评论

发布
暂无评论
Spring事务失效场景_Spring Boot_TaurusCode_InfoQ写作社区