Spring——————事務(學!)

Spring 實現事務管理有如下兩種方式

  編程式事務管理:

    將事務管理代碼嵌入到業務方法中來控制事務的提交和回滾,在編程式管理事務中,必須在每個事務操作中包含額外的事務管理代碼。

  聲明式事務管理(推薦使用,在這裏主要講聲明式事務):

    大多數情況下比編程式事務管理更好用,它將事務管理代碼從業務方法中分離出來,以聲明的方式來實現事務管理,Spring聲明式事務管理建立在AOP基礎之上,是一個典型的橫切關注點,通過環繞增強來實現,其原理是對方法前後進行攔截,然後在目標方法開始之前創建或加入一個事務,在執行完畢之後根據執行情況提交或回滾事務。

聲明式事務的實現步驟

    1、添加spring-aspects-4.3.10.RELEASE.jar包

    2、在Spring配置文件中添加如下配置:

          

    3、添加事務註解——@Transactional:

        ①、rollbackFor和rollbackForClassName:指定對哪些異常回滾事務。

            默認情況下,如果在事務中拋出了運行時異常(繼承自RuntimeException異常類),則回滾事務;

            如果沒有拋出任何異常,或者拋出了檢查時異常,則依然提交事務,不回滾。

            這種處理方式是大多數開發者希望的處理方式,也是 EJB 中的默認處理方式;但可以根據需要人爲控制事務在拋出某些運行時異常時仍然提交事務,或者在拋出某些檢查時異常時回滾事務。

            

            這個是模擬購買書籍的方法,在這個方法中,有兩個異常,一個是在執行bookDao.enough(bookId,count)時,書庫中的書本不夠,會拋出個異常BookException;另一個是在執行moneyDao.enough(userId,total)時,錢包中的餘額不足,會拋出個異常MoneyException。我將這兩個異常都繼承了RuntimeException(運行時異常),所以在public上面加上@Transactional之後,兩個異常拋出時,都會回滾事務。若將MoneyException改爲繼承Exception,這時當MoneyException拋出時不會回滾事務。若仍想讓其回滾,又不想讓MoneyException繼承RuntimeException,這時在@Transactional後面加上(rollbackFor=MoneyException.class)就可以實現,即@Transactional(rollbackFor=MoneyException.class)

        ②、noRollbackFor和noRollbackForClassName:指定對哪些異常不回滾事務。使用方法和rollbackFor一致。

        ③、readOnly:事務只讀,指對事務性資源進行只讀操作。

             所謂事務性資源就是指那些被事務管理的資源,比如數據源、 JMS 資源,以及自定義的事務性資源等等。如果確定只對事務性資源進行只讀操作,那麼可以將事務標誌爲只讀的,以提高事務處理的性能。在 TransactionDefinition 中以 boolean 類型來表示該事務是否只讀。由於只讀的優化措施是在一個事務啓動時由後端數據庫實施的, 因此,只有對於那些具有可能啓動一個新事務的傳播行爲(PROPAGATION_REQUIRES_NEW、PROPAGATION_REQUIRED、 ROPAGATION_NESTED)的方法來說,將事務聲明爲只讀纔有意義。@Transactional註解中添加了readOnly=true,但@Transactional註解修飾的方法涉及數據的修改,這時就會拋出異常Caused by: java.sql.SQLException

        ④、timeout:設置一個事務所允許執行的最長時長(單位:秒),如果超過該時長且事務還沒有完成,則自動回滾事務,且出現org.springframework.transaction.TransactionTimedOutException異常。

          注意

            1、事務的開始往往會發生數據庫的表鎖或者被數據庫優化爲行鎖,如果允許時間過長,那麼這些數據會一直被鎖定,最終影響系統的併發性,因此可以給這些事務設置超時時間以規避該問題。

            2、由於超時是在一個事務啓動的時候開始的,因此,只有對於那些具有可能啓動一個新事務的傳播行爲(PROPAGATION_REQUIRES_NEW、PROPAGATION_REQUIRED、ROPAGATION_NESTED)的方法來說,聲明事務超時纔有意義。

        ⑤、propagation:指定事務傳播行爲,一個事務方法被另一個事務方法調用時,必須指定事務應該如何傳播,例如:方法可能繼承在現有事務中運行,也可能開啓一個新事物,並在自己的事務中運行。Spring定義瞭如下7種事務傳播行爲:       

            1、REQUIRED            支持當前事務,如果當前沒有事務,就新建一個事務。

            2、SUPPORTS            支持當前事務,如果當前沒有事務,就以非事務方式執行。

            3、MANDATORY         支持當前事務,如果當前沒有事務,就拋出異常。

            4、REQUIRES_NEW    新建事務,如果當前存在事務,把當前事務掛起。

            5、NOT_SUPPORTED  以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。

            6、NEVER                    以非事務方式執行,如果當前存在事務,則拋出異常。

            7、NESTED              支持當前事務,如果當前事務存在,則執行一個嵌套事務,如果當前沒有事務,就新建一個事務。

          下面對這七種行爲的部分行爲做進一步解釋:

               假如存在方法A和方法B;

               1、如果調用方法A前不存在任何事務,那麼在執行方法A時會自動開啓一個事務,而當A調用B時,由於事務已經存在,那麼就會使用存在的事務。這種情況下如果B方法過程發生異常需要回滾,那麼A方法中進行的所有數據操作也會回滾,因爲兩個方法使用了同一個事務。

               2、SUPPORTS傳播性的邏輯含義比較模糊,因此一般是不推薦使用的。

               3、此時無法在沒有事務的情況下調用A方法,因此,傳播性爲MANDATORY的方法必定是一個其他事務的子事務,當邏輯上獨立存在沒有意義或者可能違反數據、事務完整性的時候,就可以考慮設置這樣的傳播性設置。

               4、7、大多數情況下,這兩種傳播性行爲是類似的,但是在事務回滾上,二者區別還是蠻大的。

                   REQUIRES_NEW會創建一個與原事務無關的新事務,儘管是由一個事務調用了另一個事務,但卻沒有父子關係。如果B方法的傳播性是REQUIRES_NEW,而拋出了一個異常,則B方法一定會被回滾,而如果A方法捕獲並處理了這個B方法拋出的異常,那麼A方法仍有可能成功提交。當然,如果A方法沒有處理這個異常,那麼A方法也會被回滾。如果A方法在B方法完成後出現了異常,那麼B方法已經提交而無法回滾,只有A方法被回滾了。

                  NESTED雖然也會創建一個新事務,但是這個事務與調用者是有父子關係的相互依存的。如果B方法的傳播性是NESTED,而拋出了一個異常,事務的回滾行爲與REQUIRES_NEW是一致的。但是如果A方法在B方法完成後出現了異常,B方法同樣也會被回滾。因爲事實上,NESTED並不是真正啓動了一個事務,而是開啓了一個新的savepoint。

               5、強制不在事務中運行,如果當前存在一個事務,則掛起該事務。

               6、NEVER的含義很簡單,就是強制要求不在事務中運行,如果當前存在一個事務,則拋出異常,因此如果B方法傳播性是NEVER,則一定拋出異常。

        注意:如果兩個方法在一個類裏面,那麼只會使用一個事務。

事務的四種隔離方式:

    數據庫事務的隔離級別有4種,由低到高分別爲Read uncommitted(讀未提交) 、Read committed(讀提交) 、Repeatable read(重複讀) 、Serializable (序列化)。而且,在事務的併發操作中可能會出現髒讀(Drity Read),不可重複讀(Non-repeatable read),幻讀(Phantom Read)。下面通過事例一一闡述它們的概念與聯繫。(以下這三種現象是事務隔離的原因)

    髒讀:已知有兩個事務AB, A讀取了已經被B更新但還沒有被提交的數據,之後,B回滾事務,A讀取的數據就是髒數據;

               

    不可重複讀::已知有兩個事務ABA 多次讀取同一數據,B A多次讀取的過程中對數據作了修改並提交,導致A多次讀取同一數據時,結果不一致;

               

    幻讀:已知有兩個事務ABA從一個表中讀取了數據,然後B在該表中插入了一些新數據,導致A再次讀取同一個表, 就會多出幾行,簡單地說,一個事務中先後讀取一個範圍的記錄,但每次讀取的紀錄數不同,稱之爲幻象讀,(不會損壞數據庫中的數據)

              

      不可重複讀側重於修改,幻讀側重於新增或刪除。解決不可重複讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表

     MySQL數據庫支持上面四種事務隔離級別,默認爲Repeatable read;Oracle 數據庫支持Read committedSerializable兩種事務隔離級別,默認爲Read committed

    1、Read uncommitted(讀未提交):可能出現髒讀、不可重複讀和幻讀。

    2、Read committed(讀提交):可以避免髒讀,但可能出現不可重複讀和幻讀。大多數數據庫默認級別就是Read committed,比如Sql Server數據庫和Oracle數據庫。注意:該隔離級別在寫數據時只會鎖住相應的行。

    3、Repeatable read(重複讀):可以避免髒讀和不可重複讀,但可能出現幻讀。注意:事務隔離級別爲可重複讀時,如果檢索條件有索引(包括主鍵索引)的時候,默認加鎖方式是next-key 鎖;如果檢索條件沒有索引,更新數據時會鎖住整張表。一個間隙被事務加了鎖,其他事務是不能在這個間隙插入記錄的,這樣可以防止幻讀。

    4、Serializable(序列化):可以避免髒讀、不可重複讀和幻讀,但是併發性極低,一般很少使用。注意:該隔離級別在讀寫數據時會鎖住整張表。(這個事務執行完,下個事務才能執行)

    MySQL事務隔離級別:

        查看:輸入以下語句執行  select @@global.tx_isolation,@@tx_isolation;

        修改:

            1、全局修改

                 ①、在my.ini配置文件最後加上如下配置:

#可選參數有:READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE.

[mysqld]

transaction-isolation = READ-UNCOMMITTED

、重啓MySQL服務

           2、當前session修改,登錄MySQL數據庫後執行如下命令:

set session transaction isolation level read uncommitted;

    用java代碼實現,在@Transactional()    的()裏面加上屬性,如圖所示:

    

    由於數據庫中有mvcc機制,所以在實現不可重複讀和幻讀現象例子的時候,由於保存了數據在某個時間點的快照,會出現實際結果和預想結果不一致的現象。比如不可重複讀例子,執行後的結果是,不僅妻子轉賬成功,丈夫支付也成功,但是如果丈夫銀行卡里原本有3000元,最終結果會得到-1000

如果()裏面的事務隔離等級和是數據庫中的不一致,那麼在執行Java程序時,會按照()裏面的事務隔離等級,相當於一次性的,暫時的。事務結束,還是按照數據庫中的事務隔離等級。而且數據庫中的事務隔離等級還是全局的,java代碼中的只是局部的。

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