Spring事务管理

作者: binxin5108 | 来源:发表于2017-10-19 20:20 被阅读0次

1. 概念

什么是事务?事务指的是逻辑上的一组操作,这组操作要么全部成功,要么全部失败。

事务包括四大特性(ACID):原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)

  1. 原子性:指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
  2. 一致性:指事务前后数据的完整性必须保持一致。
  3. 隔离性:指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间的数据要相互隔离。
  4. 持久性:指一个事务一旦被提交,它对数据库中的数据的改变就是永久的,即使数据库发生故障也不应该对其有任何影响。

2. 事务的API

2.1 接口介绍

Spring对事务管理提供了接口支持,主要包括3个高层抽象的接口:

  1. PlatformTransactionManager事务管理器
  2. TransactionDefinition 事务定义信息(隔离、传播、超时、只读)
  3. TransactionStatus事务具体的运行状态信息(是否新事务、是否有保存点。。。)
Spring事务主要接口
2.2 PlatformTransactionManager接口

Spring为不同的持久化框架提供了不同的PlatformTransactionManager接口实现:

PlatformTransactionManager主要实现
具体实现 说明
org.springframework.jdbc.datasource.DataSourceTransactionManager 使用Spring JDBC或Mybatis进行持久化数据是使用
org.springframework.orm.hibernate5.HibernateTransactionManager 使用Hibernate5.x版本进行持久化数据时使用
org.springframework.orm.jpa.JpaTransactionManager 使用JPA进行持久化时使用
org.springframework.orm.jdo.JdoTransactionManager 当持久化机制是Jdo时使用
org.springframework.transaction.jta.JtaTransactionManager 使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用
2.3 TransactionDefinition接口
TransactionDefinition接口

TransactionDefinition接口中主要定义了事务的传播行为getPropagationBehavior()、事务的隔离级别getIsolationLevel()、超时时间getTimeout()、是否只读isReadOnly()、事务名称getName()

2.3.1 TransactionDefinition定义事务隔离级别

我们知道事务4个特性中有一个隔离性,如果不考虑隔离性的的话,会引发一些安全问题:脏读不可重复读幻读

  1. 脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。
  2. 不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同
  3. 幻读:一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。在后来的查询中,第一个事务就会发现有些原来没有的记录

隔离级别就是用来解决上述各种问题的,隔离级别有4种(上图中的ISOLATION_*** 除了ISOLATION_DEFAULT):

隔离级别 含义
DEFAULT 使用后端数据库默认的隔离级别(Spring中的选择项)
READ_UNCOMMITTED 允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读
READ_COMMITTED 允许在并发事务已经提交后读取。可防止脏读,但幻读、不可重复读仍可发生
REPEATABLE_READ 对相同的字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生
SERIALIZABLE 完全服从ACID的隔离级别,确保不发送脏、幻、不可重复读。这仔所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表完成的。
2.3.2 TransactionDefinition定义事务传播行为

事务的传播行为解决的是业务层方法之间调用的时事务的传递问题。

一般我们的系统会分为3层:Web层、业务层Service、持久层DAO;假设有两个业务类ServiceAServiceBServiceA中有方法aaa(),ServiceB中有方法bbb(),有一个业务逻辑需要调用ServiceA.aaa()ServiceB.bbb()才能完成,现在aaa()方法里有事务,bbb()方法里也有事务,那到底要用哪个呢,这就涉及到了事务的传播行为。

事务的传播行为有7种(上图中的PROPAGATION_***):

隔离级别 含义
PROPAGATION_REQUIRED 支持当前事务,如果不存在,就新建一个
PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务
PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常
PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务
PROPAGATION_NOT_SUPPORTED 以非事务的方式运行,如果有事务存在,挂起当前事务
PROPAGATION_NEVER 以非事务的方式运行,如果有事务存在,抛出异常
PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行
2.4 TransactionStatus接口
TransactionStatus接口

事务本身会存在一些状态信息,而TransactionStatus接口里面提供了一些方法,通过这些方法可以获得事务相应的状态。

isNewTransaction():判断是否是一个新的事务
hasSavepoint():是否存在保存点
setRollbackOnly():设置为只回滚
isRollbackOnly():是否为只回滚
isCompleted():是否已完成

3. 编程式事务管理(不常用)

编程式事务管理使用TransactionTemplate模板来控制事务。TransactionTemplate的重要方法就是 execute方法,此方法调用 TransactionCallback 进行处理。实际上我们需要处理的事情全部都是在TransactionCallback中编码的,我们可以定义一个类并实现此接口,然后作为 TransactionTemplate.execute的参数。把需要完成的事情放到doInTransaction中,并且传入一个 TransactionStatus参数,此参数是来调用回滚的。demo如下:

spring配置文件springDemo1.xml

    <?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"
       default-lazy-init="true">

    <description>Spring Configuration</description>

    <!-- 加载配置属性文件 -->
    <context:property-placeholder ignore-unresolvable="true" location="classpath:jdbc.properties" />

    <!-- 使用Annotation自动注册Bean base-package 如果多个,用“,”分隔-->
    <context:component-scan base-package="com.zhoubg.spring.transaction.demo1" />


    <!-- 数据源配置, 使用 Druid 数据库连接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!-- 数据源驱动类可不写,Druid默认会自动根据URL识别DriverClass -->
        <property name="driverClassName" value="${jdbc.driver}" />
        <!-- 基本属性 url、user、password -->
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- ==================================1.编程式的事务管理=============================================== -->

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

    <!-- 配置事务管理的模板:Spring为了简化事务管理的代码而提供的类 -->
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager"/>
    </bean>
</beans>

模拟业务方法AccountServiceImpl.transfer:

/**
     * 模拟转账操作
     * @param out   :转出账号
     * @param in    :转入账号
     * @param money :转账金额
     */
    public void transfer(final String out,final String in,final Double money) {

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                try { //具体业务代码
                    accountDaoImpl.outMoney(out,money); //out账户出账
                    //int i = 1/0;
                    accountDaoImpl.inMoney(in,money);// in账户入账
                }catch (Exception e){
                    status.setRollbackOnly(); //设置回滚
                    e.printStackTrace();
                }
            }
        });
    }

4. 声明式事务管理

4.1 声明式事务管理方式一:基于TransactionProxyFactoryBean的方式

spring配置文件springDemo2.xml关键配置:

    <!-- ==================================2.使用XML配置声明式的事务管理(原始方式)=============================================== -->

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

    <!-- 配置业务层的代理 -->
    <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <!-- 配置目标对象 -->
        <property name="target" ref="accountServiceImpl" />
        <!-- 注入事务管理器 -->
        <property name="transactionManager" ref="transactionManager"></property>
        <!-- 注入事务的属性 -->
        <property name="transactionAttributes">
            <props>
                <!--
                    prop的格式:
                        * PROPAGATION   :事务的传播行为
                        * ISOTATION     :事务的隔离级别
                        * readOnly      :只读
                        * -EXCEPTION    :发生哪些异常回滚事务
                        * +EXCEPTION    :发生哪些异常不回滚事务
                 -->
                <prop key="transfer">PROPAGATION_REQUIRED</prop>
                <!-- <prop key="transfer">PROPAGATION_REQUIRED,readOnly</prop> -->
                <!-- <prop key="transfer">PROPAGATION_REQUIRED,+java.lang.ArithmeticException</prop> -->
            </props>
        </property>
    </bean>

模拟业务方法AccountServiceImpl.transfer:

  public void transfer(final String out,final String in,final Double money) {
        accountDaoImpl.outMoney(out,money);
        accountDaoImpl.inMoney(in,money);
    }
4.2 声明式事务管理方式二:基于AspectJ的XML方式

spring配置文件springDemo3.xml关键配置:

    <!-- ==================================3.使用XML配置声明式的事务管理,基于tx/aop=============================================== -->

    <!-- 配置事务管理器 -->
    <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:发生哪些异常回滚
                no-rollback-for :发生哪些异常不回滚
                timeout     :过期信息
             -->
            <tx:method name="transfer" propagation="REQUIRED" />
        </tx:attributes>
    </tx:advice>

    <!-- 配置切面 -->
    <aop:config>
        <!-- 配置切入点 -->
        <aop:pointcut expression="execution(* com.zhoubg.spring.transaction..demo3.AccountService+.*(..))" id="pointcut1"/>
        <!-- 配置切面 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
    </aop:config>

模拟业务方法AccountServiceImpl.transfer:

public void transfer(final String out,final String in,final Double money) {
        accountDaoImpl.outMoney(out,money);
        accountDaoImpl.inMoney(in,money);
    }
4.3 声明式事务管理方式三:基于注解的方式(推荐)

这种方式是基于@Transactional注解的,简单易用,更清爽,是推荐的使用方式。

@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

@Transactional的属性:

属性 类型 描述
value String 可选的限定描述符,指定使用的事务管理器
propagation enum: Propagation 可选的事务传播行为设置
isolation enum: Isolation 可选的事务隔离级别设置
readOnly boolean 读写或只读事务,默认读写
timeout int (in seconds granularity) 事务超时时间设置
rollbackFor Class对象数组,必须继承自Throwable 导致事务回滚的异常类数组
rollbackForClassName 类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组
noRollbackFor Class对象数组,必须继承自Throwable 不会导致事务回滚的异常类数组
noRollbackForClassName 类名数组,必须继承自Throwable 不会导致事务回滚的异常类名字数组

spring配置文件springDemo4.xml关键配置:

    <!-- ==================================4.使用注解配置声明式事务============================================ -->

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

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

模拟业务方法AccountServiceImpl.transfer:

    @Transactional(readOnly = false)
    public void transfer(final String out,final String in,final Double money) {
        accountDaoImpl.outMoney(out,money);
        accountDaoImpl.inMoney(in,money);
    }

以上示例源码:learn-spring-transaction

相关文章

网友评论

    本文标题:Spring事务管理

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