脫離 Spring 實現複雜嵌套事務,之二(REQUIRED - 加入已有事務)

    本文是<實現 Spring 的事務控制>系列文章中一篇。本文假設讀者已經閱讀並理解《實現 Spring 的事務控制,之一(必要的概念)》文中所涉及的概念(當前鏈接引用計數),以及數據庫鏈接的(new狀態java

PROPAGATION_REQUIRED(加入已有事務)

定義:

    嘗試加入已經存在的事務中,若是沒有則開啓一個新的事務。

解釋:

    解釋 REQUIRED 行爲並不困難。它是指若是當前鏈接中若是已經開啓了數據庫事務,而且有程序在控制這個事務。那麼在處理 REQUIRED 行爲時將會忽略 commit/rollback 操做,這是加入已有事務的特徵。 數據庫

時間 事務1 事務2
T1 開始事務
T2 操做1...
T3
加入事務1
T4 操做2...
T5 操做3...
T6 遞交事務

    定義中有一句「若是沒有則開啓一個新的事務」這句話怎麼理解? 測試

    所謂「沒有」指的是當前鏈接中不存在事務(事務狀態爲false)。「則開啓新的事務」這裏的能夠理解爲,若是沒有事務那麼纔開啓新的事務。並且新開啓的事務是須要事務管理器進行維護的。 spa

做原理

開啓事務 .net

    事務管理器在建立 REQUIRED 類型事務時,會取得當前鏈接這一過程會持有當前鏈接(引用計數+1)。 code

    而後經過判斷當前鏈接是否存在事務狀態,來決定是否經過 doBegin 方法開啓事務。這個規則爲:若是存在事務狀態則忽略,不然開啓。 對象

    通常狀況下在調用 doBegin 方法以前事務都是還沒有開啓的。咱們知道沒有開啓事務的鏈接特徵是 autoCommit 屬性爲 true。在這種狀況下每一次SQL操做都是獨立成爲一個事務,所以多條SQL語句之間是不存在事務關係的。換句話說只要進入 doBegin 方法的數據庫鏈接都是沒有開啓事務的鏈接,它們都知足 new 狀態的特徵 blog

事務中的數據庫操做 接口

    不管在開啓事務的時候Connection 此時此刻,能夠直接使用 Connection 接口暢快的使用數據庫操做。因爲每次進行數據庫操做都要反覆的申請和釋放數據庫鏈接。這會反覆的使引用計數 +1,-1。 事務

遞交/回滾事務

   REQUIRED 行爲告訴咱們,若是環境中有事務控制,那麼該行爲下的事務管理器將不做爲。全部事務控制操做交給環境中的事務控制來處理。

    可是若是環境中不存在事務,那麼事務管理器是須要負責 commit & rollback 的。

    這時候在前面肯定的 new 狀態就能夠發揮做用了。由於根據 new 狀態的特徵描述咱們知道凡是持有 new 狀態的數據庫鏈接在開啓事務以前是不存在先前事務的。做爲後來者的事務管理器而言,能夠放心大膽的去遞交事務。

    若是不具有 new 特徵則正好說明外部在事務管理器以外有其它程序代碼代爲控制了事務。

    例以下面這個業務邏輯(僞代碼):

public static void main(){
  DataSource ds= ......;
  Connection conn = DataSourceUtil.getConnection(ds);//取得數據庫鏈接,會致使引用計數+1
  conn.setAutoCommit(false);//開啓事務
  conn.execute("update ...");//預先執行的 update 語句

  insertData();//執行數據庫插入

  if (test< 20){
    conn.rollback();//測試狀態不知足的條件下,將插入的測試數據連通以前的 update 一同回滾。
  }
  DataSourceUtil.releaseConnection(conn,ds);//釋放鏈接,引用計數-1
}
public static void insertData(){
  TransactionStatus status = tm.getTransaction(PROPAGATION_REQUIRED);//引用計數+1
  jdbc.execute("insert into ...");//執行插入語句,在執行過程當中引用計數會 +1,而後在-1
  tm.commit(status);//引用計數-1
}

    在上面這個例子中,第四行中開啓了事務。所以在執行 insertData 的時候事務管理器會認爲當前鏈接中已經存在事務,新的事務只須要加入到當前的事務中便可。並不須要獨立控制事務。所以在最後 insertData 方法結束的時,其插入的數據還未真正的插入到數據庫。隨後外部事務控制代碼又將全部數據都回滾了。

    上面的代碼修改一下以下:

public static void main(){
  DataSource ds= ......;
  Connection conn = DataSourceUtil.getConnection(ds);//取得數據庫鏈接,會致使引用計數+1
  conn.execute("update ...");//預先執行的 update 語句

  insertData();//執行數據庫插入,數據會進入數據庫。

  jdbc.execute("insert into ...");//這條插入的數據會被插入

  DataSourceUtil.releaseConnection(conn,ds);//釋放鏈接,引用計數-1
}
public static void insertData(){
  TransactionStatus status = tm.getTransaction(PROPAGATION_REQUIRED);//引用計數+1
  jdbc.execute("insert into ...");//執行插入語句,在執行過程當中引用計數會 +1,而後在-1
  tm.commit(status);//引用計數-1
}

    上面的代碼中咱們能夠看出,insertData 方法以外的程序並無控制事務。 insertData 中的事務管理器在建立事務對象的時候,因爲當前鏈接知足 new 狀態特徵。因此在最後 tm.commit(status) 時會執行事務遞交操做。

    而上面第8行,所使用的鏈接因爲也沒有開啓事務。因此它會以自動遞交事務的方式去運行。

關於 afterClear

    afterClear 階段的做用是將當前鏈接的狀態恢復到 getTransaction 方法以前。而且因爲 getTransaction 方法會持有當前鏈接(引用計數++),所以 afterClear 方法也會釋放掉這個鏈接的持有(引用計數--)。

相關文章
相關標籤/搜索