首先,我们通过org.springframework.transaction.annotation.Propagation来了解一下spring事务的传播定义:
0、REQUIRED(默认):

Support a current transaction, create a new one if none exists.

支持当前事务,如果没有则创建一个新的

1、SUPPORTS

Support a current transaction, execute non-transactionally if none exists.
支持当前事务,如果没有则不使用事务

2、MANDATORY

Support a current transaction, throw an exception if none exists

支持当前事务,如果没有事务则报错

3、REQUIRED_NEW

Create a new transaction, and suspend the current transaction if one exists.

新建一个事务,同时将当前事务挂起

4、NOT_SUPPORTED

Execute non-transactionally, suspend the current transaction if one exists

以无事务的方式执行,如果当前有事务则将其挂起

5、NEVER

Execute non-transactionally, throw an exception if a transaction exists.

以无事务的方式执行,如果当前有事务则报错

6、NESTED

Execute within a nested transaction if a current transaction exists,behave like PROPAGATION_REQUIRED else

如果当前有事务,则在当前事务内部嵌套一个事务,内部事务的回滚不影响当前事务。如果当前没有事务,就相当于REQUIRED

Note: Actual creation of a nested transaction will only work on specific transaction managers. Out of the box, this only applies to
the JDBC DataSourceTransactionManager when working on a JDBC 3.0 driver.
Some JTA providers might support nested transactions as well.

注意:该定义只能在JDBC3.0驱动下的DataSourceTransactionManager事务管理器中使用,有些JTA事务可能也会支持

接下来我们通过代码验证一下spring事务的传递性,在UserServiceImpl类添加两个方法如下:

@Transactional(propagation = Propagation.NEVER)
public User findById(Long id) {User user =  userMapper.findById(id);System.out.println("find user:"+user);return user;
}

@Transactional
public void transactionTest(int t) {
findById(t+0L);
}

我们调用transactionTest方法,transactionTest没有配置Propagation,所以默认是REQUIRED,会在当前新建一个事务。transactionTest内部调用findById,由于findById事务传播定义为NEVER,表明它当前不能有事务,按理说这里会抛出异常,但是我们利用junit执行后发现,transactionTest是可以正常执行的。

事实上,如果使用@Transaction方法里嵌套调用的是同一个类的方法,spring代理会忽略嵌套方法的@Transaction配置。但是,如果是其他注入对象的方法,那么@Transaction配置就会生效。我们将上面的transactionTest方法的事务传播定义为NERVER,并新增一个insert操作,即使insert启用了事务并且抛出异常,但是事务不会生效,也不会有回滚的说法,程序会抛出异常但是数据会保存到数据库中:

@Transactional(propagation = Propagation.NEVER)
public void transactionTest(int t) {findById(t+0L);insertUser("huangxl","abc123");
}
@Transactional
public int insertUser(String name, String password) {User user = new User();user.setPassword(password);user.setUsername(name);int insertCount =  userMapper.insertEntity(user);if(insertCount == 1 ){throw new RuntimeException("test transaction roll back");}return insertCount;
}

接下来我们来测试不同类之间的方法(事务)调用,以下的测试都是基于junit执行TransactionTestServiceImpl.test()方法

一、Propagation.NERVER的测试
下面我们将UserService注入到TransactionTestServiceImpl中,test方法使用@Transactional,UserService findById事务传播定义不变,还是NERVER。
UserserviceImpl:

@Service
public class TransactionTestServiceImpl implements TransactionTestService {@Autowiredprivate UserService userService;
<span class="hljs-annotation">@Override</span>
<span class="hljs-annotation">@Transactional</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">test</span>() {userService.findById(<span class="hljs-number">1</span>L);
}

}

TransactionTestServiceImpl:

@Service
public class UserServiceImpl implements UserService {@Override@Transactional(propagation = Propagation.NEVER)public User findById(Long id) {User user =  userMapper.findById(id);System.out.println("find user:"+user);return user;}
}

由于test默认启用了事务,findById不允许当前有事务,所以我们执行test方法后会发现程序抛出了异常:
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation ‘never’

结论:
NERVER 不允许当前存在事务


二、Propagation.REQUIRED的测试
UserserviceImpl:

@Transactional
public int insertUser(String name, String password) {User user = new User();user.setPassword(password);user.setUsername(name);int insertCount =  userMapper.insertEntity(user);if(insertCount == 1 ){throw new RuntimeException("test transaction roll back");}return insertCount;
}

TransactionTestServiceImpl:

@Transactional
public void test() {try {userService.insertUser("abc", "123");} catch (Exception e) {//do Nothing}userMapper.updateUserPassWord(1L, "456");
}

我们会发现,即使捕获了userService.insertUser抛出的异常,test还是把insertUser和updateUserPassword操作当成是一个整体,整个事务还是回滚了,程序抛出了下面的异常:
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

结论:
REQUIRED子事务会影响当前事务的提交、回滚


三、Propagation.NESTED的测试
UserserviceImpl:

@Transactional(propagation = Propagation.NESTED)
public int insertUser(String name, String password) {User user = new User();user.setPassword(password);user.setUsername(name);int insertCount =  userMapper.insertEntity(user);if(insertCount == 1 ){throw new RuntimeException("test transaction roll back");}return insertCount;
}

TransactionTestServiceImpl:

@Transactional
public void test() {try {userService.insertUser("abc", "123");} catch (Exception e) {//do Nothing}userMapper.updateUserPassWord(1L, "456");
}

程序正常运行,因为NESTED内部事务回滚不影响外部事务。假如这个时候我们把test的@Transactional去掉再运行test方法,发现insertUser没有插入用户信息,说明当前没有事务的情况下,NESTED会默认创建一个事务,类似于REQUIRED。
如果我们把程序改为下面的情况:

UserserviceImpl:

@Transactional(propagation = Propagation.NESTED)
public int insertUser(String name, String password) {User user = new User();user.setPassword(password);user.setUsername(name);int insertCount =  userMapper.insertEntity(user);return insertCount;
}

TransactionTestServiceImpl:

@Transactional
public void test() {userService.insertUser("abc", "123");int updateRow = userMapper.updateUserPassWord(1L, "456");if (updateRow == 1) {throw new RuntimeException("transational roll back");}
}

我们会发现没有插入用户信息,当前事务和子事务全部回滚。

结论:
NESTED子事务回滚不会影响当前事务的提交(catch回滚异常的情况下),但是当前事务回滚会回滚子事务。也就是说只有当前事务提交成功了,子事务才会提交成功。


四、Propagation.REQUIRED_NEW的测试
UserserviceImpl:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public int insertUser(String name, String password) {User user = new User();user.setPassword(password);user.setUsername(name);int insertCount =  userMapper.insertEntity(user);return insertCount;
}

TransactionTestServiceImpl:

@Transactional
public void test() {userService.insertUser("abc", "123");int updateRow = userMapper.updateUserPassWord(1L, "456");if (updateRow == 1) {throw new RuntimeException("transational roll back");}
}

运行结果:程序报错,但是有用户信息插入。

将程序改为下面的样子:
UserserviceImpl:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public int updateUserPassWorld(Long id, String password) {int update =  userMapper.updateUserPassWord(id,password);return update;
}

TransactionTestServiceImpl:

@Transactional
public void test() {//当前事务userMapper.updateUserPassWord(28L, "123456");//执行REQUIRES_NEW事务userService.updateUserPassWorld(28L, "000000");System.out.println("commit");
}

执行程序,发现程序迟迟没有打印字符串commit,发生了死锁。

结论:
REQUIRES_NEW会启用一个新的事务,事务拥有完全独立的能力,它不依赖于当前事务,执行时会挂起当前事务,直到REQUIRES_NEW事务完成提交后才会提交当前事务,如果当前事务与REQUIRES_NEW 存在锁竞争,会导致死锁


五、NOT_SUPPORTED的测试
UserserviceImpl:

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public int updateUserPassWorld(Long id, String password) {int updateRow =  userMapper.updateUserPassWord(id,password);
if(updateRow ==1 ){throw new RuntimeException("roll back test");
}
return updateRow;
}

TransactionTestServiceImpl:

@Transactional
public void test() {userService.updateUserPassWorld(28L, "000000");
}

程序运行报错,但是id为28的用户密码还是更新了。

将程序改为下面这个情况:
UserserviceImpl:

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public int updateUserPassWorld(Long id, String password) {int update =  userMapper.updateUserPassWord(id,password);return update;
}

TransactionTestServiceImpl:

@Transactional
public void test() {//当前事务userMapper.updateUserPassWord(28L, "123456");//执行REQUIRES_NEW事务userService.updateUserPassWorld(28L, "000000");System.out.println("commit");
}

执行程序,发现程序迟迟没有打印字符串commit,发生了死锁。

结论:
NOT_SUPPORTED会挂起当前事务,并且NOT_SUPPORTED定义的方法内部不启用显示事务,如果NOT_SUPPORTED和当前事务存在锁竞争,会发生死锁。


六、NOT_SUPPORTED的测试
UserserviceImpl:

@Transactional(propagation = Propagation.MANDATORY)
public int updateUserPassWorld(Long id, String password) {int updateRow =  userMapper.updateUserPassWord(id,password);return updateRow;
}

TransactionTestServiceImpl:

public void test() {userService.updateUserPassWorld(28L, "123456");
}

程序运行错误:
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation ‘mandatory’

结论:
MANDATORY必须包含在事务中,如果事务不存在,则抛出异常

以上便是对spring传播机制的简单测试,如有不对的地方,欢迎拍砖。

参考文献:
https://stackoverflow.com/questions/29857418/why-does-not-transactionalpropagation-propagation-never-work

spring事务传递机制原理相关推荐

  1. Spring事务传递机制和实现原理

    Spring事务传递机制和实现原理 实现原理 在实行事务的类或者方法上面添加@Transactional注解 Spring利用AOP思想机制,在方法执行前开启事务,在执行完成之后检查方法是否异常,根据 ...

  2. spring上下文是什么意思_Java程序员只会CRUD连Spring事务传播机制都不懂?

    AQS到底有什么用?难道就真的只是为了面试吗? 当然不是说AQS没用,如果你不是做基础架构或者中间件开发,你很难感受到AQS的威力.当然,学习很多时候,需要的是正向反馈,学了太多造火箭的东西,面试完就 ...

  3. 原创 | CRUD更要知道的Spring事务传播机制

    来自:肥朝 AQS到底有什么用?难道就真的只是为了面试吗? 当然不是说AQS没用,如果你不是做基础架构或者中间件开发,你很难感受到AQS的威力.当然,学习很多时候,需要的是正向反馈,学了太多造火箭的东 ...

  4. 《深入理解分布式事务》第三章 Spring 事务的实现原理

    <深入理解分布式事务>第三章 Spring 事务的实现原理 文章目录 <深入理解分布式事务>第三章 Spring 事务的实现原理 一.Spring 事务原理 1.JDBC 直接 ...

  5. Spring事务传播机制以及事务嵌套

    Spring事务传播机制以及事务嵌套 Spring事务传播机制 事务嵌套场景 情景0: 场景1:不同类中,开启事务的方法调用没有开启事务的方法 场景2:不同类中,methodA方法嵌套methodB方 ...

  6. jdbctemplate 开启事务_浅入浅出 Spring 事务传播实现原理

    本文和大家一起刨析 Spring 事务的相关源码,篇幅较长,代码片段较多,建议使用电脑阅读 本文目标 理解Spring事务管理核心接口 理解Spring事务管理的核心逻辑 理解事务的传播类型及其实现原 ...

  7. Spring 事务管理机制概述

    摘要: 一般地,用户的每次请求都对应一个业务逻辑方法,而一个业务逻辑方法往往包括一系列数据库原子访问操作,并且这些数据库原子访问操作应该绑定成一个事务来执行.然而,在使用传统的事务编程策略时,程序代码 ...

  8. Spring事务传播机制和隔离级别

    Spring有5种隔离级别,7种传播行为.这是面试常问的内容,也是代码中经常碰到的知识点.这些知识枯燥而且乏味,其中有些非常的绕.如果栽在这上面,就实在是太可惜了. @Transactional(is ...

  9. Java面试必问!Spring事务扩展机制(2)

    TransactionAspectSupport#invokeWithinTransaction TransactionAspectSupport#invokeWithinTransaction 是 ...

  10. Spring事务传播机制大白话(使用springboot,注解演示)

    1. 我对传播机制的理解 为什么需要传播机制? 因为事务之间可能存在相互调用,例如service业务层的方法存在相互调用,如果相互调用的方法都开启了事务(对应到springboot就是在方法上要添加@ ...

最新文章

  1. React子组件给父组件传值, 父组件引用子组件并给子组件传值
  2. 用于可解释机器学习的 Python 库
  3. python中set和frozenset方法和区别
  4. jquery学习手记(1)
  5. linux中的httpd源码安装方法
  6. .NET Core实战项目之CMS 第十章 设计篇-系统开发框架设计
  7. Outh2协议有哪四种授权模式?
  8. 取消Eclipse的自动代码格式化
  9. MSP430学习笔记10-ADC采集1602显示
  10. DirectSound学习笔记(4):设备性能
  11. C++中的void类型
  12. 浮点数指数域(阶码)的存储格式
  13. Atitti 过程导向 vs 结果导向 attilax的策略
  14. 产品经理 需求 项目经理 选择_【产品】产品和项目,产品经理和项目经理 区别...
  15. IBM Rhapsody 报错找不到 VC11 目录的解决方法
  16. IAR Embedded Workbench安装
  17. 超像素分割研究进展+SLIC近几年进展
  18. Win10 KeilC51-C251-ARM共存方法
  19. 对标大厂标准,C站(CSDN)软件工程师能力认证正式上线
  20. linux安装seaweedfs

热门文章

  1. 扎克伯格为女儿选的量子物理学童书 你看得懂不?
  2. spring4笔记----spring生命周期属性
  3. 如何处理iOS中照片的方向
  4. 修改 Ubuntu SSH 登录后的欢迎信息
  5. 4.Jenkins 2 权威指南 --- 通知与报告
  6. 25.go doc 与 godoc
  7. 19.docker attach
  8. 19.TCP/IP 详解卷1 --- TCP 的交互数据流
  9. 20.经典抽象数据类型
  10. 51. PHP 页面静态化(4)