spring事务的传播方式

文章简介:

在写spring几个有趣的小甜点中的事务失效的时候被事务传播机制卡住了,觉得有必要学习整理一下事务的传播。
事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。
例如:methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。

REQUIRED(默认是这个):

这个方法必须运行在事务中,如果事务存在,则会运行在当前事务中,否则会新开一个事务。
什么意思?从字面意思根本看不懂,举例最实在了。
controller

	@RequestMapping("tran")
    public void hello(){
        Order order = new Order();
        order.setName("order1");
        userService.createOrder(order);
    }

service

	@Override
    @Transactional
    public void createOrder(Order order) {
        orderMapper.save(order);
        try {
            UserService proxy= (UserService)AopContext.currentProxy();
            proxy.sendMsg();
        }catch (Exception e){
            System.out.println("rollback");
        }
    }
    @Override
    @Transactional(propagation= Propagation.REQUIRED)
    public void sendMsg(){
        msgMapper.save(new Msg("你已经下单了"));
        //调用短信网关
        throw new RuntimeException();//运行时异常,编译时上层可以不捕捉
    }

createOrder外层事务加了@Transactional,调用sendMsg,sendMsg事务传播方式为REQUIRED,如果createOrder外层事务存在,则sendMsg会运行createOrder事务中,这也就是为什么在sendMsg里面抛运行时异常会.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only。解决方法在spring几个有趣的小甜点事务失效部分有详细解说。

上面是外层createOrder加了 @Transactional的情况,如果外层事务不加这个注解,那么内层事务sendMsg会自己开一个事务。此时的结果应该是订单表add进去了,短信表被回滚。

SUPPORTS:

这个方法不需要事务,但是如果存在事务,就在事务中运行。
createOrder没有事务,所以sendMsg也没有事务,最终的结果就是两张表都add进去了。

	@Override
    public void createOrder(Order order) {
        orderMapper.save(order);
        try {
            UserService proxy= (UserService)AopContext.currentProxy();
            proxy.sendMsg();
        }catch (Exception e){
            System.out.println("rollback");
        }
    }
    @Override
    @Transactional(propagation= Propagation.SUPPORTS)
    public void sendMsg(){
        msgMapper.save(new Msg("你已经下单了"));
        //调用短信网关
        throw new RuntimeException();//运行时异常,编译时上层可以不捕捉
    }

如果给createOrder加事务注解,那么两个方法就都有事务了,而且是在同一个事务中运行,不出意外又是UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

MANDATORY:

该方法必须在事务中运行,如果没有事务,抛异常IllegalTransactionStateException

	@Override
	@Transactional
    public void createOrder(Order order) {
        orderMapper.save(order);
        try {
            UserService proxy= (UserService)AopContext.currentProxy();
            proxy.sendMsg();
        }catch (Exception e){
            System.out.println("rollback");
        }
    }
    @Override
    @Transactional(propagation= Propagation.MANDATORY)
    public void sendMsg(){
        msgMapper.save(new Msg("你已经下单了"));
        //调用短信网关
        throw new RuntimeException();//运行时异常,编译时上层可以不捕捉
    }

两个方法都有事务了,而且是在同一个事务中运行,不出意外又是UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
如果把createOrder改成这样

 @Override
public void createOrder(Order order) {
        orderMapper.save(order);
        try {
            UserService proxy= (UserService)AopContext.currentProxy();
            proxy.sendMsg();
        }catch (Exception e){
            System.out.println("rollback");
            e.printStackTrace();//打印异常看看
        }
    }

会抛出IllegalTransactionStateException: No existing transaction found for transaction marked with propagation ‘mandatory’

REQUIRES_NEW:

该方法必须运行在新事务中

	@Override
    @Transactional
    public void createOrder(Order order) {
        orderMapper.save(order);
        try {
            UserService proxy= (UserService)AopContext.currentProxy();
            proxy.sendMsg();
        }catch (Exception e){
            System.out.println("rollback");
            e.printStackTrace();
        }
    }
    @Override
    @Transactional(propagation= Propagation.REQUIRES_NEW)
    public void sendMsg(){
        msgMapper.save(new Msg("你已经下单了"));
        //调用短信网关
        throw new RuntimeException();//运行时异常,编译时上层可以不捕捉
    }

很明显无论createOrder加不加事务,sendMsg都是有自己的事务的,所以执行结果是order表add进去,msg回滚。非常符合业务需求。

NOT_SUPPORTED:

该方法不应该运行在事务中,如果当前存在事务,事务会被挂起。

	@Override
    @Transactional
    public void createOrder(Order order) {
        orderMapper.save(order);
        try {
            UserService proxy= (UserService)AopContext.currentProxy();
            proxy.sendMsg();
        }catch (Exception e){
            System.out.println("rollback");
            e.printStackTrace();
        }
    }

    @Override
    @Transactional(propagation= Propagation.NOT_SUPPORTED)
    public void sendMsg(){
        msgMapper.save(new Msg("你已经下单了"));
        //调用短信网关
        throw new RuntimeException();//运行时异常,编译时上层可以不捕捉
    }

createOrder不加事务时没什么好说的,createOrder加事务时,这个事务会被挂起,所以相当于没加事务,所以两张表都加进去了。

NEVER

该方法不能运行在事务中,如果存在事务,抛异常,这个最简单,不说了。

NESTED

如果当前存在事务,则该方法会在嵌套事务中运行,该嵌套事务可以独立于当前事务进行提交回滚操作。
如果当前不存在事务,会新开一个事务

	@Override
    @Transactional
    public void createOrder(Order order) {
        orderMapper.save(order);
        try {
            UserService proxy= (UserService)AopContext.currentProxy();
            proxy.sendMsg();
        }catch (Exception e){
            System.out.println("rollback");
            e.printStackTrace();
        }
    }
    @Override
    @Transactional(propagation= Propagation.NESTED)
    public void sendMsg(){
        msgMapper.save(new Msg("你已经下单了"));
        //调用短信网关
        throw new RuntimeException();//运行时异常,编译时上层可以不捕捉
    }

此时内层事务sendMsg会独立于外层事务进行事务的提交回滚,所以最终结果时order表add进去,msg被回滚。非常符合业务需求。

增强理解

例1,NOT_SUPPORTED

	@Override
    @Transactional
    public void testTran()   {
        orderService.saveOrder();//保存订单
        productService.saveProduct();//保存商品
    }
		
	@Override
    public void saveOrder(){
        Order order = new Order();
        order.setOrderSn(2L);
        order.setProductId(1);
        order.setUserId(1);
        order.setBuyNum(1);
        orderMapper.save(order);
    }
    
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    @Override
    public void saveProduct(){
        Product product = new Product();
        product.setPrice(1);
        product.setName("2121");
        productMapper.insert(product);
        int i = 1/0;
    }

读者可以先判断下最终order和product表最终的数据状态?

执行流程如下:
在这里插入图片描述结论:订单表回滚,商品表不回滚,not_supported会挂起事务,在执行完后会恢复事务

例2,REQUIRED_NEW

	@Override
    @Transactional
    public void testTran()   {
        orderService.saveOrder();//保存订单
        productService.saveProduct();//保存商品
        int i = 1/0;
    }
		
	@Override
    public void saveOrder(){
        Order order = new Order();
        order.setOrderSn(2L);
        order.setProductId(1);
        order.setUserId(1);
        order.setBuyNum(1);
        orderMapper.save(order);
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public void saveProduct(){
        Product product = new Product();
        product.setPrice(1);
        product.setName("2121");
        productMapper.insert(product);
    }

结果:product不回滚,order回滚,因为saveProduct是一个新开事务,所以没有异常直接就提交了

例3,REQUIRED

REQUIRED作用是需要事务,没有就新开一个事务。

	@Override
    @Transactional
    public void testTran()   {
        orderService.saveOrder();//保存订单
        try{
        	productService.saveProduct();//保存商品
        }catch(Exception e){
        
        }
    }
		
	@Override
    public void saveOrder(){
        Order order = new Order();
        order.setOrderSn(2L);
        order.setProductId(1);
        order.setUserId(1);
        order.setBuyNum(1);
        orderMapper.save(order);
    }
    
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void saveProduct(){
        Product product = new Product();
        product.setPrice(1);
        product.setName("2121");
        productMapper.insert(product);
        int i = 1/0;
    }

结果是两个表都回滚,这个就有点奇怪,因为自始至终就只有一个testTran开启的事务,saveProduct也只是延用了testTran开启的事务,就算saveProduct抛了异常,但是testTran捕捉了,其实不应该回滚的吧?但是源代码不是这么处理的。
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值