转载:若水三千-LOVE

所谓事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。Spring 支持 7 种事务传播行为(Transaction Propagation Behavior):

传播行为     描述
PROPAGATION_REQUIRED  如果没有,就开启一个事务;如果有,就加入当前事务(方法B看到自己已经运行在 方法A的事务内部,就不再起新的事务,直接加入方法A)
RROPAGATION_REQUIRES_NEW  如果没有,就开启一个事务;如果有,就将当前事务挂起。(方法A所在的事务就会挂起,方法B会起一个新的事务,等待方法B的事务完成以后,方法A才继续执行)
PROPAGATION_NESTED   如果没有,就开启一个事务;如果有,就在当前事务中嵌套其他事务
PROPAGATION_SUPPORTS    如果没有,就以非事务方式执行;如果有,就加入当前事务(方法B看到自己已经运行在 方法A的事务内部,就不再起新的事务,直接加入方法A)
PROPAGATION_NOT_SUPPORTED   如果没有,就以非事务方式执行;如果有,就将当前事务挂起,(方法A所在的事务就会挂起,而方法B以非事务的状态运行完,再继续方法A的事务)
  PROPAGATION_NEVER 如果没有,就以非事务方式执行;如果有,就抛出异常。
PROPAGATION_MANDATORY    如果没有,就抛出异常;如果有,就使用当前事务

其中前4种是开发中用到概率比较大的,建议熟记;后面3种不常用,了解就行。

A传播到B,显而易见进入A方法执行半途中,再次进入B方法,这才叫做传播到方法B中。

还有一点初学者不要搞错的是,这里的方法A和方法B理论上不应该在一个Service中,而是在不同的Service中,这里面有个坑我们会在后面介绍。

1.PROPAGATION_REQUIRED

我们的例子使用的是@Transactional注解,默认使用REQUIRED传播行为。

假设事务从方法 A 传播到方法 B。

A有事务

我们现在面对方法B,其中B会抛出异常。

@Service
public class UserService01 {@Autowiredprivate UserDao userDao;@Autowiredprivate LogService01 logService;@Transactionalpublic void A() {userDao.insert(new User("admin","123456"));logService.B();}
}@Service
class LogService01 {@Transactionalpublic void B() {logDao.insert(new Log("abcdefghijklmn","192.168.1.1"));System.out.println(1/0);}@Autowiredprivate LogDao logDao;
}

方法A和方法B都不会保存数据成功

我们现在面对方法B,其中A会抛出异常。

@Service
public class UserService02 {@Autowiredprivate UserDao userDao;@Autowiredprivate LogService02 logService;@Transactionalpublic void A() {userDao.insert(new User("admin","123456"));logService.B();System.out.println(1/0);}
}@Service
class LogService02 {@Transactionalpublic void B() {logDao.insert(new Log("abcdefghijklmn","192.168.1.1"));}@Autowiredprivate LogDao logDao;
}

方法A和方法B都不会保存数据成功

A没有事务
我们现在面对方法B,其中B会抛出异常。

@Service
public class UserService03 {@Autowiredprivate UserDao userDao;@Autowiredprivate LogService03 logService;public void A() {userDao.insert(new User("admin","123456"));logService.B();}
}@Service
class LogService03 {@Transactionalpublic void B() {logDao.insert(new Log("abcdefghijklmn","192.168.1.1"));System.out.println(1/0);}@Autowiredprivate LogDao logDao;
}

方法A中的数据保存成功,方法B中的数据保存失败

我们现在面对方法B,其中A会抛出异常。

@Service
public class UserService04 {@Autowiredprivate UserDao userDao;@Autowiredprivate LogService04 logService;public void A() {userDao.insert(new User("admin","123456"));logService.B();System.out.println(1/0);}
}@Service
class LogService04 {@Transactionalpublic void B() {logDao.insert(new Log("abcdefghijklmn","192.168.1.1"));}@Autowiredprivate LogDao logDao;
}

方法A中的数据保存成功,方法B中的数据保存成功

如果没有,就开启一个事务(方法B运行的时候发现自己没有在事务中,它就会为自己分配一个事务);如果有,就加入当前事务。(方法B看到自己已经运行在 方法A的事务内部,就不再起新的事务,直接加入方法A)。这就是 PROPAGATION_REQUIRED,它也是 Spring 提供的默认事务传播行为,适合绝大多数情况。假设事务从方法 A 传播到方法 B。

2.RROPAGATION_REQUIRES_NEW 

A有事务

我们现在面对方法B,其中B会抛出异常。

@Service
public class UserService11 {@Autowiredprivate UserDao userDao;@Autowiredprivate LogService11 logService;@Transactionalpublic void A() {userDao.insert(new User("admin","123456"));logService.B();}
}@Service
class LogService11 {@Transactional(propagation=Propagation.REQUIRES_NEW)public void B() {logDao.insert(new Log("abcdefghijklmn","192.168.1.1"));System.out.println(1/0);}@Autowiredprivate LogDao logDao;
}

方法A中的数据保存失败,方法B中的数据保存失败

@Service
public class UserService11_ {@Autowiredprivate UserDao userDao;@Autowiredprivate LogService11_ logService;@Transactional(noRollbackFor=ArithmeticException.class)public void A() {userDao.insert(new User("admin","123456"));logService.B();}
}@Service
class LogService11_ {@Transactional(propagation=Propagation.REQUIRES_NEW)public void B() {logDao.insert(new Log("abcdefghijklmn","192.168.1.1"));System.out.println(1/0);}@Autowiredprivate LogDao logDao;
}

方法A中的数据保存成功,方法B中的数据保存失败

我们现在面对方法B,其中A会抛出异常。

@Service
public class UserService12 {@Autowiredprivate UserDao userDao;@Autowiredprivate LogService12 logService;@Transactionalpublic void A() {userDao.insert(new User("admin","123456"));logService.B();System.out.println(1/0);}
}@Service
class LogService12 {@Transactional(propagation=Propagation.REQUIRES_NEW)public void B() {logDao.insert(new Log("abcdefghijklmn","192.168.1.1"));}@Autowiredprivate LogDao logDao;
}

方法A中的数据保存失败,方法B中的数据保存成功

A没有事务
我们现在面对方法B,其中B会抛出异常。

我们现在面对方法B,其中A会抛出异常。

例子可以参考PROPAGATION_REQUIRED,它们都是如果A没有事务,B就为自己分配一个事务,结果也一样。

如果没有,就开启一个事务(方法B运行的时候发现自己没有在事务中,它就会为自己分配一个事务);如果有,就将当前事务挂起。(方法A所在的事务就会挂起,方法B会起一个新的事务,等待方法B的事务完成以后,方法A才继续执行)。这就是 RROPAGATION_REQUIRES_NEW,意思就是创建了一个新事务,它和原来的事务没有任何关系了。

RROPAGATION_REQUIRES_NEW与 PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为 方法B是新起一个事务,那么就是存在两个不同的事务。

1.如果方法B已经提交,那么 方法A失败回滚,方法B是不会回滚的。
   2.如果方法B失败回滚,如果它抛出的异常被方法A捕获,方法A的事务仍然可能提交(主要看方法B抛出的异常是不是方法A会回滚的异常)

3.PROPAGATION_NESTED
 如果没有,就开启一个事务(方法B运行的时候发现自己没有在事务中,它就会为自己分配一个事务);如果有,就在当前事务中嵌套其他事务。这就是 PROPAGATION_NESTED,也就是传说中的“嵌套事务”了,所嵌套的子事务与主事务之间是有关联的(当主事务提交或回滚,子事务也会提交或回滚)。方法A所在的事务就会挂起,方法B会起一个新的子事务并设置savepoint,等待方法B的事务完成以后,方法A才继续执行。因为方法B是外部事务的子事务,那么

1.如果方法B已经提交,那么方法A失败回滚,方法B也将回滚。(REQUIRES_NEW中此种情况方法B是不会回滚的,因为方法B是独立事务,提交就是提交了)
2.如果方法B失败回滚,如果它抛出的异常被方法A捕获,方法A的事务仍然可能提交(主要看方法B抛出的异常是不是方法A会回滚的异常)
理解NESTED的关键是savepoint。他与REQUIRES_NEW的区别是: 
REQUIRES_NEW完全是一个新的事务,它与外部事务相互独立; 而 NESTED 则是外部事务的子事务, 如果外部事务commit, 嵌套事务也会被commit, 这个规则同样适用于roll back

4.PROPAGATION_SUPPORTS

1.如果没有,就以非事务方式执行(如果发现方法A没有开启事务,则方法B也不开启事务);

2.如果有,就加入当前事务。(方法B看到自己已经运行在方法A的事务内部,就不再起新的事务,直接加入方法A)。这就是 PROPAGATION_SUPPORTS。

这种方式非常随意,没有就没有,有就有,有点无所谓的态度,反正我是支持你的。

5.PROPAGATION_NOT_SUPPORTED

1.如果没有,就以非事务方式执行(如果发现方法A没有开启事务,则方法B也不开启事务);

2.如果有,就将当前事务挂起。(方法A的事务挂起,而方法B非事务的状态运行完,再继续方法A的事务。)

这就是 PROPAGATION_NOT_SUPPORTED,这种方式非常强硬,没有就没有,有我也不支持你,把你挂起来,不鸟你。

6.PROPAGATION_NEVER

1.如果没有,就以非事务方式执行(如果发现方法A没有开启事务,则方法B也不开启事务);、

2.如果有,就抛出异常(如果发现方法A有开启事务,则方法B直接抛出异常)。

这就是 PROPAGATION_NEVER,这种方式更猛,没有就没有,有了反而报错,确实够牛的,它说:我从不支持事务!

7.PROPAGATION_MANDATORY

如果没有,就抛出异常;如果有,就使用当前事务。

这就是 PROPAGATION_MANDATORY,这种方式可以说是牛逼中的牛逼了,没有事务直接就报错,确实够狠的,它说:我必须要有事务!

总结

需要注意的是 PROPAGATION_NESTED,不要被它的名字所欺骗,Nested(嵌套),所以凡是在类似方法 A 调用方法 B 的时候,在方法 B 上使用了这种事务传播行为,如果您真的那样做了,那您就错了。因为您错误地以为 PROPAGATION_NESTED 就是为方法嵌套调用而准备的,其实默认的 PROPAGATION_REQUIRED 就可以帮助您,做您想要做的事情了。

Spring 给我们带来了事务传播行为,这确实是一个非常强大而又实用的功能。除此以外,也提供了一些小的附加功能,比如:

1.事务超时(Transaction Timeout):为了解决事务时间太长,消耗太多的资源,所以故意给事务设置一个最大时常,如果超过了,就回滚事务。
2.只读事务(Readonly Transaction):为了忽略那些不需要事务的方法,比如读取数据,这样可以有效地提高一些性能。 
最后,推荐大家使用 Spring 的注解式事务配置,而放弃 XML 式事务配置。因为注解实在是太优雅了,当然这一切都取决于您自身的情况了。
在 Spring 配置文件中使用:

...
<tx:annotation-driven/>  

在需要事务的方法上使用:

@Transactional
public void xxx() {  ...
}  

可在 @Transactional 注解中设置:事务隔离级别、事务传播行为、事务超时时间、是否只读事务。 
简直是太完美了,太优雅了!

Spring默认情况下会对运行期例外(RunTimeException),即uncheck异常,进行事务回滚。 
如果遇到checked异常就不回滚。

如何改变默认规则:

让checked例外也回滚:在整个方法前加上

@Transactional(rollbackFor=Exception.class)

让unchecked例外不回滚:

@Transactional(notRollbackFor=RunTimeException.class)

最后,有必要对本文的内容做一个总结,免费赠送一张高清无码思维导图:

spring中事务传播特性相关推荐

  1. Spring 中事务传播行为

    事务传播行为是指方法之间的调用事务策略的问题,在理解数据库的事务时,我们一般时希望事务能够同时成功或同时失败.但是在写代码的时候并非如此.如果在进行批处理时,其中的一条处理失败了,我们只希望失败的这条 ...

  2. Spring事务传播特性实例解析

    背景介绍 目前系统正在进行代码重构前期预研工作,目标采用spring控制事务以减少开发代码量,提高开发效率.同时避免开发人员编码控制事务所带来的链接没有释放,事务没有提交,出现异常事务没有回滚的Bug ...

  3. 什么是事务、事务特性、事务隔离级别、spring事务传播特性

    1.什么是事务: 事务是指程序中的一个操作序列.其特点是:该序列的所有操作要么全部成功完成,要么只要有一个操作失败,则该序列所有操作都将被撤销.这也是事务的原子性(要么成功,要么失败). 2.事务特性 ...

  4. mysql数据库事务传播特性_什么是事务的传播特性?

    我们都知道事务的概念,那么事务的传播特性是什么呢?(此处着重介绍传播特性的概念,关于传播特性的相关配置就不介绍了,可以查看spring的官方文档) 在我们用SSH开发项目的时候,我们一般都是将事务设置 ...

  5. 【Spring】Spring不同事务传播行为测试

    首先我说下写这篇文章的原因,虽然平时也频繁使用spring的事务,但是对事务在复杂情况下发生回滚的情况和spring不同的事务传播行为还是很模糊.   因为平时都只使用默认的传播行为,其他的很少用.但 ...

  6. Spring事务专题(四)Spring中事务的使用、抽象机制及模拟Spring事务实现

    前言 本专题大纲如下: 事务专题大纲 「对于专题大纲我又做了调整哈,主要是希望专题的内容能够更丰富,更加详细」,本来是想在源码分析的文章中附带讲一讲事务使用中的问题,这两天想了想还是单独写一篇并作为事 ...

  7. spring中事务控制的一组API

    Spring事务控制我们要明确的 第一:JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案. 第二:spring框架为我们提供了一组事务控制的接口.具 ...

  8. Spring中事务的使用、抽象机制及模拟Spring事务实现

    本文大纲如下: Spring事务应用大纲 编程式事务 Spring提供了两种编程式事务管理的方法 使用 TransactionTemplate 或者 TransactionalOperator. 直接 ...

  9. 事务例子_Spring事务专题(四)Spring中事务的使用、抽象机制及模拟Spring事务实现...

    Spring中事务的使用示例.属性及使用中可能出现的问题 前言 本专题大纲如下: 「对于专题大纲我又做了调整哈,主要是希望专题的内容能够更丰富,更加详细」,本来是想在源码分析的文章中附带讲一讲事务使用 ...

最新文章

  1. php libmysqlclient,什么是php?以及mysqlnd与libmysqlclient
  2. mysql 统计_告别硬编码,mysql 如何实现按某字段的不同取值进行统计
  3. webplugin 没有画面_[问题记录] webpack devServer HtmlWebpackPlugin 没有加载 js、css
  4. g++编译时:No such file or directory
  5. .Net Core应用搭建的分布式邮件系统设计
  6. mockito接口没法赋值_Mockito:无法实例化@InjectMocks字段:类型是接口
  7. 亮点抢先看 | 旷视科技11篇 ICCV 2019 论文概览
  8. 爱我的人请别走远(转载)
  9. 他面前有一个人,有一把刀
  10. mysql怎么查合计_mysql-查询不同列的数量合计
  11. NET面试问题及答案
  12. 毕业生的商业软件开发之路 ---- 商业软件开发基础
  13. matlab求线与面的夹角,基于MATLAB的通用晶面间夹角公式的推导与求解
  14. 微信支付:手机系统自带的浏览器,调用微信支付如何实现(非扫码)
  15. java --运用hhs 框架,tomcat 访问mysql 数据库 连接 失败后,自动 重新连接怎么做?
  16. oracle学习笔记(四)-- 数学函数
  17. 网工解惑,子网掩码是什么以及子网掩码有哪些?
  18. Novell推出可替代微软的桌面应用软件包(转)
  19. macOS上的汇编入门(五)——第一个汇编程序
  20. 计算机考研公共课考英语几,新文道教育:2022考研必须要了解的30个知识点

热门文章

  1. html自适应布局视频,2018年最新的8个响应式与自适应视频教程推荐
  2. 倾斜模型数据及激光雷达点云数据在EPS中联动生产高精度1:500地形图
  3. 等价类划分法测试网易邮箱注册功能
  4. 图解transformer | The Illustrated Transformer
  5. 生鲜水果微信小程序推广教程归纳
  6. 美团CEO王兴:8年时间,我对商业的思考
  7. 激发学计算机兴趣的小游戏,如何运用游戏来激发学生的练习兴趣
  8. 一个纯html的文本简历,可供参考。
  9. postgresql snapshot快照源码解析, 快照内容生成规则, 可见性是这样判断的
  10. AutoCAD2016二次开发创建Polyline包围面Polygon