閱讀 Hibernate Reference 第11章11.3后我的總結以及理解:
(1)
帶有版本標記的 Optimistic Concurency Control 是唯一既能提供高度并發,又能提供可擴展性的解決方案。就是說,很多人可以一起對一些數據同時進行操作,而這些操作又盡量不會造成互相沖突。
(2)在并發要求低的環境里(沒有很多用戶,沒有很多更新,大家不會同時對一個數據操作,或者即使造成沖突后果也不嚴重),
如果干脆不進行版本檢查,會得到“last commit wins”的結果,就是說最后 commit 的數據覆蓋之前 commit 的數據。如果兩個對話重疊,之前commit 的數據可能被后 commit 的數據覆蓋,從而丟失。而且整個過程沒有任何出錯,會造成用戶迷惑。
(3)Extended session and automatic versioning
用戶的會話 (conversation) 通常包括多個步驟,即取出數據,操作數據,然后保存數據回數據庫。這樣的會話會跨越幾個 http 來回,持續一段時間。
Extended session and automatic versioning 在整個會話中只使用一個 hibernate session。在會話初始獲得 session,僅在會話結束時進行 session.flush(); ... session.close()。
一個 hibernate session 并不對應一個 JDBC connection。開始一個transaction的時候獲得一個新的連接,并且 resume session;commit transaction 的時候切斷 hibernate session 和 JDBC connection 的連接,也就是將 connection 還給連接池。在一個 hibernate session 中,也就是,在這種方式(Extended session)中,仍然動態地使用 JDBC connection,不會造成占用太多 connection。早先版本的 hibernate 需要寫代碼 disconnect / recommect 一個 hibernate session?,F在已經不需這樣做(deprecated)。開始/結束 transaction 產生同樣效果。
如果在session和JDBC connection重新連接的時候,如果希望強制檢查
應用程序沒有進行更新,但是有可能被其他 transaction 更新的對象的版本,可以用該對象以及LockMode.READ作為參數調用
Session.lock()。Session.lock()的文檔如下:“
Obtain the specified lock level upon the given object. This may be used to perform a version check (LockMode.READ), to upgrade to a pessimistic lock (LockMode.UPGRADE), or to simply reassociate a transient instance with a session (LockMode.NONE). This operation cascades to associated instances if the association is mapped with cascade="lock". ”Session.lock()的聲明如下:
public void lock(Object object,
LockMode lockMode)
throws HibernateException
采用 extended session 這種方式,所使用的 Session 通常被設置為
FlushMode.MANUAL,這樣僅僅在最后一個 transaction 周期(上面說到,一個Session可以有多個transaction)才允許進行向數據庫保存數據的操作。這個最后的 transaction 包含了 flush() 和 close() 操作,來結束對話。
采用 extended session 的問題是,session 可能保存了很多數據,占據很大空間,而用戶考慮的時間可能很長,就是說會話持續的時間很長,這樣保存 session 在一個長時間的會話中可能會造成困難。比如說,HttpSession中應該只保存很少的數據。(HttpSession可能要在各個服務器之間傳遞)因為 Session 實際上是所有取出來的對象的第一級緩存,因此最好僅僅將 extended session 用在有限的幾個 request / response 組成的會話中。一個 Session 只用在一個會話中,因為(否則?)Session中很快就會出現不同步(陳舊)的數據。
Disconnected session 應該被放在持久層??梢园阉旁?EJB stateful session bean 中。不要把 Session 交給 web 層,或者放在 HttpSession 中。
使用 session-per-conversion (也就是 extended session),更難實現自動 current session context 管理(沒法用 ThreadLocalSessionContext了)。需要自己實現 CurrentSessionContext。更多的需要閱讀 Hibernate Wiki。
(4)Detached objects and automatic versioning
這種方法,
在每次和數據庫打交道的時候打開一個新的 Session。但是每次和數據庫打交道的時候重復使用同樣的持久對象實例。應用程序從一個Session中調出持久對象,對它們(此時已經 detached)進行操作,然后調用另一個 Session 的 update(), saveOrUpdate() 或者 merge(),將它們 reattach 回這個 Session。
注意 update(), saveOrUpdate(), merge() 的區別。引用 Hibernate Reference 10.7 “Usually update() or saveOrUpdate() are used in the following scenario: ...... and merge() is very different”
Hibernate 在 flush 的時候檢查版本,如果有沖突,拋出異常。
如果確信(筆者認為應該說希望確信)對象沒有被更改,可以如同上面所說的調用 lock(),而不是update()。
(5)定制自動版本檢查
映射屬性 optimistic-lock 定制自動版本檢查。
false: 關閉檢查
all: 檢查所有的欄位,而不是版本欄位。這種方法可以對沒有版本欄位也無法添加版本欄位的數據庫表進行檢查。理論上來說,這種方法只能用于 extended session 而不是 session per request with detached objects,也就是上面說的方法(3)而不是(4)。原因是,這種方法需要在 Session 中保存有老的數據,以便比較老的數據和數據庫中的當前數據,來確定是否有沖突。
dirty: 僅僅在 flush 時比較更改過的(dirty)數據在數據庫中有沒有被其他 transaction 修改過,以便避免沖突。這種方法可以允許同時修改一行數據中的不重疊的字段。我的理解,dirty 同 all 一樣,都只能在 extended session 中使用,以便利用 extended session 中保存的老的數據進行比較(版本檢查)。
所有以上情況,不管是使用版本欄目,還是 all 或 dirty,Hibernate 都在一個 UPDATE 語句中完成檢查和更新(在 Where 從句中設置檢查條件)。
最后,如果設置了 transitive persistence 到關聯管理的從屬 entity,hibernate 可能會執行不必要的 update。這通常不是問題。但是,如果有數據庫 trigger 關聯到 update 時,可能實際上沒有更新但是會引發 trigger??梢栽O置 select-before-update="true" 強制 hibernate 在更新前進行 select。