zoukankan      html  css  js  c++  java
  • Spring3之事务管理

    事务管理是企业应用开发中确保数据完整性和一致性的关键技术。对于并发和分布式坏境中从不可预期的错误中恢复来说,事务管理特别重要。Spring作为一个企业应用框架,在不同的事务管理API之上提供了一个抽象层。Spring支持编程式和声明式两种事务管理。编程式事务管理通过在业务方法中嵌入控制事务提交和回滚的事务管理代码来实现,自己定义事务提交和回滚的规则,因此这样代码会很多重复,不能为不同的应用程序启用和禁用事务管理;而声明式事务管理在大部分情况下比编程式事务管理好,它通过声明将事务管理代码从业务方法中分离出来,但其灵活性不如编程式事务管理。spring支持的事务属性包括传播行为、隔离级别、回滚规则、事务超时和事务的只读属性。

    事务是一系列作为单独工作单元处理的操作,其有四个关键属性: ACID

    原子性:事务是一个包含一系列的原子操作,事务的原子性确保这些操作全部完成或者完全无效。

    一致性:一旦事务的所有操作结束,事务就被提交。然后你的数据和资源将处于遵循业务规则的一致状态。

    隔离性:因为同时在相同数据集上可能有许多事务处理,每个事务应该与其他事务隔离,避免数据损坏。

    持久性:一旦事务完成,它的结果应该能承受任何系统错误。通常,事务的结果被写入持续性存储。

      我们可以使用Jdbc API如setAutoCommit,commit,rollback来进行事务管理,Spring的事务支持提供了一组技术独立的机制,包括事务管理器(PlatformTransactionManager)、事务模板(TransactionTemplate)和事务声明支持,以简化事务管理。

     Spring的事务抽象包括3个主要接口,即PlatformTransactionManager、TransactionDefinition、TransactionStatus。PlatformTransactionManager负责界定事务边界,TransactionDefinition负责定义事务相关属性,包括隔离级别、传播行为等,前者参照后则的属性定义来开启相关事务,事务开启后到事务结束的事务状态是由TransactionStatus负责,我们也可以通过TransactionStatus对事务进行有限的控制。

    1.选择一个事务管理器

      Spring的核心事务管理抽象基于PlatformTransactionManager接口,它封装了一组用于事务管理的技术独立方法。其提供了三种处理事务的方法:

    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException

      void commit(TransactionStatus status) throws TransactionException

    void rollback(TransactionStatus status) throws TransactionException

    其用于所有Spring事务管理器的通用接口,如果仅仅处理一个数据源并且用jdbc进行访问,使用DataSourceTransactionManager;如果JTA进行javaEE应用服务器上的事务管理,就应该使用JtaTransactionManager,其对于分布式事务是合适的。如果使用ORM框架访问数据库,应该选取对应的事务管理器,如HibernateTransactionManager或JpaTransactionManager。可以使用IoC容器来进行配置,如

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

    <property name="dataSource" ref="dataSource" />

         </bean> 

    2.编程式事务管理

      Spring的事务管理器提供一个技术独立的API,通过getTransaction()启动新事务,通过调用commit()和rollback()方法管理事务。类似如下代码:

    ...

    TransactionDefinition def = new DefaultTransactionDefintion();
    TransactionStatus status = transactionManager.getTransaction(def);
    ......
    transactionManager.commit(status);
    ...
    transactionManager.rollback(status);

    ... 

      配置如下:

     <beans>

             <bean id="transactionManager" class="org.springframework.jdbc.datasourdce.DataSourceTransactionManager">
                 <property name="dataSource" ref="dataSource" />
             </bean>    
            
             <bean id="xxxx" class="yyyyy>
                    ...
                    <property name="
    transactionManager" ref="transactionManager"/>
             </bean>
         </beans>

     

    Spring还提供了事务模板 TransactionTemplate帮助控制整个事务管理过程和事务异常处理。只要在实现TransactionCallback<T>接口的回调类里封装代码,并传递给TransactionTemplate的execute方法执行就可以了。使用如下:

    ...

    private TransactionTemplate transactionTemplate ;
    ...
    transactionTemplate.execute(new TransactionCallbackWithoutResult(){
            protected void doInTransactionWithoutResult(TransactionStatus status){
            ....
             });

    ... 

    如果想使用IoC容器注入transactionTemplate的话,则为其提供set方法,配置如下:

           <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate> 

             <property name="transactionManager" ref="transactionManager" />
    </bean>
          <bean id="xxxx" class="yyyyy">
         ...
         <property name="transactionTemplate" ref="transactionTemplate"/>
          ...

     </bean> 

    3.声明式事务管理

     Spring 2.x之后使用AOP方法进行声明式管理事务,通过tx schema中定义的<tx:advice>元素来配置,因此事先要在xml文件中加入schema的约束。该方式对应基于Schema的aop配置,需在<aop:config>元素中声明一个通知器(Advisor),将通知与切入点关联。如下配置:

    <tx:advice id="xyzTxAdvice" transaction-manager="transactionManager">

            <tx:attributes>
                 <tx:method name="xyz"/>
            </tx:attributes>    
        </tx:advice>
    ...
        <aop:config>
            ...
         <aop:advisor advice-ref="xyzTxAdvice" pointcut-ref="zyz" />
            ...
        </aop:config>
        <bean id="transactionManager" class="org.springframework.jdbc.datasourdce.DataSourceTransactionManager">
                <property name="dataSource" ref="dataSource" />
        </bean>    

      除此之外,Spring允许仅通过@Transactional注解你的事务性方法,并且启用<tx:annotation-driven>元素声明事务。最好只注解公开方法。

    配置如下:

          <tx:annotation-driven transaction-manager="transactionManager"/>

    <bean id="transactionManager" class="org.springframework.jdbc.datasourdce.DataSourceTransactionManager">
                <property name="dataSource" ref="dataSource" />

    </bean>     

    4.设置事务属性

    传播属性 事务传播在事务性方法被另一个方法调用时发生的,其由事务属性propagation指定,spring定义了7中传播行为,这些行为在org.springframework.transaction.TransactionDefinition接口中定义。如下:

    REQUIRED:如果现有的事务正在进行,当前方法应该在这个事务中运行,否则重新开启一个事务,并在自己的事务中运行。 

    REQUIRED_NEW:当前方法必须启动新事务,并在自己的事务中运行,如果现有的事务正在运行,则应该挂起。

    SUPPORTS:如果现有事务正在运行,当前方法应该运行在该事务中,否则,它没有必要运行在事务中。

    NOT_SUPPORTED:当前方法不应该运行在事务中。如果现在事务正在进行,它应该挂起。

    MANDATORY:当前方法必须运行一个事务中。如果没有事务在进行中,将抛出一个异常。

    NEVER:当前方法不应该运行于事务中。如果现有事务在运行中,将抛出异常。

    NESTED: 现有事务不应该运行于事务中。如果现有事务在运行中,将抛出异常。

    这些传播行为,并不是所有的事务管理器都支持的,而且不同的隔离级别,也会限制传播行为。 传播属性的配置三种如下:

      <tx:advice>

         <tx:attributes>
             <tx:method name="..." propagation="REQUIRED"/>
         </tx:attributes>
        </tx:advice>

         <property name="transactionAttributes">
          <props>
                 <prop key="...">PROPAGATION_REQUIRED</prop>
           </props>    
         </property>

    DefaultTransactionDefinition  def = new DefaultTransactionDefinition();
    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

    隔离属性 当相同应用或者不同应用的多个失误同时在同一数据集上操作时,可能发生许多不可预期的问题,必须至少能够幻想隔离的预期方式。并发产生的问题有:

    脏数据:两个事务,后一个事务读取到了前一个事物还没有提交的字段,若前一个事务出现回滚,则后一个事务读取的字段是无效的。

    不可重复读: 两个事务,前一个事务读取一个字段,后一个事务更新了这个字段,前一事务再去读相同的字段,数据不一致。

    幻读:两个事务,前一个事务读取一些数据行,后一个事务新添加了数据行,前一个事务再去读,读到更多的数据行。

    更新丢失:两个事务,更新同一个字段数据,前一个事务进行提交,后一个事务之后进行提交,前一个事务的更新会被覆盖掉。

     理论上应该完全隔离,但是会对性能造成很大的影响,因为事务是顺序进行的。Spring支持5中隔离级别,这些级别在org.springframework.transaction.transaction.TransactionDefinition 接口中定义。如下:

    DEFAULT:使用低层数据库的默认隔离级别,对于大多数数据库来说,默认的隔离级别是READ_COMMITTED

    READ_UNCOMMITTED:允许事务读取其他事物未提交修改。可能发生脏读、不可重复读和幻读,该级别下读操作不加S锁

    READ_COMMITTED:仅允许事务读取已经提交的修改。能避免脏读,但不可重复读和幻读可能发生,该级别下读操作需要加S锁,但在语句执行完之后释放S锁

    REPEATABLE_READ:确保食物能够多次从一个字段读到相同值,在其他事务的更新被禁止。能避免脏读和不可重复读,幻读可能发生,该级别下读操作需要加S锁,但在事务提交之后才释放S锁。

    SERIALIZABLE:确保一个事物能从表中多次读取相同的行。在事务期间,其他事务的行为均被禁止。能避免所有并发问题,但性能低,在该级别下又添加了一个范围锁,保证事务的两次查询一样。 

     配置如下:

    <tx:advice ...>

         <tx:attributes>
             <tx:method name="*" isolation="REPEATABLE_READ"/>    
            </tx:attributes>
        </tx:advice>

        <property name="transactionAttributes">
         <props>
             <prop key="...">
                     PROPAGATION_REQUIRED,ISOLATION_REPEATABLE_READ    
               </prop>    
          </props>
        </property>

    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    def.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);

    Rollback、超时和只读事务属性 我们有时候需要自己设置回滚异常,而不是遇到异常或者错误的时候才让事务回滚。通过设置rollback事务属性来指定。若不指定则按照默认规则处理。如果事务长时间占用资源势必会影响性能,因此同样需要优化和改进引用性能,timeout事务属性表示事务在被强制回滚之前存活的时间,read-only属性表示该事务仅仅读取而不更新数据。这些配置如下:

    可直接在@Transactional注解中设置rollbackFor和noRollbackFor属性定义,属性被声明为Class[],可以指定超过一个异常,也支持直接设置timeout和readOnly。

    其他如下:

      <tx:advice ...>

          <tx:attributes>
            <tx:method name="...">
                rollback-for="java.io.IOException"
                no-rollback-for="java.lang.ArithmeticException"/>    
                timeout="30"
                read-only="true"
            </tx:method>    
           </tx:attributes>
       </tx:advice>

       <property name="transactionAttributes">
         <prop key="...">
               PROPAGATION_REQUIRED,-java.io.IOException,+java.lang.ArithmeticException,timeout_30,readOnly
         </prop>    
       </property>

    RuleBasedTransactionAttribute attr = new RuleBasedTransactionAttribute();
    attr.getRollbackRules().add(new RollbackRuleAttribute(IOException.class));
    attr.getRollbackRules().add(new NoRollbackAttribute(ArithmeticException));
    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    def.setTimeout(30);
    def.setReadOnly(true);
  • 相关阅读:
    “sockaddr”: “struct”类型重定义的错误的解决办法《转》
    2019年车险
    tinylib
    命令行利用ffmpeg实现rtmp推流《转》
    Inno setup 判断系统32位还是64位
    vs2015编译OBS-Studio21.1.12
    啃OBS源码-界面汉字
    百年孤独人物关系1
    windows命令行查看文件MD5
    python 玩爬虫安装了一大堆第三方库
  • 原文地址:https://www.cnblogs.com/kingcucumber/p/2872520.html
Copyright © 2011-2022 走看看