Spring-AOP事物管理

作者: Tian_Peng | 来源:发表于2020-01-20 00:49 被阅读0次

    1.概述

    Spring 中的事务主要是利用 Aop 思想,简化事务的配置

    2.核心接口

    Spring事务管理的实现有许多细节,如果对整个接口框架有个大体了解会非常有利于我们理解事务。
    下面通过讲解Spring的事务接口来了解Spring实现事务管理的具体策略。
    Spring事务管理涉及的接口的联系如下:

    2.1事物管理器

    Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。

    Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。
    此接口的内容如下:

    package org.springframework.transaction;
    
    import org.springframework.lang.Nullable;
    
    /**
     * This is the central interface in Spring's transaction infrastructure.
     * Applications can use this directly, but it is not primarily meant as API:
     * Typically, applications will work with either TransactionTemplate or
     * declarative transaction demarcation through AOP.
     *
     * <p>For implementors, it is recommended to derive from the provided
     * {@link org.springframework.transaction.support.AbstractPlatformTransactionManager}
     * class, which pre-implements the defined propagation behavior and takes care
     * of transaction synchronization handling. Subclasses have to implement
     * template methods for specific states of the underlying transaction,
     * for example: begin, suspend, resume, commit.
     *
     * <p>The default implementations of this strategy interface are
     * {@link org.springframework.transaction.jta.JtaTransactionManager} and
     * {@link org.springframework.jdbc.datasource.DataSourceTransactionManager},
     * which can serve as an implementation guide for other transaction strategies.
     *
     * @author Rod Johnson
     * @author Juergen Hoeller
     * @since 16.05.2003
     * @see org.springframework.transaction.support.TransactionTemplate
     * @see org.springframework.transaction.interceptor.TransactionInterceptor
     */
    public interface PlatformTransactionManager extends TransactionManager {  
        // 由TransactionDefinition得到TransactionStatus对象
        TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException; 
        // 提交
        Void commit(TransactionStatus status) throws TransactionException;  
        // 回滚
        Void rollback(TransactionStatus status) throws TransactionException;  
    } 
    

    从这里可知,具体的事务管理机制对Spring来说是透明的,它并不关心那些,那些是对应各个平台需要关心的。
    所以Spring事务管理的一个优点就是为不同的事务API提供一致的编程模型,如JDBC、Hibernate、JPA、JTA。
    下面分别介绍各个平台框架实现事务管理的机制。

    • JDBC事物
      如果应用程序中直接使用JDBC来进行持久化,DataSourceTransactionManager会为你处理事务边界。
      为了使用DataSourceTransactionManager,需要使用如下的XML将其装配到应用程序的上下文定义中:
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    

    实际上,DataSourceTransactionManager是通过调用java.sql.Connection来管理事务,而后者是通过DataSource获取到的。
    通过调用连接的commit()方法来提交事务,同样,事务失败则通过调用rollback()方法进行回滚。

    • Hibernate事务
      如果应用程序的持久化是通过Hibernate实现的,那么需要使用HibernateTransactionManager。
      对于Hibernate3,需要在Spring上下文定义中添加如下的<bean>声明:
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    

    sessionFactory属性需要装配一个Hibernate的session工厂。
    HibernateTransactionManager的实现细节是它将事务管理的职责委托给org.hibernate.Transaction对象,而后者是从Hibernate Session中获取到的。
    当事务成功完成时,HibernateTransactionManager将会调用Transaction对象的commit()方法,反之,将会调用rollback()方法

    • Java持久化API事务(JPA)
      Hibernate多年来一直是事实上的Java持久化标准,但是现在Java持久化API作为真正的Java持久化标准进入大家的视野。
      如果你计划使用JPA的话,那你需要使用Spring的JpaTransactionManager来处理事务。
      你需要在Spring中这样配置JpaTransactionManager:
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
    

    JpaTransactionManager只需要装配一个JPA实体管理工厂(javax.persistence.EntityManagerFactory接口的任意实现)。
    JpaTransactionManager将与由工厂所产生的JPA EntityManager合作来构建事务。

    • Java原生API事务
      如果你没有使用以上所述的事务管理,或者是跨越了多个事务管理源(比如两个或者是多个不同的数据源),你就需要使用JtaTransactionManager:
    <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
        <property name="transactionManagerName" value="java:/TransactionManager" />
    </bean>
    

    JtaTransactionManager将事务管理的责任委托给javax.transaction.UserTransaction和javax.transaction.TransactionManager对象。
    其中事务成功完成通过UserTransaction.commit()方法提交,事务失败通过UserTransaction.rollback()方法回滚。

    2.2 基本事务属性的定义

    上面讲到的事务管理器接口PlatformTransactionManager通过getTransaction(TransactionDefinition definition)方法来得到事务,这个方法里面的参数是TransactionDefinition类,这个类就定义了一些基本的事务属性。
    那么什么是事务属性呢?事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。
    事务属性包含了5个方面,如图所示:

    2.2.1事物传播行为

    事务的传播行为(propagation behavior):当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。
    例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
    Spring定义了七种传播行为:

    传播行为 含义
    PROPAGATION_REQUIRED 表示当前方法必须运行在事务中。
    如果当前事务存在,方法将会在该事务中运行。否则,会启动一个新的事务
    PROPAGATION_SUPPORTS 表示当前方法不需要运行在事务中。
    但是如果存在当前事务的话,那么该方法会在这个事务中运行
    PROPAGATION_MANDATORY 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
    PROPAGATION_REQUIRED_NEW 表示当前方法必须运行在它自己的事务中,一个新的事务将被启动。
    如果存在当前事务,在该方法执行期间,当前事务会被挂起。
    如果使用JTATransactionManager的话,则需要访问TransactionManager
    PROPAGATION_NOT_SUPPORTED 表示该方法不应该运行在事务中。
    如果存在当前事务,在该方法运行期间,当前事务将被挂起。
    如果使用JTATransactionManager的话,则需要访问TransactionManager
    PROPAGATION_NEVER 表示当前方法不应该运行在事务上下文中。
    如果当前正有一个事务在运行,则会抛出异常
    PROPAGATION_NESTED 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。
    嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。
    注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务
    具体实例:
    • PROPAGATION_REQUIRED
      如果存在一个事务,则支持当前事务。
      如果没有事务则开启一个新的事务。
    //事务属性 PROPAGATION_REQUIRED
    methodA{
        ……
        methodB();
        ……
    }
    

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

    main{ 
        metodB(); 
    } 
    

    相当于:

    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,执行效果相当于:

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

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

    • 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(){
        ……
    }
    

    调用A方法:

    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_NOT_SUPPORTED,也需要使用JtaTransactionManager作为事务管理器。
    • PROPAGATION_NEVER
      总是非事务地执行,如果存在一个活动事务,则抛出异常。
    • 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_REQUIRES_NEW 启动一个新的,不依赖于环境的 “内部” 事务。这个事务将被完全 commited 或 rolled back而不依赖于外部事务,它拥有自己的隔离范围,自己的锁等等。当内部事务开始执行时,外部事务将被挂起,内务事务结束时,外部事务将继续执行。

    另一方面, PROPAGATION_NESTED 开始一个“嵌套的”事务,它是已经存在事务的一个真正的子事务。
    嵌套事务开始执行时, 它将取得一个savepoint,如果这个嵌套事务失败,我们将回滚到此 savepoint。
    潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交。

    由此可见PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于PROPAGATION_REQUIRES_NEW 完全是一个新的事务,而 PROPAGATION_NESTED 则是外部事务的子事务,如果外部事务 commit,嵌套事务也会被 commit,这个规则同样适用于 roll back。

    PROPAGATION_REQUIRED应该是我们首先的事务传播行为。它能够满足我们大多数的事务需求。

    2.2.2隔离级别

    事务的第二个维度就是隔离级别(isolation level)
    隔离级别定义了一个事务可能受其他并发事务影响的程度。
    不考虑事物隔离级别会导致的问题:
    在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务。并发虽然是必须的,但可能会导致一下的问题

    • 脏读(Dirty reads)
      脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。
      如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
    • 不可重复读(Nonrepeatable read)
      不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。
      这通常是因为另一个并发事务在两次查询期间进行了更新。
    • 幻读(Phantom read)
      幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。
      在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。

    以上在以前的文章中我们已经说过,不再赘述。

    Spring中事物的隔离级别设置
    隔离级别 含义
    ISOLATION_DEFAULT 使用后端数据库默认的隔离级别
    ISOLATION_READ_UNCOMMITTED 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
    ISOLATION_READ_COMMITTED 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
    ISOLATION_REPEATABLE_READ 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
    ISOLATION_SERIALIZABLE 最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的
    2.2.3只读

    Spring中事务的第三个属性是它是否为只读事务。
    如果事务只对后端的数据库进行该操作,数据库可以利用事务的只读特性来进行一些特定的优化。
    通过将事务设置为只读,你就可以给数据库一个机会,让它应用它认为合适的优化措施。
    这里需要注意:Spring中事物readOnly的定义,并不是不能在事务中进行修改等DML操作,它只是一个“暗示”,提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。
    只读事务仅仅是一个性能优化的推荐配置而已,并非强制你非要这样处理不可。
    结合源码的一个解读分析具体是通过什么方式来进行优化:
    如果是只读事务,Spring会将transactionContext的isFlushModeNever 设置为true;
    当事务被标识为只读事务时,Spring可以对某些可以针对只读事务进行优化的资源就可以执行相应的优化措施,上面Spring告之hibernate的session在只读事务模式下不用尝试检测和同步持久对象的状态的更新。
    总结:
    如果在使用事务的情况下,所有操作都是读操作,那建议把事务设置成只读事务,或者事务的传播途径最好能设置为 supports (运行在当前的事务范围内,如果当前没有启动事务,那么就不在事务范围内运行)或者 not supports (不在事务范围内执行,如果当前启动了事务,那么挂起当前事务),这样不在事务下,就不会调用transactionContext.managedFlush(); 方法。
    所有只读事务与读写事务的比较大的运行性能区别就是只读事务避免了Hibernate的检测和同步持久对象的状态的更新,提升了运行性能。

    2.2.4事务超时

    为了使应用程序很好地运行,事务不能运行太长的时间。
    因为事务可能涉及对后端数据库的锁定,所以长时间的事务会不必要的占用数据库资源。
    事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。
    默认设置为底层事务系统的超时值,如果底层数据库事务系统没有设置超时值,那么就是none,没有超时限制。
    推荐阅读文章:http://www.heartthinkdo.com/?p=910

    2.2.5 回滚规则

    事务五边形的最后一个方面是一组规则,这些规则定义了哪些异常会导致事务回滚而哪些不会。
    默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚(这一行为与EJB的回滚行为是一致的)
    但是你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。
    同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常。
    2.3 事务状态
    上面讲到的调用PlatformTransactionManager接口的getTransaction()的方法得到的是TransactionStatus接口的一个实现,这个接口的内容如下:

    public interface TransactionStatus{
        boolean isNewTransaction(); // 是否是新的事物
        boolean hasSavepoint(); // 是否有恢复点
        void setRollbackOnly();  // 设置为只回滚
        boolean isRollbackOnly(); // 是否为只回滚
        boolean isCompleted; // 是否已完成
    } 
    

    可以发现这个接口描述的是一些处理事务提供简单的控制事务执行和查询事务状态的方法,在回滚或提交的时候需要应用对应的事务状态。

    3.编程式事物

    Spring提供了对编程式事务和声明式事务的支持,编程式事务允许用户在代码中精确定义事务的边界,而声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦。
    简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现。

    Spring提供两种方式的编程式事务管理,分别是:使用TransactionTemplate和直接使用PlatformTransactionManager。

    因为日常开发中极少使用编程式事务,不再做介绍,想要了解的同学自行百度

    4.声明式事物

    声明式事务基于SpringAOP,它是SpringAOP的典型应用场景,Spring中声明式事物的配置方式常用的有两种:

    • 基于XML的声明式事物配置
    • 基于注解的声明式事物配置

    4.1基于XML的声明式事物配置

    XML中配置事务一共分为三个步骤:

    • 1.配置TransactionManager
    <!-- 非Tomcat应用服务器环境下推荐使用JTA事务管理器,Tomcat应用服务器环境下推荐使用纯粹的JDBC事务管理器。 -->
    <!--JTA事务管理器,使用该选项时候,必须使用一个容器管理的DataSource(即使用应用服务器JNDI数据源) -->
    <!--
    <bean id="txManager"
        class="org.springframework.transaction.jta.JtaTransactionManager" />
     -->
    <!--纯粹的JDBC事务管理器, 框架使用JTA事务,该选项一般不打开,缺省下打开是考虑支持所有的应用服务器 -->
    <bean id="txManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!--纯粹的Hibernate的本地事务,框架使用JTA事务 -->
    <!--
        <bean id="txManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" /> </bean>
    -->
    
    • 2.配置事务要处理的方法
    <!-- spring声明式事务的配置,以下为spring的AOP事务管理的增强部分 -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <!-- 需要由交给spring的aop来进行代理的方法的集合,如果应用有自己的方法需有由spring来进行事务控制必须添加方法-->
            <!--
                PROPAGATION_REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
                PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
                PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
                PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
                PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
                PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
                PROPAGATION_NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
             -->
            <!-- 读取数据方法,一般采用只读事务-->
            <tx:method name="get*" isolation="DEFAULT"
                       propagation="SUPPORTS" read-only="true"/>
            <tx:method name="load*" isolation="DEFAULT"
                       propagation="SUPPORTS" read-only="true"/>
            <tx:method name="select*" isolation="DEFAULT"
                       propagation="SUPPORTS" read-only="true"/>
            <tx:method name="list*" isolation="DEFAULT"
                       propagation="SUPPORTS" read-only="true"/>
            <tx:method name="query*" isolation="DEFAULT"
                       propagation="SUPPORTS" read-only="true"/>
            <tx:method name="criteria*" isolation="DEFAULT"
                       propagation="SUPPORTS" read-only="true"/>
    
    
            <!--其他方法,如save,update,insert等对数据库进行写入操作的方法,当产生ServiceException进行回滚 -->
            <tx:method name="init*" isolation="DEFAULT"
                       read-only="false" propagation="REQUIRED"
                       rollback-for="ServiceException"/>
            <tx:method name="insert*" isolation="DEFAULT"
                       read-only="false" propagation="REQUIRED"
                       rollback-for="ServiceException"/>
            <tx:method name="update*" isolation="DEFAULT"
                       read-only="false" propagation="REQUIRED"
                       rollback-for="ServiceException"/>
            <tx:method name="save*" isolation="DEFAULT"
                       read-only="false" propagation="REQUIRED"
                       rollback-for="ServiceException"/>
            <tx:method name="add*" isolation="DEFAULT" read-only="false"
                       propagation="REQUIRED" rollback-for="ServiceException"/>
            <tx:method name="create*" isolation="DEFAULT"
                       read-only="false" propagation="REQUIRED"
                       rollback-for="ServiceException"/>
            <tx:method name="del*" isolation="DEFAULT" read-only="false"
                       propagation="REQUIRED" rollback-for="ServiceException"/>
            <tx:method name="remove*" isolation="DEFAULT"
                       read-only="false" propagation="REQUIRED"
                       rollback-for="ServiceException"/>
            <tx:method name="process" isolation="DEFAULT" read-only="false"
                       propagation="REQUIRED" rollback-for="ServiceException"/>
        </tx:attributes>
    </tx:advice>
    

    注意:一旦配置了方法名称规则之后,service 中的方法一定要按照这里的名称规则来,否则事务配置不会生效

    • 3.配置 Aop
    <aop:config>
        <aop:pointcut id="serviceOperation"
                      expression="execution(* com.tp..*Service.*(..))"/>
        <aop:advisor pointcut-ref="serviceOperation"
                     advice-ref="txAdvice"/>
    </aop:config>
    

    完整的配置信息:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
                            http://www.springframework.org/schema/tx
                            http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
                            http://www.springframework.org/schema/aop
                            http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
    
        <!-- 非Tomcat应用服务器环境下推荐使用JTA事务管理器,Tomcat应用服务器环境下推荐使用纯粹的JDBC事务管理器。 -->
        <!--JTA事务管理器,使用该选项时候,必须使用一个容器管理的DataSource(即使用应用服务器JNDI数据源) -->
        <!--
        <bean id="txManager"
            class="org.springframework.transaction.jta.JtaTransactionManager" />
         -->
        <!--纯粹的JDBC事务管理器, 框架使用JTA事务,该选项一般不打开,缺省下打开是考虑支持所有的应用服务器 -->
        <bean id="txManager"
              class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
        <!--纯粹的Hibernate的本地事务,框架使用JTA事务 -->
        <!--
            <bean id="txManager"
            class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory" /> </bean>
        -->
    
        <!-- spring声明式事务的配置,以下为spring的AOP事务管理的增强部分 -->
        <tx:advice id="txAdvice" transaction-manager="txManager">
            <tx:attributes>
                <!-- 需要由交给spring的aop来进行代理的方法的集合,如果应用有自己的方法需有由spring来进行事务控制必须添加方法-->
                <!--
                    PROPAGATION_REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
                    PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
                    PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
                    PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
                    PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
                    PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
                    PROPAGATION_NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
                 -->
                <!-- 读取数据方法,一般采用只读事务-->
                <tx:method name="get*" isolation="DEFAULT"
                           propagation="SUPPORTS" read-only="true"/>
                <tx:method name="load*" isolation="DEFAULT"
                           propagation="SUPPORTS" read-only="true"/>
                <tx:method name="select*" isolation="DEFAULT"
                           propagation="SUPPORTS" read-only="true"/>
                <tx:method name="list*" isolation="DEFAULT"
                           propagation="SUPPORTS" read-only="true"/>
                <tx:method name="query*" isolation="DEFAULT"
                           propagation="SUPPORTS" read-only="true"/>
                <tx:method name="criteria*" isolation="DEFAULT"
                           propagation="SUPPORTS" read-only="true"/>
    
    
                <!--其他方法,如save,update,insert等对数据库进行写入操作的方法,当产生ServiceException进行回滚 -->
                <tx:method name="init*" isolation="DEFAULT"
                           read-only="false" propagation="REQUIRED"
                           rollback-for="ServiceException"/>
                <tx:method name="insert*" isolation="DEFAULT"
                           read-only="false" propagation="REQUIRED"
                           rollback-for="ServiceException"/>
                <tx:method name="update*" isolation="DEFAULT"
                           read-only="false" propagation="REQUIRED"
                           rollback-for="ServiceException"/>
                <tx:method name="save*" isolation="DEFAULT"
                           read-only="false" propagation="REQUIRED"
                           rollback-for="ServiceException"/>
                <tx:method name="add*" isolation="DEFAULT" read-only="false"
                           propagation="REQUIRED" rollback-for="ServiceException"/>
                <tx:method name="create*" isolation="DEFAULT"
                           read-only="false" propagation="REQUIRED"
                           rollback-for="ServiceException"/>
                <tx:method name="del*" isolation="DEFAULT" read-only="false"
                           propagation="REQUIRED" rollback-for="ServiceException"/>
                <tx:method name="remove*" isolation="DEFAULT"
                           read-only="false" propagation="REQUIRED"
                           rollback-for="ServiceException"/>
                <tx:method name="process" isolation="DEFAULT" read-only="false"
                           propagation="REQUIRED" rollback-for="ServiceException"/>
            </tx:attributes>
        </tx:advice>
    
        <!--
            Spring采用AOP进行事务控制,这里指定了凡是实现了以com.tp打头的包及其子包里以Service结尾接口中的所有方法需要由事务进行控制
        -->
        <aop:config>
            <aop:pointcut id="serviceOperation"
                          expression="execution(* com.tp..*Service.*(..))"/>
            <aop:advisor pointcut-ref="serviceOperation"
                         advice-ref="txAdvice"/>
        </aop:config>
    </beans>
    

    将此配置文件加入到Spring配置文件中,我们使用UserServiceImpl中的saveUsere方法来测试声明式事物:

    @Service
    public class UserServiceImpl implements IUserService {
    
        @Autowired
        private UserMapper userMapper;
    
        @Override
        public User getById(Integer id) {
            if (null != id && id > 0) {
                return userMapper.getById(id);
            }
            return null;
        }
    
        @Override
        public int saveUser(User user) {
            if (null != user) {
                user.setCreateTime(new Date());
                int i = userMapper.saveUser(user);
                // 测试Spring声明式事物
                // i = 2/0;
                return i;
            }
            return 0;
        }
    }
    

    我们会发现当我们将int i = 2/0;注释掉时数据库能够成功保存用户,而将其放开时无法事物会回滚,数据库中不会保存用户

    4.2基于注解的声明式事物配置

    Spring支持注解方式的声明式事物配置方式:在需要添加事务的方法上,添加 @Transactional 注解,表示该方法开启事务,当然,这个注解也可以放在类上,表示这个类中的所有方法都开启事务。
    如果要开启 Java 注解配置,在 XML 配置中添加如下配置

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

    这行配置,相当于上面XML配置中如下两个配置:

    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <!-- 需要由交给spring的aop来进行代理的方法的集合,如果应用有自己的方法需有由spring来进行事务控制必须添加方法-->
            <!--
                PROPAGATION_REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
                PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
                PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
                PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
                PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
                PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
                PROPAGATION_NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
             -->
            <!-- 读取数据方法,一般采用只读事务-->
            ......
    </tx:advice>
    
    <!--
        Spring采用AOP进行事务控制,这里指定了凡是实现了以com.tp打头的包及其子包里以Service结尾接口中的所有方法需要由事务进行控制
    -->
    <aop:config>
       ......
    </aop:config>
    

    然后,添加@Transactional注解:

    @Override
    @Transactional
    public int saveUser(User user) {
        if (null != user) {
            user.setCreateTime(new Date());
            int i = userMapper.saveUser(user);
            // 测试Spring声明式事物
            // i = 2/0;
            return i;
        }
        return 0;
    }
    

    重新测试,效果一致。
    参考文章:https://blog.csdn.net/trigl/article/details/50968079

    相关文章

      网友评论

        本文标题:Spring-AOP事物管理

        本文链接:https://www.haomeiwen.com/subject/fifrzctx.html