Hibernate4實戰 之第五部分:Hibernate的事務和並發 – JAVA編程語言程序開發技術文章

Hibernate本身沒有事務的實現
Hibernate 直接使用 JDBC 連接和 JTA 資源,不添加任何附加鎖定行為。也就是說你在Hibernate裡面使用的事務要麼是JDBC的事務,要麼是JTA的事務。
Hibernate不鎖定內存中的對象
你的應用程序會按照你的數據庫事務的隔離級別規定的那樣運作,真正對事務的實現和支持也依賴於數據庫。
 
對於並發處理,Hibernate提供瞭樂觀鎖和悲觀鎖來進行並發處理
Hibernate對自動樂觀並發控制提供版本管理,針對行級悲觀鎖定,Hibernate 也提供瞭輔助的(較小的)API,它使用瞭 SELECT FOR UPDATE 的 SQL 語法
Hibernate的Session是和事務聯系在一起的
可以通過Session去獲取事務的接口,從而進行事務的控制。
 
數據庫事務應該盡可能的短
這樣能降低數據庫中的鎖爭用。數據庫長事務會阻止你的應用程序擴展到高的並發負載。因此,假若在用戶思考期間讓數據庫事務開著,直到整個工作單元完成才關閉這個事務,這絕不是一個好的設計。
這就引出一個問題:一個操作單元,也就是一個事務單元的范圍應該是多大?
一個操作一個?一個請求一個?一個應用一個?
 
反模式:session-per-operation
在單個線程中, 不要因為一次簡單的數據庫調用,就打開和關閉一次 Session!數據庫事務也是如此。也就是說應該禁止自動事務提交(auto-commit)。
 
session-per-request
最常用的模式是 每個請求一個會話。 在這種模式下,來自客戶端的請求被發送到服務器端,即 Hibernate 持久化層運行的地方,一個新的 Hibernate Session 被打開,並且執行這個操作單元中所有的數據庫操作。一旦操作完成(同時對客戶端的響應也準備就緒),session 被同步,然後關閉。會話和請求之間的關系是一對一的關系。
Hibernate 內置瞭對“當前 session(current session)” 的管理,用於簡化此模式。你要做的一切就是在服務器端要處理請求的時候,開啟事務,在響應發送給客戶之前結束事務,通常使用Servelt Filter來完成。
 
非托管環境下
所謂非托管,指的是:應用程序沒有托管到J2EE環境中,通常由Hibernate自己來負責管理數據庫連接池。應用程序開發人員必須手工設置事務聲明,換句話說,就是手工啟動,提交,或者回滾數據庫事務。
 
使用JTA
又有兩種方式,一種是在Hibernate配置裡面修改transaction的factory類,從而在程序裡面可以直接使用Hibernate的事務API,也就是程序不用變化。
另外一種方式就是直接通過JNDI去查找UserTransaction,然後直接在程序裡面使用JTA的接口來控制事務。
應用程序級別的版本檢查
簡單點說,就是由應用程序自己實現版本檢查來確保對話事務的隔離,從數據訪問的角度來說是最低效的,不推薦使用。
 
擴展周期的session和自動版本化
Hibernate 使用擴展周期的 Session 的方式,或者脫管對象實例的方式來提供自動版本檢查。單個 Session 實例和它所關聯的所有持久化對象實例都被用於整個對話,這被稱為 sessionper-
conversation。
Hibernate 在同步的時候進行對象實例的版本檢查,如果檢測到並發修改則拋出異常。由開發人員來決定是否需要捕獲和處理這個異常(通常的抉擇是給用戶 提供一個合並更改,或者在無臟數據情況下重新進行業務對話的機會)。
在等待用戶交互的時候, Session 斷開底層的 JDBC 連接。這種方式以數據庫訪問的角度來說是最高效的方式。應用程序不需要關心版本檢查或脫管對象實例的重新關聯,在每個數據庫事務中,應用程序也不需要載入讀取對象實例。
應用程序級別的版本檢查
簡單點說,就是由應用程序自己實現版本檢查來確保對話事務的隔離,從數據訪問的角度來說是最低效的,不推薦使用。
 
擴展周期的session和自動版本化
Hibernate 使用擴展周期的 Session 的方式,或者脫管對象實例的方式來提供自動版本檢查。單個 Session 實例和它所關聯的所有持久化對象實例都被用於整個對話,這被稱為 sessionper-conversation。
Hibernate 在同步的時候進行對象實例的版本檢查,如果檢測到並發修改則拋出異常。由開發人員來決定是否需要捕獲和處理這個異常(通常的抉擇是給用戶 提供一個合並更改,或者在無臟數據情況下重新進行業務對話的機會)。
在等待用戶交互的時候, Session 斷開底層的 JDBC 連接。這種方式以數據庫訪問的角度來說是最高效的方式。應用程序不需要關心版本檢查或脫管對象實例的重新關聯,在每個數據庫事務中,應用程序也不需要載入讀取對象實例。
通常不需要自己去管理鎖定策略
Hibernate 總是使用數據庫的鎖定機制,從不在內存中鎖定對象。因而用戶並不需要花很多精力去擔心鎖定策略的問題。通常情況下,隻要為 JDBC 連接指定一下隔離級別,然後讓數據庫去搞定一切就夠瞭。
然而,高級用戶有時候希望進行一個排它的悲觀鎖定,或者在一個新的事務啟動的時候,重新進行鎖定。
 
類 LockMode 定義瞭Hibernate 所需的不同的鎖定級別
1:當更新或者插入一行記錄的時候,鎖定級別自動設置為LockMode.WRITE。
2:當用戶顯式的使用數據庫支持的 SQL 格式 SELECT … FOR UPDATE 發送 SQL 的時候,鎖定級別設置為 LockMode.UPGRADE。
3:當用戶顯式的使用 Oracle 數據庫的 SQL 語句 SELECT … FOR UPDATE NOWAIT 的時候,鎖定級別設置 LockMode.UPGRADE_NOWAIT。
4:當 Hibernate 在“可重復讀”或者是“序列化”數據庫隔離級別下讀取數據的時候,鎖定模式自動設置為 LockMode.READ。這種模式也可以通過用戶顯式指定進行設置。
5:LockMode.NONE 代表無需鎖定。在 Transaction 結束時, 所有的對象都切換到該模式上來。與session 相關聯的對象通過調用 update() 或者 saveOrUpdate() 脫離該模式。
 
顯示的指定鎖定模式
1:調用 Session.load() 的時候指定鎖定模式(LockMode)。
2:調用 Session.lock()。
3:調用 Query.setLockMode()。
顯示指定鎖定模式的說明
1:如果在 UPGRADE 或者 UPGRADE_NOWAIT 鎖定模式下調用 Session.load(),並且要讀取的對象尚未被session 載入過,那麼對象通過 SELECT … FOR UPDATE 這樣的 SQL 語句被載入。如果為一個對象調用 load() 方法時,該對象已經在另一個較少限制的鎖定模式下被載入瞭,那麼 Hibernate 就對該對象調用 lock() 方法。
2:如果指定的鎖定模式是 READ,UPGRADE 或 UPGRADE_NOWAIT,那麼 Session.lock() 就執行版本號檢查。(在 UPGRADE 或者 UPGRADE_NOWAIT 鎖定模式下,執行 SELECT … FOR UPDATE這樣的SQL語句。)
3:如果數據庫不支持用戶設置的鎖定模式,Hibernate 將使用適當的替代模式(而不是扔出異常)。這一點可以確保應用程序的可移植性。
 作者:jinnianshilongnian

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *