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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,886評論 18 139
  • spring支持編程式事務管理和聲明式事務管理兩種方式。 編程式事務管理使用TransactionTemplate...
    熊熊要更努力閱讀 258評論 0 0
  • 對大多數Java開發者來說,Spring事務管理是Spring應用中最常用的功能,使用也比較簡單。本文主要從三個方...
    sherlockyb閱讀 3,228評論 0 18
  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,941評論 6 342
  • 1 什么是事務 生活中關于事務有一個常見的場景,即銀行用戶轉賬。簡單的講,轉賬可以分為下面 2 個步驟: 查看用戶...
    millions_chan閱讀 617評論 0 4