当前位置:   article > 正文

Spring中七种事务传播行为_事务默认的传播行为

事务默认的传播行为
  1. 前言
  2. 在开发中,相信大家都使用过Spring的事务管理功能。那么,你是否有了解过,Spring的事务传播行为呢?
  3. Spring中,有7种类型的事务传播行为。事务传播行为是Spring框架提供的一种事务管理方式,它不是数据库提供的。不知道大家是否听说过“不要在service事务方法中嵌套事务方法,这样会提交多个事务”的说法,其实这是不准确的。了解了事务传播行为之后,相信你就会明白!
  4. Spring中七种事务传播行为
  5. 事务的传播行为,默认值为 Propagation.REQUIRED。可以手动指定其他的事务传播行为,如下:
  6. - Propagation.REQUIRED 如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。
  7. - Propagation.SUPPORTS 如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。
  8. - Propagation.MANDATORY 如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
  9. - Propagation.REQUIRES_NEW 重新创建一个新的事务,如果当前存在事务,延缓当前的事务。
  10. - Propagation.NOT_SUPPORTED 以非事务的方式运行,如果当前存在事务,暂停当前的事务。
  11. - Propagation.NEVER 以非事务的方式运行,如果当前存在事务,则抛出异常。
  12. - Propagation.NESTED 如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务。
准备工作
  1. CREATE TABLE `t_user` (
  2. `id` int(11) NOT NULL,
  3. `password` varchar(255) DEFAULT NULL,
  4. `username` varchar(255) DEFAULT NULL,
  5. PRIMARY KEY (`id`)
  6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

一、REQUIRED(默认的事务传播行为)

默认的事务传播行为是Propagation.REQUIRED,也就是说:如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。

  1. 下面,我们就验证下前面说的“不要循环嵌套事务方法”的问题:
  2. 现在有两个Service,如下:
  3. #### UserService.java
  1. @Service
  2. public class UserService {
  3. @Autowired
  4. private UserRepo userRepo;
  5. @Transactional(propagation = Propagation.REQUIRED)
  6. public void insert() {
  7. UserEntity user = new UserEntity();
  8. user.setUsername("happyjava");
  9. user.setPassword("123456");
  10. userRepo.save(user);
  11. }
  12. }
  1. 这里很简单,就一个insert插入用户的方法。
  2. #### UserService2.java
  1. @Service
  2. public class UserService2 {
  3. @Autowired
  4. private UserService userService;
  5. @Transactional
  6. public void inserBatch() {
  7. for (int i = 0; i < 10; i++) {
  8. if (i == 9) {
  9. throw new RuntimeException();
  10. }
  11. userService.insert();
  12. }
  13. }
  14. }
注入UserService,循环十次调用参数方法。并且第十次抛出异常。调用inserBatch方法,查看结果:
  1. @Test
  2. public void insertBatchTest() {
  3. userService2.inserBatch();
  4. }
结果如下:数据库中没有记录:

这也证明了“如果当前存在事务,则加入该事务”的概念。如果以后还碰到有人说不要循环嵌套事务的话,可以叫他回去好好看看Spring的事务传播行为。

二、SUPPORTS

如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。也就是说,该模式是否支持事务,看调用它的方法是否有事务支持。测试代码如下:
#### UserService
  1. @Transactional(propagation = Propagation.SUPPORTS)
  2. public void insert() {
  3. UserEntity user = new UserEntity();
  4. user.setUsername("happyjava");
  5. user.setPassword("123456");
  6. userRepo.save(user);
  7. throw new RuntimeException();
  8. }
#### UserService2
  1. public void insertWithoutTx() {
  2. userService.insert();
  3. }
 

调用的方法没有开启事务,运行结果:

运行报错了,但是数据却没有回滚掉。说明了insert方法是没有在事务中运行的。

三、MANDATORY

  1. 如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。mandatory中文是强制性的意思,表明了被修饰的方法,一定要在事务中去调用,否则会抛出异常。
  2. #### UserService.java
  1. @Transactional(propagation = Propagation.MANDATORY)
  2. public void insert() {
  3. UserEntity user = new UserEntity();
  4. user.setUsername("happyjava");
  5. user.setPassword("123456");
  6. userRepo.save(user);
  7. }
UserService2.java
  1. public void insertWithoutTx() {
  2. userService.insert();
  3. }
调用:
  1. @Test
  2. public void insertWithoutTxTest() {
  3. userService2.insertWithoutTx();
  4. }
 

运行结果:

抛出了异常,提示没有存在的事务。

四、REQUIRES_NEW

  1. 这个理解起来可能会比较绕,官方的解释是这样子的:
  2. Create a new transaction, and suspend the current transaction if one exists.
  3. 大意就是:重新创建一个新的事务,如果当前存在事务,延缓当前的事务。这个延缓,或者说挂起,可能理解起来比较难,下面通过例子来分析:
  4. #### UserService.java
  1. @Transactional(propagation = Propagation.REQUIRES_NEW)
  2. public void insert() {
  3. UserEntity user = new UserEntity();
  4. user.setUsername("happyjava");
  5. user.setPassword("123456");
  6. userRepo.save(user);
  7. }
  1. 这个insert方法的传播行为改为REQUIRES_NEW。
  2. #### UserService2.java
  1. @Transactional
  2. public void inserBatch() {
  3. UserEntity user = new UserEntity();
  4. user.setUsername("初次调用");
  5. user.setPassword("123456");
  6. userRepo.save(user);
  7. for (int i = 0; i < 10; i++) {
  8. if (i == 9) {
  9. throw new RuntimeException();
  10. }
  11. userService.insert();
  12. }
  13. }
  1. inserBatch拥有事务,然后后面循环调用的insert方法也有自己的事务。根据定义,inserBatch的事务会被延缓。具体表现就是:后面的10次循环的事务在每次循环结束之后都会提交自己的事务,而inserBatch的事务,要等循环方法走完之后再提交。但由于第10次循环会抛出异常,则inserBatch的事务会回滚,既数据库中不会存在:“初次调用”的记录:
  2. 测试代码:
  1. @Test
  2. public void insertBatchTest() {
  3. userService2.inserBatch();
  4. }
 

执行结果:

这种情况,符合开始说的“不要循环嵌套事务方法”的说话,当然是否需要循环嵌套,还是要看业务逻辑的。

五、NOT_SUPPORTED

  1. Execute non-transactionally, suspend the current transaction if one exists.
  2. 以非事务的方式运行,如果当前存在事务,暂停当前的事务。这种方式与REQUIRES_NEW有所类似,但是NOT_SUPPORTED修饰的方法其本身是没有事务的。这里就不做代码演示了。

六、NEVER

以非事务的方式运行,如果当前存在事务,则抛出异常。
  1. @Transactional(propagation = Propagation.NEVER)
  2. public void insert() {
  3. UserEntity user = new UserEntity();
  4. user.setUsername("happyjava");
  5. user.setPassword("123456");
  6. userRepo.save(user);
  7. }
  1. @Transactional
  2. public void insertWithTx() {
  3. userService.insert();
  4. }
执行结果:

七、NESTED

  1. 如果没有事务,就新建一个事务;如果有,就在当前事务中嵌套其他事务。
  2. 这个也是理解起来比较费劲的一个行为。我们一步一步分析。
  3. 外围方法没有事务:这种情况跟REQUIRED是一样的,会新建一个事务。
  4. 外围方法如果存在事务:这种情况就会嵌套事务。所谓嵌套事务,大意就是,外围事务回滚,内嵌事务一定回滚,而内嵌事务可以单独回滚而不影响外围主事务和其他子事务。
  5. 由于本人使用Spring Data JPA 进行的演示代码,使用嵌套事务会提示:
  6. org.springframework.transaction.NestedTransactionNotSupportedException: JpaDialect does not support savepoints - check your JPA provider's capabilities
搜索了下,hibernate似乎不支持这种事务传播方式。所以这里就不做演示了

  1. 总结
  2. 事务传播行为,在开发中可能不会特别的留意到它(更多时候,我们可能只是使用默认的方式),但是还是需要对其要有所理解。希望本篇文章能让大家明白Spring的7种事务传播行为。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/405401
推荐阅读
相关标签
  

闽ICP备14008679号