Spring嵌套事务之同一个类中,一个方法调用另外一个有事务的方法

前段时间,朋友问了我一个问题,说有一个service类中,有一个A()方法和B()方法, A()方法没有添加事
务,B()方法添加了一个默认的事务,A()方法中调用B()方法,如果B()方法抛出异常,那么A()方法B()方法是否会回滚?

今天就回顾一下这个问题,看看到底如何?回顾这个问题之前,需要先了解下事务的传播行为,事务的传播
行为共7种如下:


PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务,最常见的选择。

PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。

PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。

PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起, 两个事务之间没有关系。

PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。

PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。


Spring默认事务传播行为:PROPAGATION_REQUIRED
@Service
public class TUserServiceImpl implements TUserService {

    @Autowired
    private TUserMapper tUserMapper;

    @Override
    public void addTuser() {
        TUser user = new TUser();
        user.setName("司总");
        user.setAge(23);
        tUserMapper.addTuser(user);
        updateTuser();
    }

    @Transactional
    @Override
    public void updateTuser() {
        TUser user = new TUser();
        user.setId(1);
        user.setName("张三他王大爷");
        user.setAge(66);
        tUserMapper.updateTuser(user);
        throw new RuntimeException();
    }
}


如果直接调用updateTuser()方法,因为updateTuser()方法开启了事务,抛出异常事务会进行回滚。

但是直接调用addTuser()方法,updateTuser()方法中抛出异常,addTuser()方法与updateTuser()
方法并没有回滚?why?是不是一脸黑人问号?
其实这个原因如下:
spring 在扫描bean的时候会扫描方法上是否包含@Transactional注解,如果包含,spring会为这个bean
动态地生成一个子类(即代理类,proxy),代理类是继承原来那个bean的。此时,当这个有注解的方法被调
用时,实际上是由代理类来调用的,代理类在调用之前就会启动transaction。然而,如果这个有注解的方法
是被同一个类中的其他方法调用的,那么该方法的调用并没有通过代理类,而是直接通过原来的那个bean,所
以就不会启动transaction,我们看到的现象就是@Transactional注解无效。


@Service
class A{
    @Transactinal
    method b(){...}
    
    method a(){    //标记1
        b();
    }
}
 
//Spring扫描注解后,创建了另外一个代理类,并为有注解的方法插入一个startTransaction()方法:
class proxy$A{
    A objectA = new A();
    method b(){    //标记2
        startTransaction();
        objectA.b();
    }
 
    method a(){    //标记3
        objectA.a();    //由于a()没有注解,所以不会启动transaction,而是直接调用A的实例的a()方法
    }
}

当我们调用A的bean的a()方法的时候,也是被proxy$A拦截,执行proxy$A.a()(标记3),然而,由以上代
码可知,这时候它调用的是objectA.a(),也就是由原来的bean来调用a()方法了,所以代码跑到了(标记
1)。由此可见,(标记2)并没有被执行到,所以startTransaction()方法也没有运行。

附带案例

@Transactional
@Override
public void addTuser() {
	TUser user = new TUser();
	user.setName("司总");
	user.setAge(23);
	tUserMapper.addTuser(user);
	updateTuser();
}

@Override
public void updateTuser() {
	TUser user = new TUser();
	user.setId(1);
	user.setName("张三他王大爷");
	user.setAge(66);
	tUserMapper.updateTuser(user);
	throw new RuntimeException();
}

addTuser()方法存在事务,updateTuser()方法不存在事务,这种方式调用addTuser()方法抛出异常会进行
回滚操作。原因想必大家都很清楚了,如果不清楚的我再解释解释:

addTuser()方法存在事务注解,调用addTuser()方法其实是通过代理类来调用的,会在代理类方法中开启
startTransaction() 然后再调用被代理类的addTuser()方法,addTuser()方法又调用了updateTuser()方
法所以它们是在同一个事务中的,抛出异常肯定会进行回滚。看下面这个代码图(借鉴了上面案例,重新修改了下)



@Service
class A{
    @Transactinal
    method addTuser(){
       updateTuser();
    }
    
    method updateTuser(){
        
    }
}
 
Spring扫描注解后,创建了另外一个代理类,并为有注解的方法插入一个startTransaction()方法:
class proxy$A{
    A objectA = new A();

    method addTuser(){   
        startTransaction();
        objectA.addTuser();
    }
 
    method updateTuser(){    
        objectA.updateTuser();   
    }
}

 

  • 3
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
一个事务方法调用一个事务方法时,根据Spring事务传播行为,具体的行为取决于所使用的传播行为类型。在Spring中,有七种事务传播行为,其中第七种(PROPAGATION_NESTED)是一个特殊变量。如果当前方法事务,则在嵌套事务内执行;如果当前方法没有事务,则与required操作类似。\[1\] 这意味着,当一个事务方法调用一个事务方法时,如果两个方法都使用了嵌套事务传播行为,那么内部方法将在一个独立的嵌套事务中执行。如果外部方法已经存在一个事务,内部方法将在该事务嵌套事务中执行。如果外部方法没有事务,内部方法将创建一个新的嵌套事务。\[2\] 需要注意的是,如果内部方法使用了其他的事务传播行为类型,如required或requires_new,那么内部方法事务行为将根据其自身的传播行为类型来确定,而不受外部方法的影响。\[2\] 总之,当一个事务方法调用一个事务方法时,具体的事务行为取决于所使用的传播行为类型,可以是嵌套事务,也可以是其他类型的事务。\[1\]\[2\] #### 引用[.reference_title] - *1* *2* *3* [Spring事务嵌套机制](https://blog.csdn.net/sun82111/article/details/95334611)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值