Spring事务管理

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • spring支持编程式事务管理和声明式事务管理两种方式。 编程式事务管理使用TransactionTemplate...
    熊熊要更努力阅读 245评论 0 0
  • 对大多数Java开发者来说,Spring事务管理是Spring应用中最常用的功能,使用也比较简单。本文主要从三个方...
    sherlockyb阅读 3,191评论 0 18
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,724评论 6 342
  • 1 什么是事务 生活中关于事务有一个常见的场景,即银行用户转账。简单的讲,转账可以分为下面 2 个步骤: 查看用户...
    millions_chan阅读 609评论 0 4