JPA的鎖機制

JPA 各類實體鎖模式的區別

字數2084 閱讀304 評論0 

爲了可以同步訪問實體,JPA提供了2種鎖機制。這兩種機制均可以免兩個事務中的其中一個,在不知情的狀況下覆蓋另外一個事務的數據。html

經過實體鎖,咱們一般但願避免在兩個並行事務中產生以下情形:java

  1. Adam的事務讀取數據 X
  2. Barbara的事務讀取數據 X
  3. Adam的事務修改數據 X,並將其修改成 XA
  4. Adam的事務寫入數據 XA
  5. Barbara的事務修改數據 X,並將其修改成 XB
  6. Barbara的事務寫入數據 XB

結果是,Adam所作的修改徹底被Barbara所覆蓋掉了,可是Barbara對此卻絕不知曉。像這樣的狀況一般被稱爲「髒讀」。顯然,咱們但願的結果是Adam寫入 XA,而Barbara須要在寫入 XB以前檢查對 XA 的修改。數據庫

樂觀鎖的工做原理

樂觀鎖基於的假設是實際中衝突不多發生,即便發生,拋出一個錯誤也比想辦法避免它們更容易接受和簡單。在樂觀鎖中,容許一個事務正確完成,但另外一個事務須要拋出異常並回滾,而且必須被從新執行或者丟棄。apache

咱們還以Adam和Barbara爲例,下面是一個使用樂觀鎖可能發生的情形:api

  1. Adam的事務讀取數據 X
  2. Barbara的事務讀取數據 X
  3. Adam的事務修改數據 X,並將其修改成 XA
  4. Adam的事務寫入數據 XA
  5. Barbara的事務修改數據 X,並將其修改成 XB
  6. Barbara的事務試圖寫入數據 XB,可是收到一個錯誤
  7. Barbara須要讀取數據 XA(或者從新開始一個新的事務)
  8. Barbara的事務修改數據 XA,並將其修改成 XAB
  9. Barbara的事務寫入數據 XAB

如你所見,Barbara被強制要求檢查Adam的修改,而且她能夠選擇繼續修改Adam的結果並保存(合併修改)。最後的數據將同時包括Adam和Barbara的修改。oracle

樂觀鎖徹底由JPA控制。它須要在DB表中額外存儲一個版本號列。它徹底依靠於底層用來存儲關係型數據的DB引擎來工做。ui

悲觀鎖的工做原理

對於某些人來講,悲觀鎖更容易接受。當事務須要修改一個可能被其餘事務同時修改的實體時,事務會發起一個命令將實體鎖住。全部的鎖會持續到事務結束後再自動釋放。spa

使用悲觀鎖的情形可能以下所示:設計

  1. Adam的事務讀取數據 X
  2. Adam的事務鎖住 X
  3. Barbara的事務但願讀取數據 X,可是由於 X 已經被鎖住,只好等待
  4. Adam的事務修改數據 X,並將其修改成 XA
  5. Adam的事務寫入數據 XA
  6. Barbara的事務讀取數據 XA
  7. Barbara的事務修改數據 XA,並將其修改成 XAB
  8. Barbara的事務寫入數據 XAB

如你所見,Barbara又一次被強制的寫入 XAB,同時也包含了Adam的修改。可是,這個方案與樂觀鎖徹底不一樣——Barbara須要等待Adam的事務完成之後纔可以讀取數據。更甚的是,爲了讓該場景正確工做,咱們須要在兩個事務中都手動發起一個lock命令。(由於咱們並不肯定那個事務先運行,因此兩個事務都須要在修改數據前先進行鎖定)雖然樂觀鎖要爲每一個實體增長一個版本列,比悲觀鎖工做略多,可是以後咱們不須要再在事務中發起鎖操做了。JPA會自動完成全部的檢查,咱們只須要處理可能的異常便可。code

悲觀鎖使用底層數據庫提供的鎖機制來鎖住表中已有的記錄。JPA須要知道如何觸發這些鎖,而且尚不能徹底支持某些數據庫。

即便是JPA規範中也說到,不須要提供PESSIMISTIC_READ(由於許多DB只支持WRITE鎖):

容許JPA實現用LockModeType.PESSIMISTIC_WRITE來代替LockModeType.PESSIMISTIC_READ,可是反之不可。

JPA中可用的鎖類型

首先,我想說,對於實體中有添加了@Version註解的列,JPA會自動對該實體使用樂觀鎖。你不須要使用任何鎖命令。可是,你能夠在任什麼時候候發起一個如下類型的鎖:

  1. LockModeType.Optimistic
    1. 這就是默認的鎖類型。也是如ObjectDB所說一般被你們所忽略的鎖類型。在個人印象中,只有在須要動態獲取並傳遞鎖類型時,纔會用到它,即便咱們很清楚最後的鎖是OPTIMISTIC的。雖然這個例子不太恰當,可是一個好的API設計,即便是默認值也應該爲其提供一個可選項。
    2. 示例:Java
LockModeType lockMode = resolveLockMode();
A a = em.find(A.class, 1, lockMode);
  1. LockModeType.OPTIMISTIC_FORCE_INCREMENT

    1. 這個選項不多被用到。可是若是你但願用另外一個實體來鎖住對當前實體的引用,就須要使用它。換句話說,即便當前實體沒有被修改,可是其餘實體可能由於當前實體被修改,你就能夠用它來鎖住對當前實體的引用。
    2. 示例:
      1. 假設咱們有兩個實體「書(Book)」和「書架(Shelf)」。咱們能夠將書添加到書架中,可是書不持有對其書架的引用。咱們須要對全部移動書到其餘書架的動做加鎖,以免一本書被放在2個書架上。爲了鎖住這個動做,光鎖住當前的書架實體是不夠的,由於書可能尚未放到某個書架上。鎖住全部書架也不合理,由於他們在不一樣的事務中可能都是不一樣的。惟一合理的是鎖住書實體自己,即便在咱們這個例子中它並無發生變化(由於它並不持有其書架的引用)。
  2. LockModeType.PESSIMISTIC_READ

    1. 這個模式相似於LockModeType.PESSIMISTIC_WRITE,可是有一點不一樣:若是沒有事務對實體加寫鎖,那麼就不能阻塞對該實體的讀取。它還容許其餘事務使用LockModeType.PESSIMISTIC_READ來加鎖。WRITE鎖和READ鎖之間的區別,已經被這兩篇文章(here (ObjectDB) 和 here (OpenJPA))很詳細的說明了。可是,不只由於規範中容許,並且許多實現也沒有分開處理,因此該鎖模式常常被等價於LockModeType.PESSIMISTIC_WRITE
  3. LockModeType.PESSIMISTIC_WRITE

    1. 這是LockModeType.PESSIMISTIC_READ的加強版。當WRITE鎖發生時,JPA在數據庫的幫助下,會阻止其餘事務讀取實體,而不像READ鎖那樣只禁止寫入。
  4. LockModeType.PESSIMISTIC_FORCE_INCREMENT

    1. 這是另外一個不多使用的鎖模式。可是,它能夠用來結合PESSIMISTICOPTIMISTIC時使用。在如下場景中,單獨使用PESSIMISTIC_WRITE是無效的:

      1. 事務A使用樂觀鎖並讀取實體E
      2. 事務B請求實體E上的WRITE鎖
      3. 事務B提交併釋放E上的鎖
      4. 事務A更新E並提交
    2. 在步驟4中,若是事務B沒有增長版本列的值,那麼就沒法阻止事務A覆蓋B的修改。即便事務B使用的是悲觀鎖,鎖模式LockModeType.PESSIMISTIC_FORCE_INCREMENT也會強制事務B更新版本號,並讓事務A失敗並拋出OptimisticLockException

爲了發起一個指定類型的鎖,JPA提供瞭如下方法:

你可使用JPA中這兩種鎖機制中的任意一種。若是須要,也能夠選擇悲觀鎖類型PESSIMISTIC_FORCE_INCREMENT,把兩者混起來用。

轉自:http://www.jianshu.com/p/4bc01d3c980a

相關文章
相關標籤/搜索