`
peigen
  • 浏览: 76154 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Spring事务管理小结

    博客分类:
  • Java
阅读更多

Spring中嵌套事务的方式可以在org.springframework.transaction.TransactionDefinition中找到。

PROPAGATION_REQUIRED -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED -- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
前六个策略类似于EJB CMT,第七个(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。
它要求事务管理器或者使用JDBC 3.0 Savepoint API提供嵌套事务行为(如Spring的DataSourceTransactionManager)

我们平时常用的是PROPAGATION_REQUIRED,也就说说嵌套的2个事务实际上一个事务,如果内部事务抛出了异常,那么外部事务跟着一起回滚。

在收费中同时也配置了PROPAGATION_REQUIRES_NEW,PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全commited 或rolled back而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行,外部事务的提交或回滚也不受内部事务的影响。

在xml配置文件中如下:

<bean id="goldenTransactionTemplate"
  class="org.springframework.transaction.support.TransactionTemplate">
  <property name="transactionManager"><ref bean="goldenTransactionManager"/></property>
 </bean>

<!- 新事务模板: 该事务模板中的处理将在新事务中运行 - >
 <bean id="newTransTransactionTemplate"
  class="org.springframework.transaction.support.TransactionTemplate">
  <property name="propagationBehaviorName" value="PROPAGATION_REQUIRES_NEW" />
 </bean>
 

 

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

 

TransactionDefinition
public interface TransactionDefinition {
    int getPropagationBehavior();
    int getIsolationLevel();
    int getTimeout();
    boolean isReadOnly();
}



getTimeout()方法,它返回事务必须在多少秒内完成。
isReadOnly(),事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的。
getIsolationLevel()方法返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据。

在TransactionDefinition接口中定义了五个不同的事务隔离级别
ISOLATION_DEFAULT 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应
ISOLATION_READ_UNCOMMITTED 这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
  例如:
  Mary的原工资为1000,财务人员将Mary的工资改为了8000,但未提交事务

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


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

     

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



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

     

    //con1
      con1.rollback();


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

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

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

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

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



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

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



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

         

        //con1
        select salary from employee empId ="Mary";



        在一个事务中前后两次读取的结果并不致,导致了不可重复读。
        使用ISOLATION_REPEATABLE_READ可以避免这种情况发生。

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

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

         

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

        共读取10条记录

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

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



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

           

          //con1
          select * from employee where salary =1000;



          共读取到了11条记录,这就产生了幻像读。
          ISOLATION_SERIALIZABLE能避免这样的情况发生。但是这样也耗费了最大的资源。

          getPropagationBehavior() 返回事务的传播行为,由是否有一个活动的事务来决定一个事务调用。

          在TransactionDefinition接口中定义了七个事务传播行为

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

            //事务属性 PROPAGATION_REQUIRED
            methodA{
            ……
            methodB();
            ……
            }
            
            //事务属性 PROPAGATION_REQUIRED
            methodB{
               ……
            }


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

            单独调用methodB方法

             

            main{
              metodB();
            }


            相当于

             

            Main{
            Connection con=null;
            
               rry{
                  con = getConnection();
                  con.setAutoCommit(false);
            //方法调用
            methodB();
            //提交事务
            con.commit();
            }
            Catch(RuntimeException ex){
              //回滚事务
              con.rollback();  
            }
            finally{
              //释放资源
              closeCon();
            }
            }


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

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

            执行效果相当于

             

            main{
               Connection con = null;
               try{
                  con = getConnection();
                  methodA();
                  con.commit();
            }
            cathc(RuntimeException ex){
             con.rollback();
            }
            finally{
              closeCon();
            } 
            }


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

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

             

            //事务属性 PROPAGATION_REQUIRED 
            methodA(){
              methodB();
            }
            
            //事务属性 PROPAGATION_SUPPORTS 
            methodB(){
              ……
            }


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

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

              //事务属性 PROPAGATION_REQUIRED 
              methodA(){
                methodB();
              }
              
              //事务属性 PROPAGATION_MANDATORY 
              methodB(){
                ……
              }


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

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

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

               

              //事务属性 PROPAGATION_REQUIRED 
              methodA(){
                doSomeThingA();
              methodB();
              doSomeThingB();
              }
              
              //事务属性 PROPAGATION_REQUIRES_NEW 
              methodB(){
                ……
              }


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

              当调用methodA时

               

              main(){
                methodA();
              }

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

               

              main(){
               TransactionManager tm = null;
              try{
                //获得一个JTA事务管理器
                 tm = getTransactionManager();
                 tm.begin();//开启一个新的事务
                 Transaction ts1 = tm.getTransaction();
                 doSomeThing();
                 tm.suspend();//挂起当前事务
                 try{
                   tm.begin();//重新开启第二个事务
                   Transaction ts2 = tm.getTransaction();
                   methodB();
                   ts2.commit();//提交第二个事务
                   
                 }
                Catch(RunTimeException ex){
                   ts2.rollback();//回滚第二个事务
                }
                finally{
                  //释放资源
                }
                 //methodB执行完后,复恢第一个事务
                 tm.resume(ts1);
              doSomeThingB();
                 ts1.commit();//提交第一个事务
              }
              catch(RunTimeException ex){
                ts1.rollback();//回滚第一个事务
              }
              finally{
                //释放资源
              }
              }


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

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

               

              //事务属性 PROPAGATION_REQUIRED 
              methodA(){
                doSomeThingA();
              methodB();
              doSomeThingB();
              }
              
              //事务属性 PROPAGATION_NOT_SUPPORTED 
              methodB(){
                ……
              }


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

               

              main(){
               TransactionManager tm = null;
              try{
                //获得一个JTA事务管理器
                 tm = getTransactionManager();
                 tm.begin();//开启一个新的事务
                 Transaction ts1 = tm.getTransaction();
                 doSomeThing();
                 tm.suspend();//挂起当前事务
                   methodB();
                 //methodB执行完后,复恢第一个事务
                 tm.resume(ts1);
              doSomeThingB();
                 ts1.commit();//提交第一个事务
              }
              catch(RunTimeException ex){
                ts1.rollback();//回滚第一个事务
              }
              finally{
                //释放资源
              }
              }

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

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

               

              //事务属性 PROPAGATION_REQUIRED 
              methodA(){
                doSomeThingA();
              methodB();
              doSomeThingB();
              }
              
              //事务属性 PROPAGATION_NEVER 
              methodB(){
                ……
              }

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


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

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

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

               

              //事务属性 PROPAGATION_REQUIRED 
              methodA(){
                doSomeThingA();
              methodB();
              doSomeThingB();
              }
              
              //事务属性 PROPAGATION_NESTED
              methodB(){
                ……
              }


              如果单独调用methodB方法,则按REQUIRED属性执行。

              如果调用methodA方法,相当于下面的效果

               

              main(){
              Connection con = null;
              Savepoint savepoint = null;
              try{
                con = getConnection();
                con.setAutoCommit(false);
                doSomeThingA();
                savepoint = con2.setSavepoint();
                try
                    methodB();
                }catch(RuntimeException ex){
                   con.rollback(savepoint);
                }
                finally{
                  //释放资源
                }
              
                doSomeThingB();
                con.commit();
              }
              catch(RuntimeException ex){
                con.rollback();
              }
              finally{
                //释放资源
              }
              }

              当 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应该是我们首先的事务传播行为。它能够满足我们大多数的事务需求。

              分享到:
              评论

              相关推荐

                spring事务管理几种方式代码实例

                spring事务管理几种方式代码实例:涉及编程式事务,声明式事务之拦截器代理方式、AOP切面通知方式、AspectJ注解方式,通过不同方式实例代码展现,总结spring事务管理的一般规律,从宏观上加深理解spring事务管理特性...

                Spring中事务的传播属性详解

                Spring中事务的传播属性详解,Spring中事务的传播属性详解

                Spring事务五种不同的代理配置

                Spring事务配置的五种方式 ,根据代理机制的不同,总结了五种Spring事务的配置方式.

                在Spring中使用JTA事务管理

                在Spring中使用JTA事务管理 1 通过集成JOTM,直接在Spring中使用JTA事务 1.1. 将JOTM以下类库添加到类路径中 1.2. 编写JOTM配置文件,放到类路径下 1.3. 在MySQL上建立两个数据库 1.4. 在Spring配置文件中配置JOTM ...

                关于spring声明式事务管理异常处理的测试和小结

                spring声明式事务管理异常处理的测试

                spring的事务管理

                csdn博客竟然也出问题,好不容总结一下,竟然发不了博客。依稀csdn! 1、spring与hibernate事务管理 2、aop 3、事务管理,声明式。 3、事务传递特性说明

                spring事务总结.docx

                事务失效、事务回滚、大事务问题、编程式事务

                Spring事务不生效.pdf

                Spring事务不生效,spring项目中常见事务不生效总结

                spring杂谈 作者zhang KaiTao

                1.1 Spring事务处理时自我调用的解决方案及一些实现方式的风险 1.2 我对AOP的理解 1.3 Spring开闭原则的表现-BeanPostProcessor的扩展点-1 1.4 我对IoC/DI的理解 1.5 SpringMVC + spring3.1.1 + hibernate4.1.0 集成...

                Spring事务总结

                spring配置事务 项目总结出来的 公司多年开发经验 使用 方便

                Spring事务配置的五种方式

                Spring事务配置的五种方式,学习总结

                spring声明事务,编程事务实现

                相信对于很多初学者或者使用者,都了解spring事务,但是具体细节,实现方式,都没有系统的概念或者无从下手,本人周末闲来无事,坐下总结,时间紧促,写的不周全的地方还请提出宝贵意见。

                Spring事务配置的五种方法(二)[总结].pdf

                Spring事务配置的五种方法(二)[总结].pdf

                Spring的事务10种常见失效场景总结.zip

                Spring的事务10种常见失效场景总结.zip

                Spring + Hibernate + Struts 事务配置小例子(带提示框等小技巧)

                前几天搞 Spring + Hibernate + Struts 事务配置 ,网上找了好多资料,不过好无语,大多都是 Ctrl + V,浪费俺的宝贵时间 现在我总结配出一套,给大家参考参考,可能有不足,请大家多多交流。 附:内有弹出...

                Spring总结.xmind

                包含Spring简介,AOP,SpringMVC,集成JDBC,事务,ORM框架,Spring集成知识点总结,以思维导图的形式展示,xmind文件。

                Spring攻略PDF版

                 第8章 Spring中的事务管理   第9章 Spring对ORM的支持   第10章 Spring MVC框架   第11章 整合Spring与其他Web框架   第12章 Spring对测试的支持  第三部分 高级主题  第13章 Spring ...

                全网最热spring问题总结.pdf

                总结了常见的spring面试题,方便学习者查漏补缺。 使用 Spring 框架的好处是什么? 轻量: Spring 是轻量的, 基本的版本大约 2MB。 控制反转: Spring 通过控制反转实现了松散耦合, 对象们给出它们的依 赖, ...

                spring.doc

                小结: 47 面向接口编程: 47 4 面向切面编程 52 4.1 代理模式 52 代理模式拓展: 52 4.1.1 JDK动态代理 58 JDK动态代理拓展: 59 4.1.2 CGLIB做代理 66 CGLIB动态代理拓展: 68 4.1.3 Spring的动态代理 71 4.2 AOP...

              Global site tag (gtag.js) - Google Analytics