本篇主要讨论下面几点:
一: Spring 事务的传递性介绍
二: 第三方调用含有事务的Service抛异常方法探讨
一: Spring 事务的传递性介绍
事务传播行为,所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价TransactionDefinition.PROPAGATION_REQUIRED。
这里需要指出的是,前面的六种事务传播行为是 Spring 从 EJB 中引入的,他们共享相同的概念。而 PROPAGATION_NESTED是 Spring 所特有的。以 PROPAGATION_NESTED 启动的事务内嵌于外部事务中(如果存在外部事务的话),此时,内嵌事务并不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交。如果熟悉 JDBC 中的保存点(SavePoint)的概念,那嵌套事务就很容易理解了,其实嵌套的子事务就是保存点的一个应用,一个事务中可以包括多个保存点,每一个嵌套子事务。另外,外部事务的回滚也会导致嵌套子事务的回滚。
二: 第三方调用含有事务的Service抛异常方法探讨
原始数据:
假设术语如下:
a)正常情况
调用方为Conumer, 调用ServiceA的methodA, methodA调用ServiceA的 methodB和
ServiceA的methodC。
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void modifyCommodityInfo(Commodity commodity) {
updateCommodityCatalog1(commodity);
updateCommodityCatalog2(commodity);
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
private void updateCommodityCatalog1(Commodity commodity) {
commodity.setCatalog("catalog222222222");
commodityDao.updateCommodity(commodity);
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
private void updateCommodityCatalog2(Commodity commodity) {
commodity.setName("name222");
commodityDao.updateCommodity(commodity);
}
运行结果为:
b)事务中一个方法跑出异常
ServiceA的methodA 调用ServiceA的methodB和ServiceA的methodC,代码如下:
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void modifyCommodityInfo(Commodity commodity) {
updateCommodityCatalog1(commodity);
updateCommodityCatalog2(commodity);
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
private void updateCommodityCatalog1(Commodity commodity) {
commodity.setCatalog("catalog222222222");
commodityDao.updateCommodity(commodity);
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
private void updateCommodityCatalog2(Commodity commodity) {
commodity.setName("name222");
commodityDao.updateCommodity(commodity);
throw new RuntimeException("222");
}
运行结果:
c)捕获异常的情况
ServiceA的methodA 调用ServiceA的methodB和ServiceA的methodC,代码如下:
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void modifyCommodityInfo(Commodity commodity) {
updateCommodityCatalog1(commodity);
try {
updateCommodityCatalog2(commodity);
} catch (Exception e) {
}
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
private void updateCommodityCatalog1(Commodity commodity) {
commodity.setCatalog("catalog222222222");
commodityDao.updateCommodity(commodity);
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
private void updateCommodityCatalog2(Commodity commodity) {
commodity.setName("name222");
commodityDao.updateCommodity(commodity);
throw new RuntimeException("222");
}
运行结果:
d)跨服务事务
ServiceA的methodA 调用 ServiceA的methodB和ServiceA的methodC和ServiceB的methodD,代码如下:
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void modifyCommodityInfo(Commodity commodity) {
updateCommodityCatalog1(commodity);
updateCommodityCatalog2(commodity);
updateCommodityCatalog3(commodity);
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
private void updateCommodityCatalog1(Commodity commodity) {
commodity.setCatalog("catalog222222222");
commodityDao.updateCommodity(commodity);
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
private void updateCommodityCatalog2(Commodity commodity) {
commodity.setName("name222");
commodityDao.updateCommodity(commodity);
}
//另一个Service InnerService的方法
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void updateCommodityCatalog3(Commodity commodity) {
commodity.setDescription("desc333");
commodityDao.updateCommodity(commodity);
throw new RuntimeException("333");
}
运行结果:
假设MethodD抛出异常,则Consumer调用MethodA会发生怎样的情形?
发现Consumer调用MethodA的时候出现了运行时异常,UnexpectedRollbackException: “Transaction rolled back because it has been marked as rollback-only”。这是为什么呢?
网上搜索了下,终于发现了一个合理的解释。当MethodA调用MethodD的时候,且两个方法都为required属性,根据事务传播级别,则methodA和methodD共享一个事务,当methodD抛出了异常,则共享事务回滚,但是被MethodA catch了,而MethodA又没有及时抛出异常,则MethodA正常执行到最后的时候,则会做提交事务的操作,但是事务已经被回滚了,所以才出现了上面的异常。
既然这样,小弟就开始YY了一下,哪些情况会使调用方没有这个异常呢?经过与小伙伴们的思维碰撞,发现有一下几个方法。
1) MethodA 不加事务,所以执行到最后就不会commit,SUPPORTS和NOT_SUPPORTED都可以实现这种功能。
2) MethodD设置不共享事务,拥有自己单独的事务。验证发现,REQUIRES_NEW可以实现这种功能。
e)
代码如下:
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void modifyCommodityInfo(Commodity commodity) {
updateCommodityCatalog1(commodity);
updateCommodityCatalog2(commodity);
try {
commodityInnerService.updateCommodityCatalog3(commodity);
} catch (Exception e) {
}
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
private void updateCommodityCatalog1(Commodity commodity) {
commodity.setCatalog("catalog222222222");
commodityDao.updateCommodity(commodity);
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
private void updateCommodityCatalog2(Commodity commodity) {
commodity.setName("name222");
commodityDao.updateCommodity(commodity);
}
//另一个Service InnerService的方法
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void updateCommodityCatalog3(Commodity commodity) {
commodity.setDescription("desc333");
commodityDao.updateCommodity(commodity);
throw new RuntimeException("333");
}
结果如下:
然后又YY了下,既然一个回滚的事务不能提交了,那么这个回滚的事务可以重复回滚吗?
f)ServiceA的methodA 调用 ServiceA的methodB和ServiceA的methodC和ServiceB的methodD和ServiceB的methodE。代码如下:
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void modifyCommodityInfo(Commodity commodity) {
updateCommodityCatalog1(commodity);
updateCommodityCatalog2(commodity);
try {
ommodityInnerService.updateCommodityCatalog3(commodity);
} catch (Exception e) {
}
try {
commodityInnerService.updateCommodityCatalog4(commodity);
} catch (Exception e) {
}
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
private void updateCommodityCatalog1(Commodity commodity) {
commodity.setCatalog("catalog222222222");
commodityDao.updateCommodity(commodity);
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
private void updateCommodityCatalog2(Commodity commodity) {
commodity.setName("name222");
commodityDao.updateCommodity(commodity);
}
//另一个Service InnerService的方法
@Transactional(propagation = Propagation.REQUIRES, rollbackFor = Exception.class)
public void updateCommodityCatalog3(Commodity commodity) {
commodity.setDescription("desc333");
commodityDao.updateCommodity(commodity);
throw new RuntimeException("333");
}
//另一个Service InnerService的方法
@Transactional(propagation = Propagation.REQUIRES, rollbackFor = Exception.class)
public void updateCommodityCatalog4(Commodity commodity) {
commodity.setDescription("desc333");
commodityDao.updateCommodity(commodity);
throw new RuntimeException("333");
}
运行结果:
发现还是只抛出了一个Transaction rolled back because it has been marked as rollback-only
所以猜测一个被共享的事务抛出多个异常的时候只是标记下rollback-only,而在方法结束的时候判断是执行事务还是回滚事务。
总结:
1) 单个ServiceA 内部调用不存在事务传播,相当于把methodB和methodC的代码嵌套到methodA的代码中,即使定义了也无法生效。
2) 跨Service调用存在事务传播级别,需要考虑共享事务,还是新事务调用,即跨Service的调用是否需要需要回滚本Servcie的代码。
参考:https://www.ibm.com/developerworks/cn/education/opensource/os-cn-spring-trans/
- 大小: 7.8 KB
- 大小: 3.5 KB
- 大小: 3.3 KB
- 大小: 9.8 KB
- 大小: 8.3 KB
- 大小: 13 KB
- 大小: 14.8 KB
分享到:
相关推荐
spring事务与数据库操作
Spring事务管理Demo
Spring事务流程图时序图Spring事务流程图时序图Spring事务流程图时序图Spring事务流程图时序图
Spring事务原理、Spring事务配置的五种方式
spring事务配置详解 spring事务配置详解
spring 事务spring 事务spring 事务spring 事务spring 事务
spring事务的底层实现流程图 spring事务的底层实现流程图 spring事务的底层实现流程图 spring事务的底层实现流程图 spring事务的底层实现流程图 spring事务的底层实现流程图 spring事务的底层实现流程图 spring事务...
spring学习事务源码spring学习事务源码spring学习事务源码spring学习事务源码spring学习事务源码spring学习事务源码spring学习事务源码spring学习事务源码
Spring中事务的传播属性详解,Spring中事务的传播属性详解
Spring事务操作示例(四种方式),包含完整代码和数据库文件(基于MySQL,在项目sql文件夹中),可运行,学习Spring事务详见博客:http://blog.csdn.net/daijin888888/article/details/51822257
spring 事务传播 demo
spring事务源码解析
Java高级编程 实验报告 spring 声明事务 实验目的 掌握spring 声明式事务管理配置 实验环境 本实验采用本实验采用的eclipse或者 Myeclpse开发工具。Spring 4.0以上 Jdk1.7以上、oracle/mysql。
Spring事务管理教程,详细讲解了Spring中的事务管理,包括声明式事务,注解式事务,以及事务配置等等
Spring的事务框架将开发过程中事务管理相关的关注点进行适当的分离,并对这些关注点进行合 理的抽象,最终打造了一套使用方便,却功能强大的事务管理“利器”。通过Spring的事务框架,我 们可以按照统一的编程模型来...
spring事务控制jar包,请网上自寻下载
Spring事务失效Spring事务失效
Spring事务管理.pdf 1.资料 2.本地事务与分布式事务 3.编程式模型 4.宣告式模型
spring 事务基于注解模式