事务传播行为的失效(异常传递)

众所周知,事务的传播行为一共有7种,7种传播行为具体有什么特征在这里不再赘述,详情参考https://zhuanlan.zhihu.com/p/256263914,本文主要对对于传播行为失效进行探究,旨在对于事务的使用时对于细节的把控。

在这里引出第一个问题:

1.为什么我用 @Transactional(propagation = Propagation.REQUIRES_NEW)却得到了意想不到的结果?

首先,由于spring中的事务底层采用的是jdk或cglib的动态代理,因此,不能在一个类中一个方法直接调用另一个方法,可以采用动态代理上下文的方式,在一个类中调用另一个采用事务的方法,具体参考https://blog.csdn.net/xlgen157387/article/details/79026285,亦或者在当前类中注入当前类的对象,采用对象的方式调用。以下采用两个类的方式进行调用。

代码如下:

 @Service
 public class TestTransactional {
     @Autowired
     private CustomerMapper customerMapper;
 
     @Autowired
     private TestTransactional2 testTransactional2;
 
     @Transactional(propagation = Propagation.REQUIRED)
     public void methodA() {
         System.out.println("pre");
        Customer customer = new Customer();
         customer.setAddress("zzzzzzzz");
         customer.setNickname("zzzzzzzz");
         customer.setUsername("zzzzzzzz");
         customer.setPassword("zzzzzzzz");
         customerMapper.insert(customer);
         testTransactional2.methodB(65);
         System.out.println("post");
     }
 
 }
@Service
public class TestTransactional2 {
    @Autowired
    private CustomerMapper customerMapper;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB(Integer id) {
        customerMapper.deleteById(id);
        throw new RuntimeException();
    }
}

methodA中事务采用Propagation.REQUIRED,在methodA调用methodB,methodB的事务传播行为为Propagation.REQUIRES_NEW,按照Propagation.REQUIRES_NEW的特征,方法B会开启一个新的事务,方法A的事务会被挂起,如果方法B发生异常,方法B回滚,但是方法A不回滚,但是当方法B发生异常时,方法A也进行了回滚。如下:

在这里插入图片描述

可以发现65条数据没有删除,也没有添加新的数据,说明方法B和方法A都发生了回滚。这是为什么呢?

这事因为,方法B执行时抛出了异常,而方法A中并没有进行捕获,造成方法A中也出现了异常,造成A的事务也进行了回滚。如果在方法A中加入try{}catch{},那么问题就会迎刃而解。如下:

@Service
public class TestTransactional {
    @Autowired
    private CustomerMapper customerMapper;

    @Autowired
    private TestTransactional2 testTransactional2;

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        System.out.println("pre");
        Customer customer = new Customer();
        customer.setAddress("zzzzzzzz");
        customer.setNickname("zzzzzzzz");
        customer.setUsername("zzzzzzzz");
        customer.setPassword("zzzzzzzz");
        customerMapper.insert(customer);
        try {
            testTransactional2.methodB(65);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("post");
    }

}

在这里插入图片描述

可以看出,方法B进行了回滚,方法A没有进行回滚。

2.如果methodB的传播采用@Transactional(propagation = Propagation.REQUIRED)时,我可以对异常进行捕获吗?

但是当方法B中事务传播采用Propagation.REQUIRED(如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文中不存在事务,则新建事务执行)时,try{}catch{}会不会影响事务的执行呢?

@Service
public class TestTransactional2 {
@Autowired
private CustomerMapper customerMapper;

@Transactional(propagation = Propagation.REQUIRED)
public void methodB(Integer id) {
   customerMapper.deleteById(id);
   throw new RuntimeException();
   }
}

在这里插入图片描述

可以发现,try{}catch{}并没有影响Propagation.REQUIRED的特征,因为,方法B使用的事务是方法A中的事务,方法A与方法B公用一个事务,如果方法B发生了异常,即使方法A中进行了捕获,方法A也会发生回滚。所以,try{}catch{}也应用于propagation = Propagation.REQUIRED时。

3.如果方法B中事务传播采用Propagation.NOT_SUPPORTED时,我需要进行异常捕获吗?

Propagation.NOT_SUPPORTED,当前级别的特点就是上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。

@Service
public class TestTransactional {
    @Autowired
    private CustomerMapper customerMapper;

    @Autowired
    private TestTransactional2 testTransactional2;

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        System.out.println("pre");
        Customer customer = new Customer();
        customer.setAddress("zzzzzzzz");
        customer.setNickname("zzzzzzzz");
        customer.setUsername("zzzzzzzz");
        customer.setPassword("zzzzzzzz");
        customerMapper.insert(customer);
        testTransactional2.methodB(65);
        System.out.println("post");
    }

}
@Service
public class TestTransactional2 {
    @Autowired
    private CustomerMapper customerMapper;

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void methodB(Integer id) {
        customerMapper.deleteById(id);
        throw new RuntimeException();
    }
}

当方法A没有进行异常捕获时,得到了意想不到的结果:

在这里插入图片描述

方法B中删除成功,方法A进行了回滚,按照Propagation.NOT_SUPPORTED的特性,应该是方法B没有事务的执行,不会影响方法A中的事务,但是,结果显示,方法B抛出的异常影响了方法A中的事务,因此,方法A中一定要进行异常的捕获。

4.如果方法B中事务传播采用Propagation.NESTED时,我需要进行异常捕获吗?

Propagation.NESTED,嵌套是子事务套在父事务中执行,子事务是父事务的一部分,在进入子事务之前,父事务建立一个回滚点,叫save point,然后执行子事务,这个子事务的执行也算是父事务的一部分,然后子事务执行结束,父事务继续执行。

如果子事务回滚,会发生什么?

父事务会回滚到进入子事务前建立的save point,然后尝试其他的事务或者其他的业务逻辑,父事务之前的操作不会受到影响,更不会自动回滚。

如果父事务回滚,会发生什么?

父事务回滚,子事务也会跟着回滚!为什么呢,因为父事务结束之前,子事务是不会提交的,我们说子事务是父事务的一部分,正是这个道理。那么:

@Service
public class TestTransactional {
    @Autowired
    private CustomerMapper customerMapper;

    @Autowired
    private TestTransactional2 testTransactional2;

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        System.out.println("pre");
        Customer customer = new Customer();
        customer.setAddress("zzzzzzzz");
        customer.setNickname("zzzzzzzz");
        customer.setUsername("zzzzzzzz");
        customer.setPassword("zzzzzzzz");
        customerMapper.insert(customer);
        testTransactional2.methodB(65);
        System.out.println("post");
    }

}

@Service
public class TestTransactional2 {
    @Autowired
    private CustomerMapper customerMapper;

    @Transactional(propagation = Propagation.NESTED)
    public void methodB(Integer id) {
        customerMapper.deleteById(id);
        throw new RuntimeException();
    }
}

当方法A中没有对异常进行捕获时,结果如下:

在这里插入图片描述

可以发现,方法B和方法A都进行了回滚,按照NESTED特性,应该方法A不会进行回滚而是回到了开始执行方法B的保存的point,但是方法A进行了回滚。当方法A中对异常进行捕获时,结果就会与预期一致。

在这里插入图片描述

综上所述:无论方法B中传播行为是何种方式,方法A中都要对异常进行捕获,否则会发生出乎意料的结果,而且这种错误还很难发现,虽然这是运用事务时的细节问题,但是在一些项目中总是这些细节造成重大的损失,因此,在我们做项目时在逻辑严谨的同时也要对一些细节进行把握,增强代码的健壮性。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值