Spring之美-事務管理

1、概述

事務管理對於企業應用來講是相當重要的,即便出現異常狀況,它也能夠保證數據的一致性。
Spring Framework對事務管理提供了一致的抽象,其特色以下:html

  • 爲不一樣的事務API提供一致的編程模型,好比JTA(Java Transaction API), JDBC, Hibernate, JPA(Java Persistence API和JDO(Java Data Objects)java

  • 支持聲明式事務管理,特別是基於註解的聲明式事務管理,簡單易用spring

  • 提供比其餘事務API如JTA更簡單的編程式事務管理API數據庫

  • 與spring數據訪問抽象的完美集成

2、事務的基本原理

Spring事務的本質其實就是數據庫對事務的支持,沒有數據庫的事務支持,spring是沒法提供事務功能的。對於純JDBC操做數據庫,想要用到事務,能夠按照如下步驟進行:編程

  1. 獲取鏈接 Connection con = DriverManager.getConnection()
  2. 開啓事務con.setAutoCommit(true/false);
  3. 執行CRUD
  4. 提交事務/回滾事務 con.commit() / con.rollback();
  5. 關閉鏈接 conn.close();

使用Spring的事務管理功能後,咱們能夠再也不寫步驟 2 和 4 的代碼,而是由Spirng 自動完成。
那麼Spring是如何在咱們書寫的 CRUD 以前和以後開啓事務和關閉事務的呢?解決這個問題,也就能夠從總體上理解Spring的事務管理實現原理了。併發

注意: 性能

默認狀況下,數據庫處於自動提交模式(即con.setAutoCommit(true))。每一條語句處於一個單獨的事務中,在這條語句執行完畢時,若是執行成功則隱式的提交事務,若是執行失敗則隱式的回滾事務。this

對於正常的事務管理,是一組相關的操做處於一個事務之中,所以必須關閉數據庫的自動提交模式。不過,這個咱們不用擔憂,spring會將底層鏈接的自動提交特性設置爲false。spa

下面是Spring底層實現該功能的代碼:debug

// switch to manual commit if necessary. this is very expensive in some jdbc drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getautocommit()) {
    txobject.setmustrestoreautocommit(true);
    if (logger.isdebugenabled()) {
        logger.debug("switching jdbc connection [" + con + "] to manual commit");
    }
    con.setautocommit(false);
}

3、事務管理方式

Spring支持編程式事務管理聲明式事務管理兩種方式。

編程式事務管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager。對於編程式事務管理,Spring推薦使用TransactionTemplate

聲明式事務管理創建在AOP之上的。其本質是對方法先後進行攔截,而後在目標方法開始以前建立或者加入一個事務,在執行完目標方法以後根據執行狀況提交或者回滾事務。聲明式事務最大的優勢就是不須要經過編程的方式管理事務,這樣就不須要在業務邏輯代碼中摻瑣事務管理的代碼,只需在配置文件中作相關的事務規則聲明(或經過基於@Transactional註解的方式),即可以將事務規則應用到業務邏輯中。

顯然聲明式事務管理要優於編程式事務管理,這正是Spring倡導的非侵入式的開發方式。聲明式事務管理使業務代碼不受污染,一個普通的POJO對象,只要加上註解就能夠得到徹底的事務支持。和編程式事務相比,聲明式事務惟一不足地方是,後者的最細粒度只能做用到方法級別,沒法作到像編程式事務那樣能夠做用到代碼塊級別。可是即使有這樣的需求,也存在不少變通的方法,好比,能夠將須要進行事務管理的代碼塊獨立爲方法等等。

聲明式事務管理也有兩種經常使用的方式,一種是基於tx和aop名字空間的xml配置文件,另外一種就是基於@Transactional註解。顯然基於註解的方式更簡單易用,更清爽。

 

4、Spring 事務的傳播屬性

spring事務的傳播屬性:存在多個事務同時存在的時候,spring應該如何處理這些事務的行爲。這些屬性在TransactionDefinition中定義,具體常量的解釋見下表:

常量名稱 常量解釋
PROPAGATION_REQUIRED 支持當前事務,若是當前沒有事務,就新建一個事務。這是最多見的選擇,也是 Spring 默認的事務的傳播。
PROPAGATION_REQUIRES_NEW 新建事務,若是當前存在事務,把當前事務掛起。新建的事務將和被掛起的事務沒有任何關係,是兩個獨立的事務,外層事務失敗回滾以後,不能回滾內層事務執行的結果,內層事務失敗拋出異常,外層事務捕獲,也能夠不處理回滾操做
PROPAGATION_SUPPORTS 支持當前事務,若是當前沒有事務,就以非事務方式執行。
PROPAGATION_MANDATORY 支持當前事務,若是當前沒有事務,就拋出異常。
PROPAGATION_NOT_SUPPORTED 以非事務方式執行操做,若是當前存在事務,就把當前事務掛起。
PROPAGATION_NEVER 以非事務方式執行,若是當前存在事務,則拋出異常。
PROPAGATION_NESTED

若是一個活動的事務存在,則運行在一個嵌套的事務中。若是沒有活動事務,則按REQUIRED屬性執行。它使用了一個單獨的事務,這個事務擁有多個能夠回滾的保存點。內部事務的回滾不會對外部事務形成影響。它只對DataSourceTransactionManager事務管理器起效。

5、數據庫隔離級別

隔離級別 隔離級別的值 致使的問題
Read-Uncommitted 0 致使髒讀
Read-Committed 1 避免髒讀,容許不可重複讀和幻讀
Repeatable-Read 2 避免髒讀,不可重複讀,容許幻讀
Serializable 3 串行化讀,事務只能一個一個執行,避免了髒讀、不可重複讀、幻讀。執行效率慢,使用時慎重

髒讀:一事務對數據進行了增刪改,但未提交,另外一事務能夠讀取到未提交的數據。若是第一個事務這時候回滾了,那麼第二個事務就讀到了髒數據。

不可重複讀:一個事務中發生了兩次讀操做,第一次讀操做和第二次操做之間,另一個事務對數據進行了修改,這時候兩次讀取的數據是不一致的。

幻讀:第一個事務對必定範圍的數據進行批量修改,第二個事務在這個範圍增長一條數據,這時候第一個事務就會丟失對新增數據的修改。

總結:

隔離級別越高,越能保證數據的完整性和一致性,可是對併發性能的影響也越大。

大多數的數據庫默認隔離級別爲 Read Commited,好比 SqlServer、Oracle

少數數據庫默認隔離級別爲:Repeatable Read 好比: MySQL InnoDB

相關文章
相關標籤/搜索