Spring是一个Java开源框架,是为了解决企业应用程序开发复杂性由Rod Johnson创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。

Spring声明式事务让我们从复杂的事务处理中得到解脱,使得我们再也不必去处理获得连接、关闭连接、事务提交和回滚等这些操作,再也无需我们在与事务相关的方法中处理大量的try…catch…finally代码。 
我们在使用Spring声明式事务时,有一个非常重要的概念就是事务属性。事务属性通常由事务的传播行为、事务的隔离级别、事务的超时值、事务只读标志组成。我们在进行事务划分时,需要进行事务定义,也就是配置事务的属性。

Spring在TransactionDefinition接口中定义这些属性,以供PlatfromTransactionManager(github)使用,PlatfromTransactionManager是spring事务管理的核心接口。

TransactionDefinition.java(spring-tx/src/main/java/org/springframework/transaction/TransactionDefinition.java,github

  1. public interface TransactionDefinition {
  2. int getPropagationBehavior();
  3. int getIsolationLevel();
  4. int getTimeout();
  5. boolean isReadOnly();
  6. }

1) getPropagationBehavior(): 返回事务的传播行为,由是否有一个活动的事务来决定一个事务调用。 
2) getTimeout(): 它返回事务必须在多少秒内完成。 
3) isReadOnly(): 事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的。 
4) getIsolationLevel(): 返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据。

1、事务的的隔离级别

在TransactionDefinition接口中,定义了五个不同的事务隔离级别 :
1) ISOLATION_DEFAULT (默认隔离级别)

这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应

2) ISOLATION_READ_UNCOMMITTED (读未提交)

这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻读。 例如: 
  Mary的原工资为1000,财务人员将Mary的工资改为了8000,但未提交事务

  1. Connection con1 = getConnection();
  2. con.setAutoCommit(false);
  3. update employee set salary = 8000 where empId ="Mary";

与此同时,Mary正在读取自己的工资

  1. Connection con2 = getConnection();
  2. select  salary from employee where empId ="Mary";
  3. con2.commit();

Mary发现自己的工资变为了8000,欢天喜地! 
而财务发现操作有误,而回滚了事务,Mary的工资又变为了1000

  1. //con1
  2. con1.rollback();

像这样,Mary记取的工资数8000是一个脏数据。

3)ISOLATION_READ_COMMITTED(读已提交)

该隔离级别保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。

在事务1中,Mary 读取了自己的工资为1000,操作并没有完成

  1. con1 = getConnection();
  2. select salary from employee empId ="Mary";

在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.

  1. con2 = getConnection();
  2. update employee set salary = 2000;
  3. con2.commit();

在事务1中,Mary 再次读取自己的工资时,工资变为了2000

  1. //con1
  2. select salary from employee empId ="Mary";

在一个事务中前后两次读取的结果并不致,导致了不可重复读。

4)ISOLATION_REPEATABLE_READ

这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。

目前工资为1000的员工有10人。 
事务1,读取所有工资为1000的员工。

  1. con1 = getConnection();
  2. Select * from employee where salary =1000;

共读取10条记录

这时另一个事务向employee表插入了一条员工记录,工资也为1000

  1. con2 = getConnection();
  2. Insert into employee(empId,salary) values("Lili",1000);
  3. con2.commit();

事务1再次读取所有工资为1000的员工

  1. //con1
  2. select * from employee where salary =1000;

共读取到了11条记录,这就产生了幻像读。

5)ISOLATION_SERIALIZABLE

这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。但是这样也耗费了最大的资源。

2、事务的传播特性

getPropagationBehavior()返回事务的传播行为,由是否有一个活动的事务来决定一个事务调用。 在TransactionDefinition接口中定义了七个事务传播行为。

1)PROPAGATION_REQUIRED

如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。

  1. //事务属性 PROPAGATION_REQUIRED
  2. methodA{
  3. ……
  4. methodB();
  5. ……
  6. }
  7. //事务属性 PROPAGATION_REQUIRED
  8. methodB{
  9. ……
  10. }

使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在方法调用之前决定是否开启一个事务,并在方法执行之后决定事务提交或回滚事务。

单独调用methodB方法

  1. main{
  2. metodB();
  3. }

相当于

  1. Main{
  2. Connection con=null;
  3. rry{
  4. con = getConnection();
  5. con.setAutoCommit(false);
  6. //方法调用
  7. methodB();
  8. //提交事务
  9. con.commit();
  10. }
  11. Catch(RuntimeException ex){
  12. //回滚事务
  13. con.rollback();
  14. }
  15. finally{
  16. //释放资源
  17. closeCon();
  18. }
  19. }

Spring保证在methodB方法中所有的调用都获得到一个相同的连接。在调用methodB时,没有一个存在的事务,所以获得一个新的连接,开启了一个新的事务。

单独调用MethodA时,在MethodA内又会调用MethodB.

执行效果相当于

  1. main{
  2. Connection con = null;
  3. try{
  4. con = getConnection();
  5. methodA();
  6. con.commit();
  7. }
  8. cathc(RuntimeException ex){
  9. con.rollback();
  10. }
  11. finally{
  12. closeCon();
  13. }
  14. }

调用MethodA时,环境中没有事务,所以开启一个新的事务. 
当在MethodA中调用MethodB时,环境中已经有了一个事务,所以methodB就加入当前事务。

2)PROPAGATION_SUPPORTS

如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。

  1. //事务属性 PROPAGATION_REQUIRED
  2. methodA(){
  3. methodB();
  4. }
  5. //事务属性 PROPAGATION_SUPPORTS
  6. methodB(){
  7. ……
  8. }

单纯的调用methodB时,methodB方法是非事务的执行的。 
当调用methdA时,methodB则加入了methodA的事务中,事务地执行。

3)PROPAGATION_MANDATORY

如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。

  1. //事务属性 PROPAGATION_REQUIRED
  2. methodA(){
  3. methodB();
  4. }
  5. //事务属性 PROPAGATION_MANDATORY
  6. methodB(){
  7. ……
  8. }

当单独调用methodB时,因为当前没有一个活动的事务,则会抛出异常 
throw new IllegalTransactionStateException("Transaction propagation 'mandatory' but no existing transaction found");

当调用methodA时,methodB则加入到methodA的事务中,事务地执行。

4) PROPAGATION_REQUIRES_NEW

总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。

  1. //事务属性 PROPAGATION_REQUIRED
  2. methodA(){
  3. doSomeThingA();
  4. methodB();
  5. doSomeThingB();
  6. }
  7. //事务属性 PROPAGATION_REQUIRES_NEW
  8. methodB(){
  9. ……
  10. }

当单独调用methodB时,相当于把methodb声明为REQUIRED。开启一个新的事务,事务地执行。

当调用methodA时

  1. main(){
  2. methodA();
  3. }

情况有些大不一样.相当于下面的效果。

  1. main(){
  2. TransactionManager tm = null;
  3. try{
  4. //获得一个JTA事务管理器
  5. tm = getTransactionManager();
  6. tm.begin();//开启一个新的事务
  7. Transaction ts1 = tm.getTransaction();
  8. doSomeThing();
  9. tm.suspend();//挂起当前事务
  10. try{
  11. tm.begin();//重新开启第二个事务
  12. Transaction ts2 = tm.getTransaction();
  13. methodB();
  14. ts2.commit();//提交第二个事务
  15. }
  16. Catch(RunTimeException ex){
  17. ts2.rollback();//回滚第二个事务
  18. }
  19. finally{
  20. //释放资源
  21. }
  22. //methodB执行完后,复恢第一个事务
  23. tm.resume(ts1);
  24. doSomeThingB();
  25. ts1.commit();//提交第一个事务
  26. }
  27. catch(RunTimeException ex){
  28. ts1.rollback();//回滚第一个事务
  29. }
  30. finally{
  31. //释放资源
  32. }
  33. }

在这里,我把ts1称为外层事务,ts2称为内层事务。从上面的代码可以看出,ts2与ts1是两个独立的事务,互不相干。Ts2是否成功并不依赖于ts1。如果methodA方法在调用methodB方法后的doSomeThingB方法失败了,而methodB方法所做的结果依然被提交。而除了methodB之外的其它代码导致的结果却被回滚了。 
使用PROPAGATION_REQUIRES_NEW,需要使用JtaTransactionManager作为事务管理器。

5)PROPAGATION_NOT_SUPPORTED

总是非事务地执行,并挂起任何存在的事务。

  1. //事务属性 PROPAGATION_REQUIRED
  2. methodA(){
  3. doSomeThingA();
  4. methodB();
  5. doSomeThingB();
  6. }
  7. //事务属性 PROPAGATION_NOT_SUPPORTED
  8. methodB(){
  9. ……
  10. }

当单独调用methodB时,不启用任何事务机制,非事务地执行。 
当调用methodA时,相当于下面的效果

  1. main(){
  2. TransactionManager tm = null;
  3. try{
  4. //获得一个JTA事务管理器
  5. tm = getTransactionManager();
  6. tm.begin();//开启一个新的事务
  7. Transaction ts1 = tm.getTransaction();
  8. doSomeThing();
  9. tm.suspend();//挂起当前事务
  10. methodB();
  11. //methodB执行完后,复恢第一个事务
  12. tm.resume(ts1);
  13. doSomeThingB();
  14. ts1.commit();//提交第一个事务
  15. }
  16. catch(RunTimeException ex){
  17. ts1.rollback();//回滚第一个事务
  18. }
  19. finally{
  20. //释放资源
  21. }
  22. }

使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作为事务管理器。

6)PROPAGATION_NEVER

总是非事务地执行,如果存在一个活动事务,则抛出异常

  1. //事务属性 PROPAGATION_REQUIRED
  2. methodA(){
  3. doSomeThingA();
  4. methodB();
  5. doSomeThingB();
  6. }
  7. //事务属性 PROPAGATION_NEVER
  8. methodB(){
  9. ……
  10. }

单独调用methodB,则非事务的执行。 
调用methodA则会抛出异常 
throw new IllegalTransactionStateException( 
"Transaction propagation 'never' but existing transaction found");

7)PROPAGATION_NESTED

如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行

这是一个嵌套事务,使用JDBC 3.0驱动时,仅仅支持DataSourceTransactionManager作为事务管理器。需要JDBC 驱动的java.sql.Savepoint类。有一些JTA的事务管理器实现可能也提供了同样的功能。

使用PROPAGATION_NESTED,还需要把PlatformTransactionManager的nestedTransactionAllowed属性设为true; 
而nestedTransactionAllowed属性值默认为false;

  1. //事务属性 PROPAGATION_REQUIRED
  2. methodA(){
  3. doSomeThingA();
  4. methodB();
  5. doSomeThingB();
  6. }
  7. //事务属性 PROPAGATION_NESTED
  8. methodB(){
  9. ……
  10. }

如果单独调用methodB方法,则按REQUIRED属性执行。 
如果调用methodA方法,相当于下面的效果

  1. main(){
  2. Connection con = null;
  3. Savepoint savepoint = null;
  4. try{
  5. con = getConnection();
  6. con.setAutoCommit(false);
  7. doSomeThingA();
  8. savepoint = con2.setSavepoint();
  9. try
  10. methodB();
  11. }catch(RuntimeException ex){
  12. con.rollback(savepoint);
  13. }
  14. finally{
  15. //释放资源
  16. }
  17. doSomeThingB();
  18. con.commit();
  19. }
  20. catch(RuntimeException ex){
  21. con.rollback();
  22. }
  23. finally{
  24. //释放资源
  25. }
  26. }

当methodB方法调用之前,调用setSavepoint方法,保存当前的状态到savepoint。如果methodB方法调用失败,则恢复到之前保存的状态。但是需要注意的是,这时的事务并没有进行提交,如果后续的代码(doSomeThingB()方法)调用失败,则回滚包括methodB方法的所有操作。

嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。

PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:它们非常类似,都像一个嵌套事务,如果不存在一个活动的事务,都会开启一个新的事务。使用PROPAGATION_REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。两个事务不是一个真正的嵌套事务。同时它需要JTA事务管理器的支持。 
使用PROPAGATION_NESTED时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。DataSourceTransactionManager使用savepoint支持PROPAGATION_NESTED时,需要JDBC 3.0以上驱动及1.4以上的JDK版本支持。其它的JTA TrasactionManager实现可能有不同的支持方式。 
PROPAGATION_REQUIRED应该是我们首先的事务传播行为。它能够满足我们大多数的事务需求。

30分钟让你学会 Spring事务管理属性的更多相关文章

  1. Spring事务管理配置以及异常处理

    Spring事务管理配置: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=" ...

  2. 【Java EE 学习 52】【Spring学习第四天】【Spring与JDBC】【JdbcTemplate创建的三种方式】【Spring事务管理】【事务中使用dbutils则回滚失败!!!??】

    一.JDBC编程特点 静态代码+动态变量=JDBC编程. 静态代码:比如所有的数据库连接池 都实现了DataSource接口,都实现了Connection接口. 动态变量:用户名.密码.连接的数据库. ...

  3. Spring 事务管理高级应用难点剖析--转

    第 1 部分 http://www.ibm.com/search/csass/search/?q=%E4%BA%8B%E5%8A%A1&sn=dw&lang=zh&cc=CN& ...

  4. MyBatis6:MyBatis集成Spring事务管理(下篇)

    前言 前一篇文章<MyBatis5:MyBatis集成Spring事务管理(上篇)>复习了MyBatis的基本使用以及使用Spring管理MyBatis的事务的做法,本文的目的是在这个的基 ...

  5. Spring事务管理——其他的事务属性

    之前我们说过Spring事务管理中的事务的传播行为的属性.下面我们来说一下它的其他属性. 一.事务的隔离级别 1 .数据库事务并发问题.假设现在有两个事务:Transaction01和Transact ...

  6. 框架源码系列十一:事务管理(Spring事务管理的特点、事务概念学习、Spring事务使用学习、Spring事务管理API学习、Spring事务源码学习)

    一.Spring事务管理的特点 Spring框架为事务管理提供一套统一的抽象,带来的好处有:1. 跨不同事务API的统一的编程模型,无论你使用的是jdbc.jta.jpa.hibernate.2. 支 ...

  7. Spring事务管理的四种方式(以银行转账为例)

    Spring事务管理的四种方式(以银行转账为例) 一.事务的作用 将若干的数据库操作作为一个整体控制,一起成功或一起失败.   原子性:指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不 ...

  8. Spring事务管理的demo

    事务是逻辑上的一组操作,这组操作要么全部成功,要么全部失败,最为典型的就是银行转账的案例: A要向B转账,现在A,B各自账户中有1000元,A要给B转200元,那么这个转账就必须保证是一个事务,防止中 ...

  9. Spring事务管理笔记

    事务的目的就是要保证数据的高度完整性和一致性. 在实际的项目中,大多都是使用注解的方式来实现事物,这里也就简单记录下使用@Transactional方法和注意事项. 在xml中添加配置 1234567 ...

随机推荐

  1. javascript常用工具类util.js

    //如果大家想要补充,请留言 /** * 判断指定名称的复选框是否被选中 * * @param {} * chname复选框名称 */ function chkCheckCha(chname) { v ...

  2. HTML 009 CSS

    HTML 样式- CSS CSS (Cascading Style Sheets) 用于渲染HTML元素标签的样式.     Look! Styles and colors Manipulate Te ...

  3. url的主要功能是什么

    URL是Uniform Resource Loctor的缩写 URL作用:通过URL可以到达任何一个地方寻找需要的东西,比如文件.数据库.图像.新闻组等等,可以这样说,URL是Internet上的地址 ...

  4. Vagrant Docker Composer Yarn 国外资源下载慢或失败的问题

    1 问题 有时,我们请求国外资源时,下载巨慢,甚至失败.如: cd vue-devtools/ $ yarn install 进行到 cypress.... 时,可能失败. 2 解决 次日凌晨(7-8 ...

  5. Map集合类

    1.1 Map.clear方法——从Map集合中移除所有映射关系 public static void main(String[] args) {    Map map=new HashMap();  ...

  6. 微信小程序客服系统

    微信公众平台 点击 客服 添加 微信文档-接收消息和事件   在页面中使用 第三方客服系统 芝麻小客服 填写对应的 appid && AppSecret 等信息 微信文档-接收消息和事 ...

  7. 打造简单OS-总目录

    1-汇编写入引导区,虚拟机启动步骤 (了解即可) 2-开机BIOS初始化与MBR操作系统引导详解 (了解即可) 3-MBR引导区转移加载简单程序(突破512限制)(了解即可) 4-loader硬盘加载 ...

  8. jenkins之docker安装(jenkins/jenkins:lts)

    建议使用此镜像安装,不要使用官网推荐的jenkinsci/blueocean镜像,使用它构建node程序会出现问题. 1.宿主服务器jenkins_home目录权限 为了方便安装插件,升级,迁移,因此 ...

  9. OpenFOAM动网格技术介绍【转载】

    转载自:http://blog.sina.com.cn/s/blog_e256415d0101nfhp.html Chalmers大学的Andreu Oliver González对OpenFOAM中 ...

  10. Visual C++ 6.0精简绿色版下载及简单使用教程

    Visual C++ 6.0精简绿色版下载及简单使用教程 Microsoft Visual C++简介 Visual Studio 是微软公司推出的开发环境,Visual Studio 可以用来创建 ...