spring知識八------事務管理

事務簡介

事務主要是用來操作數據庫的,他的使用是用來保證數據的完整性和一致性。
事務就是一系列的動作,它們被當做一個單獨的工作單元。這些動作要麼全部完成,要麼全部不起作用。

事務的四個關鍵屬性(ACID)

原子性(atomicity): 事務是一個原子操作,由一系列動作組成。事務的原子性確保動作要麼全部完成要麼完全不起作用。
一致性(consistency): 一旦所有事務動作完成,事務就被提交。數據和資源就處於一種滿足業務規則的一致性狀態中。
隔離性(isolation): 可能有許多事務會同時處理相同的數據,因此每個事物都應該與其他事務隔離開來,防止數據損壞。
持久性(durability): 一旦事務完成,無論發生什麼系統錯誤,它的結果都不應該受到影響。通常情況下,事務的結果被寫到持久化存儲器中。

Spring對事務的管理

Spring 的核心事務管理抽象是 【org.springframework.transaction.PlatformTransactionManager 】接口,他有一系列的實現類,用於不同的事務管理,其中最常用的類是【 HibernateTransactionManager】和【 DataSourceTransactionManager】。HibernateTransactionManager用 Hibernate 框架存取數據庫。DataSourceTransactionManager在應用程序中只需要處理一個數據源,而且通過 JDBC 存取。
它爲事務管理封裝了一組獨立於技術的方法。無論使用 Spring 的哪種事務管理策略(編程式或聲明式),事務管理器都是必須的。

Spring 既支持編程式事務管理,也支持聲明式的事務管理

編程式事務管理: 將事務管理代碼嵌入到業務方法中來控制事務的提交和回滾。在編程式管理事務時,必須在每個事務操作中包含額外的事務管理代碼,這樣的話,會引起業務代碼混亂。

聲明式事務管理: 大多數情況下比編程式事務管理更好用。它將事務管理代碼從業務方法中分離出來,以聲明的方式來實現事務管理。事務管理作爲一種橫切關注點, 可以通過 AOP 方法模塊化。Spring 通過 Spring AOP 框架支持聲明式事務管理。建議使用這種方式。

聲明式事務管理應用
聲明式事務三步走:
第一步:配置文件中添加對應的命名空間 tx,引入對應的約束。
第二步:配置文件中聲明式事務配置。
第三步:進行事務處理。
注意:在聲明事務配置時,不同的配置事務管理器,有不同的類來完成,所配置的屬性也有所不同
注意:在進行事務處理時,不要在daoImpl層處理,應該將事務的處理提取到Service層,因爲事務的回滾一般會牽扯到多個dao的使用,所以在Service層進行事務的處理,這樣能最大化的保證數據的完整性。

實例: 用戶去書店買書,減少庫存與用戶餘額扣除可以是認爲是一個事務,需要同時進行處理。

第一步:添加命名空間tx

xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"

第二步:聲明式事務的配置

<!-- 配置事務管理器 屬性的配置可以在類中進行發現  -->
===============================================================
<!-- DataSourceTransactionManager -->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="datasource"></property>
</bean>

<!-- 啓用事務註解 -->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
==================================================================
<!--HibernateTransactionManager-->

<!-- 配置事務管理器 屬性的配置可以在類中進行發現 -->
<bean id="txManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
<!-- 啓用事務註解 -->
<tx:annotation-driven transaction-manager="txManager"/>

第三步:進行事務處理,在對應的方法上面添加註解@Transactional

@Transactional  //添加事務註解 一般都是在service層添加
    public void buyBook(String username,String isbn) {
        // 1 獲取書的單價
        int price=bookStoreDAO.findbookPriceByIsbn(isbn);       
        // 2 更新書的庫存 
        bookStoreDAO.updateBookStock(isbn);
        // 3 更新用戶餘額
        bookStoreDAO.updateUserAccount(username,price);
    }

@Transactional 註解

@Transactional 註解可以在方法上定義爲支持事務處理的方法。
@Transactional註解可以在類上進行定義,表示這個類中的所有公共方法都會被定義成支持事務處理的
@Transactional註解屬性propagation表示事務的傳播行爲
@Transactional註解屬性isolation表示事務的隔離級別
@Transactional註解屬性rollbackFor表示事務的回滾
@Transactional註解屬性readOnly表示只讀
@Transactional註解屬性timeout表示事務過期

事務的傳播行爲propagation

事務的交互使用,當事務方法被另一個事務方法調用時,必須指定事務應該如何傳播。 方法可能繼續在現有事務中運行, 也可能開啓一個新事務, 並在自己的事務中運行。
事務的傳播行爲可以由傳播屬性指定。Propagation(傳播,擴展)默認值是required,即是如果當前存在事務,則不進行創建,只是利用以前的,如果沒有,則是進行創建。
這裏寫圖片描述

官網對於REQUIRED和REQUIRED_NEW的說明,也是最常用的兩個,其餘的自行查詢API。
對於REQUIRED,則是用同一事務進行處理,一旦出錯則是回滾到最初的狀態,
對於REQUIRED_NEW,則是如果在一個事務中,又出現了一個新事務,對不起,我嫌棄你,我要自己再重新開啓一個事務,而對於第一個事務,你先掛起,等我執行完之後你再來執行,有一種喧賓奪主的感覺。

這裏寫圖片描述
這裏寫圖片描述

例子:買書 資金可以買第一本書,無法購買第二本書


    // 一個人買多本書
    @Transactional  // 此事務中利用for循環多次購買,多次打開買書事務
    public void checkout(String username,List<String> isbns) {
        for(String isbn:isbns){
            bookStoreService.buyBook(username,isbn);  //buyBook 中是存在簡單的事務模擬
        }
    }
=====================================================================
   // 基本事務,進行買書,每次只能購買一本
    @Transactional (propagation=Propagation.REQUIRES_NEW)
    public void buyBook(String username,String isbn) {
        // 1 獲取書的單價
        int price=bookStoreDAO.findbookPriceByIsbn(isbn);       
        // 2 更新書的庫存 
        bookStoreDAO.updateBookStock(isbn);
        // 3 更新用戶餘額
        bookStoreDAO.updateUserAccount(username,price);
    }
===============================================================
@Test //事務的傳播行爲
    public void testtransaction(){
        cashierService.checkout("AA",Arrays.asList("1001","1002"));
    }
    /*數據庫數據符合用戶金額可以買第一本書,但是不能買第二本書,記得是在內部的那個事務中添加事務的傳播屬性
     * 默認的情況下, 使用propagation=Propagation.REQUIRED,則是在外面的事務中,不進行重新開啓事務,
     * 這樣的情況下,買第一本書的時候成功了,不發生事務回滾,
     * 但是在買第二本書的時候,則發生事務回滾,一下回滾到外面事務的開始位置,而將第一次買成功的也進行 了回滾
     * 
     * 使用propagation=Propagation.REQUIRES_NEW 則是進行重新事務的開啓。
     * 這種情況下,買第一本書的時候成功了,不發生事務回滾,
     * 但是再買第二次的時候,由於我們設置了新開啓事務,所以在買第二次的時候,新事務開啓,資金不足的情況下,發生事務回滾,但是由於新開了一個事務,
     * 所以只是回到此事務的開始位置,即第一次買書結束的位置,而只是第一本書不進行回滾,而第二次的進行回滾。
     */

事務的隔離級別isolation

事務的隔離級別主要是用於在併發情況下的事務處理
當同一個應用程序或者不同應用程序中的多個事務在同一個數據集上併發執行時,可能會出現許多意外的問題。併發事務所導致的問題主要有:髒讀、不可重複讀、幻讀。

髒讀:對於兩個事務T1,T2, T1 讀取了已經被 T2 更新但還沒有被提交的字段。 之後, 若 T2 回滾,T1讀取的內容就是臨時且無效的。【讀但未提交】
不可重複讀:對於兩個事務 T1, T2,T1 讀取了一個字段,然後 T2 更新了該字段.。之後, T1再次讀取同一個字段,值就不同了。【讀更讀】
幻讀:對於兩個事務 T1,T2, T1 從一個表中讀取了一個字段,然後 T2 在該表中插入了一些新的行。之後,如果 T1 再次讀取同一個表,就會多出幾行。【讀插讀】

理論上來說,事務應該彼此完全隔離,以避免併發事務所導致的問題。然而,那樣會對性能產生極大的影響,因爲事務必須按順序運行,所以爲了提升性能,事務會以較低的隔離級別運行。

事務的隔離級別要得到底層數據庫引擎的支持,而不是應用程序或者框架的支持.
Oracle 支持的 2 種事務隔離級別:READ_COMMITED ,SERIALIZABLE
Mysql 支持 4 中事務隔離級別

Spring 支持的事務隔離級別
這裏寫圖片描述

這裏寫圖片描述

@Transactional其他屬性介紹

回滾事務屬性:一般我們只是用默認的即可。
默認情況下只有未檢查異常(RuntimeException和Error類型的異常)會導致事務回滾。而受檢查異常不會。
事務的回滾規則可以通過 @Transactional 註解的 rollbackFor 和 noRollbackFor 屬性來定義。這兩個屬性被聲明爲 Class[] 類型的,因此可以爲這兩個屬性指定多個異常類,大括號表示。

noRollbackFor={UserAccountException.class,IOException.class}

超時事務屬性: 事務在強制回滾之前可以保持多久。這樣可以防止長期運行的事務佔用資源。
只讀事務屬性: 表示這個事務只讀取數據但不更新數據,這樣可以幫助數據庫引擎優化事務。

基於XML配置的事務管理

基於XML的事務配置主要是在文件中進行配置。
三步走:
1.配置事務管理器
2.配置事務屬性
3.配置事務切入點,以及把事務切入點和事務屬性關聯起來
配置文件如下:

<!-- 事務管理的配置 -->

        <!-- 基本bean配置 -->
        <bean id="bookStoreDAO" class="com.wf.springtransactionXml.BookStoreDAOImpl">
            <property name="jdbcTemplate" ref="jdbcTemplate"></property>
        </bean>

        <bean id="bookStoreService" class="com.wf.springtransactionXml.serviceImpl.BookStoreServiceImpl">
            <property name="bookStoreDAO" ref="bookStoreDAO"></property>
        </bean> 

        <bean id="cashierService" class="com.wf.springtransactionXml.serviceImpl.CashierServiceImpl">
            <property name="bookStoreService" ref="bookStoreService"></property>
        </bean>
        <!--1.配置事務管理器 -->
        <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <property name="dataSource" ref="datasource"></property>
        </bean>

        <!--2.配置事務屬性     傳播行爲,隔離級別,事務回滾,只讀事務,超時結束,  -->
        <tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
            <tx:attributes>
                <!-- <tx:method name="*"/>  匹配所有屬性爲默認值 -->
                <tx:method name="buyBook" propagation="REQUIRES_NEW"/>  <!-- 指定事務方法的屬性 -->
                <!-- <tx:method name="buyBook*" propagation="REQUIRES_NEW"/>    指定事務方法的屬性,可以使用通配符 -->
            </tx:attributes>
        </tx:advice>

        <!-- 3.配置事務切入點,以及把事務切入點和事務屬性關聯起來,即哪些方法需要配置事務 -->
            <aop:config>
                <aop:pointcut expression="execution(* com.wf.springtransactionXml.service.*.*(..))" 
                id="txPointCut"/>
                <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
                <!-- 在這個txPointCut匹配的方法上面執行事務txAdvice -->
            </aop:config>   

源碼不再單獨上傳到博客資源,不容易通過,後期會發出百度雲鏈接或者源碼Github地址:
https://github.com/wangfa1994/SpringLearning/tree/spring007

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章