Spring事务管理学习笔记

Spring事务管理

Spring事务管理接口

  • PlatformTransactionManager: (平台)事务管理器

  • TransactionDefinition: 事务定义信息(事务隔离级别、传播行为、超时、只读、回滚规则)

  • TransactionStatus: 事务运行状态

所谓事务管理,其实就是“按照给定的事务规则来执行提交或者回滚操作”。

PlatformTransactionManager接口介绍

Spring并不直接管理事务,而是提供了多种事务管理器。Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。

接口内方法

public interface PlatformTransactionManager(){  
    // 根据指定的传播行为,返回当前活动的事务或创建一个新事务
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; 
    // 使用事务目前的状态提交事务
    Void commit(TransactionStatus status) throws TransactionException;  
    // 对执行的事务进行回滚
    Void rollback(TransactionStatus status) throws TransactionException;  
    } 

我们刚刚也说了Spring中PlatformTransactionManager根据不同持久层框架所对应的接口实现类,几个比较常见的如下图所示

Spring事务管理学习笔记

比如我们在使用JDBC或者iBatis(就是Mybatis)进行数据持久化操作时,我们的xml配置通常如下:

	<!-- 事务管理器 -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<!-- 数据源 -->
		<property name="dataSource" ref="dataSource" />
	</bean>

TransactionDefinition接口介绍

这个类就定义了事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了5个方面:隔离级别,传播行为,回滚规则,是否只读,事务超时。

接口内方法

public interface TransactionDefinition {
    // 返回事务的传播行为
    int getPropagationBehavior(); 
    // 返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据
    int getIsolationLevel(); 
    //返回事务的名字
    String getName()// 返回事务必须在多少秒内完成
    int getTimeout();  
    // 返回是否优化为只读事务。
    boolean isReadOnly();
} 
事务隔离级别

定义了一个事务可能受到其他并发事务影响的程度

几种并发导致的问题

  • 脏读
  • 丢失修改,A、B两个事务读取x,并做x–操作,当A先读取到后进行x–,B紧接着也读取到了同一个x值,并做x–,A的修改就被丢失了
  • 不可重复读
  • 幻读

不可重复读与幻读的区别

二者是类似的,区别在于不可重复读重点在于修改,幻读在于增删。

几种隔离级别

  • TransactionDefinition.ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别

  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 未提交读

  • TransactionDefinition.ISOLATION_READ_COMMITTED: 提交读

  • TransactionDefinition.ISOLATION_REPEATABLE_READ: 重复读

  • TransactionDefinition.ISOLATION_SERIALIZABLE: 串行化

事务传播行为

为了解决业务层方法之间相互调用的事务问题。

当事务方法被另一个事务方法(确定另一个也必须是事务方法??)调用的时候,必须指定事物应该如何进行传播。

因为方法可能继续在现有事务中运行,也可能开启一个新的事务。

Spring定义了几个表示传播行为的常量,下面按照是否支持当前事务进行分类:

支持当前事务:

  • TransactionDefinition.PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)

不支持当前事务的情况:

  • TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。

  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。

  • TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。

其他情况:

  • TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

以 PROPAGATION_NESTED 启动的事务内嵌于外部事务中(如果存在外部事务的话),此时,内嵌事务并不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交。如果熟悉 JDBC 中的保存点(SavePoint)的概念,那嵌套事务就很容易理解了,其实嵌套的子事务就是保存点的一个应用,一个事务中可以包括多个保存点,每一个嵌套子事务。另外,外部事务的回滚也会导致嵌套子事务的回滚。

事务超时属性

所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。

事务只读属性

对事务性资源进行只读操作或者读写操作。

事务性资源就是指那些被事务管理的资源,比如数据源,JMS资源,以及自定义的事务性资源等。

如果能标记为只读的话,可以提高事务处理的性能。

回滚规则

定义了哪些异常会导致事务回滚而那些不会。

默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚

你可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样的,你还可以声明事务遇到特定的异常不会滚,即使这些异常是运行期异常。

检查型异常与非检查型异常

  • 继承自Runtime Exception或 Error 的是非检查型异常,而继承自 Exception 的则是检查型异常(当然,Runtime Exception 本身也是 Exception 的子类)。

  • 对非检查型类异常可以不用捕获,而检查型异常则必须用try语句块进行处理或者把异常交给上级方法处理总之就是必须写代码处理它。

TransactionStatus接口介绍

TransactionStatus接口用来记录事务的状态,该接口定义了一组方法,用来获取或判断事务的相应状态信息.

PlatformTransactionManager.getTransaction(…) 方法返回一个 TransactionStatus 对象。返回的TransactionStatus 对象可能代表一个新的或已经存在的事务(如果在当前调用堆栈有一个符合条件的事务)。

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

Spring支持两种方式的事务管理

  • 编程式事务管理:通过底层API或者Transaction Template手动管理事务,实际应用中很少使用
  • 使用xml或者annotation配置声明式事务管理:代码侵入性最小,实际是通过aop实现

实现声明式事务的四种方式

  1. 基于TransactionInterceptor的声明式事务:Spring 声明式事务的基础,通常也不建议使用这种方式,但是与前面一样,了解这种方式对理解 Spring 声明式事务有很大作用。
  2. 基于TransactionProxyFactoryBean的声明式事务:第一种方式的改进版本,简化了配置文件的书写,Spring2.0中已经不推荐了。
  3. 基于 和命名空间的声明式事务管理:目前推荐的方式,使用aop实现,充分利用切点表达式的强大支持,使得管理事务更加灵活。
  4. 基于@Transaction的全注解方式:开发人员只需在配置文件中加上一行启用相关后处理Bean的配置,然后在需要实施事务管理的

基于转账业务的案例

我们在两次转账之间添加一个错误语句(对应银行断电等意外情况),如果这个时候两次转账不能成功,则说明事务配置正确,否则,事务配置不正确。

编程式事务管理

底层API实现

xml配置

<bean class="org.springframework.transaction.support.DefaultTransactionDefinition" id="transactionDefinition">
        <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
    </bean>
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

java配置

	@Autowired
    TransactionDefinition transactionDefinition;

    @Autowired
    PlatformTransactionManager transactionManager;
...
 public void transfer() {
 TransactionStatus status = transactionManager.getTransaction(transactionDefinition);
        try {
            //本地事务执行逻辑...
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            e.printStackTrace();
        }   
}

TransactionTemplate实现

xml配置

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

    <bean class="org.springframework.transaction.support.TransactionTemplate" id="transactionTemplate">
        <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/>
        <property name="transactionManager" ref="transactionManager"/>
        <property name="isolationLevelName" value="ISOLATION_DEFAULT"/>
    </bean>

java

@Autowired
    TransactionTemplate transactionTemplate;
public void transfer() {
    transactionTemplate.execute(new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction(TransactionStatus transactionStatus) {
                Object result = null;
                try {
                    //本地事务逻辑执行...
                    //如果顺利执行,参数transactionStatus会自动提交
                } catch (Exception e) {
                    transactionStatus.setRollbackOnly();
                    result = false;
                    e.printStackTrace();
                }
                return result;
            }
        });
}

顺便说一下,TransactionTemplate是继承于DefaultTransactionDefinition的。

AbstractPlatformTransactionManager中用到了很多ThreadLocal类变量,有空需要研究一下。

声明式事务管理

基于AspectJ(aop)的

xml配置

<!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 配置事务增强 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!-- 做事务操作 -->
        <tx:attributes>
            <!-- 设置进行事务操作的方法匹配规则 -->
            <!--
              propagation:事务传播行为;
              isolation:事务隔离级别;
              read-only:是否只读;
              rollback-for:发生那些异常时回滚
              timeout:事务过期时间
             -->
            <tx:method name="bankdemo*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!-- 第三步:配置切面 切面即把增强用在方法的过程 -->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.kip.service.impl..*.*(..))"/>
    </aop:config>

java

public void accountMoney () {
	//正常业务逻辑执行... 
}

基于注解的方式

xml配置

<!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 第二步: 开启事务注解 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

    <!-- 第三步 在方法所在类上加注解 -->

java

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false, timeout = -1)
    public void bankdemo() {
        //正常业务逻辑执行...
    }

参考文章