zoukankan      html  css  js  c++  java
  • 事务的传播机制

    1.什么是事务:

      事务是程序中一系列严密的操作,所有操作执行必须成功完成,否则在每个操作所做的更改将会被撤销,这也是事务的原子性(要么成功,要么失败)。

      数据库向用户提供保存当前程序状态的方法,叫事务提交(commit;当事务执行过程中,使数据库忽略当前的状态并回到前面保存的状态的方法叫事务回滚(rollback

    2.事务的传播机制

      以spring的事务传播机制为例子:

      Spring事务机制主要包括声明式事务和编程式事务,此处侧重讲解声明式事务,编程式事务在实际开发中得不到广泛使用,仅供学习参考。

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


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

      • propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
      • propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
      • propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
      • propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
      • propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
      • propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
      • propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作

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

      Java代码:

    复制代码
    //事务属性 PROPAGATION_REQUIRED 
    methodA{ 
      …… 
      methodB(); 
      …… 
    }
     
    //事务属性 PROPAGATION_REQUIRED 
    methodB{ 
       …… 
    }
    复制代码

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

      单独调用methodB方法:

    Java代码

    main{
      metodB(); 
    }

    相当于

    Java代码

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

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

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

      执行效果相当于:

    Java代码

    复制代码
    main{ 
         Connection con = null; 
        try{ 
             con = getConnection(); 
            methodA(); 
            con.commit(); 
        } 
        catch(RuntimeException ex){ 
            con.rollback(); 
        } 
        finally{ 
           closeCon(); 
        }  
    }                
    复制代码

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

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

    Java代码:

    复制代码
    //事务属性 PROPAGATION_REQUIRED 
    methodA(){ 
      methodB(); 
    }
     
    //事务属性 PROPAGATION_SUPPORTS 
    methodB(){ 
      …… 
    }
    复制代码

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

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

    Java代码:

    复制代码
    //事务属性 PROPAGATION_REQUIRED 
    methodA(){ 
        methodB(); 
    }
     
    //事务属性 PROPAGATION_MANDATORY 
    methodB(){ 
        …… 
    }
    复制代码

      当单独调用methodB时,因为当前没有一个活动的事务,则会抛出异常throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”);当调用methodA时,methodB则加入到methodA的事务中,事务地执行。

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

    Java代码:

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

    Java代码:

    main(){ 
      methodA(); 
    }

    相当于

    Java代码:

    复制代码
    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作为事务管理器。

    (5)PROPAGATION_NOT_SUPPORTED  总是非事务地执行,并挂起任何存在的事务。使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作为事务管理器。(代码示例同上,可同理推出)

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

    (7)PROPAGATION_NESTED如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。这是一个嵌套事务,使用JDBC 3.0驱动时,仅仅支持DataSourceTransactionManager作为事务管理器。需要JDBC 驱动的java.sql.Savepoint类。有一些JTA的事务管理器实现可能也提供了同样的功能。使用PROPAGATION_NESTED,还需要把PlatformTransactionManager的nestedTransactionAllowed属性设为true;而nestedTransactionAllowed属性值默认为false;

    Java代码:

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

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

    Java代码:

    复制代码
    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方法的所有操作。

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

      Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。假设 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。

  • 相关阅读:
    tee & vim
    xml ui
    Maven多模块项目打包前的一些注意事项(打包失败)
    jdk11+ spring5+ 报反射错误的屏蔽方法
    Plugin 'org.springframework.boot:spring-boot-maven-plugin:' not found 用IDEA创建springboot项目的时候遇到的一些坑
    如何在Zynq-7000上烧写PL Image
    python3 使用ssl安全连接发送邮件
    Centos7 启动指定docker容器报错
    Linux下多进程编程
    make的变量及赋值运算
  • 原文地址:https://www.cnblogs.com/cnndevelop/p/12311033.html
Copyright © 2011-2022 走看看