首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Hibernate 事務與併發控制 (一):Session 作用域、工作單元、應用事務模式

     編輯:Java 相關知識

核心設計哲學

Hibernate 直接使用 JDBC/JTA,**不添加額外鎖定、不改變隔離級別**,僅提供自動版本管理。使用體驗接近原生 JDBC,但消除了樣板代碼。強烈建議先掌握 JDBC 事務、ANSI SQL 隔離級別、目標數據庫鎖行為。

Session 與事務作用域

SessionFactory:重量級、線程安全、單例

  • 啟動時由 Configuration 創建一次,所有線程共享
  • 昂貴資源:連接池、二級緩存、編譯映射元數據

Session:輕量級、非線程安全、單業務單元

  • 按需獲取 JDBC 連接 (Lazy),用完即丟棄
  • 即使不確定是否需要數據訪問,也可放心 openSession()/close()
  • 嚴禁多線程共享 Session 實例

數據庫事務:越短越好

長事務持有鎖導致併發度下降,擴展性受限。原則:事務邊界盡可能貼近業務操作。

工作單元模式

反模式:session-per-operation

單次 DB 調用開關 Session — **極其低效**,破壞事務原子性。Hibernate 禁用 JDBC auto-commit。

標準模式:session-per-request

**多用戶 Client/Server 最常用**:

  1. 請求到達服務端
  2. 開啟 Session
  3. 執行業務單元所有 DB 操作
  4. 響應準備就緒 → flush() + commit()
  5. 關閉 Session

一請求一 Session 一事務,1:1:1 對應。實現關鍵:

  • 攔截器:Servlet Filter / Spring OpenSessionInViewFilter / EJB 攔截器,在請求進入/離開時管理 Session
  • ThreadLocal 綁定:將 Session 綁定到處理線程,DA 代碼像訪問靜態變量一樣獲取
  • 事務上下文同樣可用 ThreadLocal 管理

此模式稱 ThreadLocal Session 或 Open Session in View。

應用程序事務 (長事務)

場景:跨請求的用戶交互

  1. 首屏加載數據 (Session1 + Tx1)
  2. 用戶思考修改 (5 分鐘...)
  3. 點擊保存 → 持久化修改 (Session2 + Tx2)
  4. 期望:期間無人修改同一數據 (樂觀鎖)

反模式:用戶思考期間保持 Session/事務開啟

長事務持鎖 → 併發崩潰。**絕對禁止**。

正確方案:多數據庫事務實現一個應用事務

只有最後提交事務寫入數據,前序事務僅讀取。Hibernate 三大特性支撐:

模式核心機制優點缺點
session-per-request-with-detached-objects請求間對象脫管,saveOrUpdate/merge 重新關聯,自動版本檢查Session 輕量、無長連接、適合 Web 無狀態需管理脫管狀態、合併邏輯
session-per-application-transaction (Long Session)單 Session 跨請求,disconnect()/reconnect() 斷開/恢復連接,自動版本檢查無重新加載、最高效 DB 訪問、對象身份保證Session 累積髒數據大、不宜序列化傳 Web 層、需狀態 EJB 持有
應用級版本檢查每次新 Session 手動重新加載比較版本概念簡單效率最低、代碼冗長、不推薦

對象標識與併發

雙重標識語義

  • 數據庫標識:foo.getId().equals(bar.getId())
  • JVM 標識:foo == bar

Session 內等價,Session 外不等價

  • 同一 Session 範圍:Hibernate 保證 foo == bar ⇔ 同一行數據
  • 不同 Session:加載同一行得到不同實例 (foo != bar),但 foo.getId().equals(bar.getId())
  • 併發衝突通過同步時自動版本檢查解決,無需應用層加鎖

脫管對象的 equals/hashCode 契約

不要用數據庫 ID 實現 equals/hashCode (瞬時對象 ID 為 null,持久化後改變,破壞 Set/Map 契約)。
使用業務鍵:唯一、通常不變的業務屬性組合 (如用戶名、郵箱、產品代碼)。只需保證同一集合內對象業務鍵穩定。

常見陷阱與避坑

  1. session-per-user-session / session-per-application:極少數例外外**絕對禁止**。Session 非線程安全,併發請求導致競態;長生命周期導致 OOM、髒數據泛濫、事務邊界模糊
  2. HttpSession 中存 Session:若必須,需同步訪問 HttpSession (用戶狂點刷新產生併發線程)
  3. 異常處理:Hibernate 異常 → 必須回滾事務 + 關閉 Session + 停止應用 (綁定應用場景)。回滾**不會**恢復對象狀態,數據庫與對象不同步,但異常通常不可恢復,重新開始即可
  4. Session 緩存膨脹:一級緩存持有所有加載對象。長 Session/大量加載 → OOM。對策:clear()/evict()、批量操作用無狀態 Session/存儲過程 (第14章)

數據庫事務聲明原則

  • 永遠使用顯式事務聲明,即使只讀操作 (取決於隔離級別/數據庫能力,但無害)
  • 非托管環境:手動 begin/commit/rollback
  • 托管環境 (J2EE/EJB):容器管理事務 (CMT) 聲明式配置,Session 同步可自動
  • 可移植性:使用 Hibernate Transaction API 封裝底層差異 (除 CMT 外強烈推薦)

Session 結束四階段

  1. 同步 Session (flush)
  2. 提交事務
  3. 關閉 Session
  4. 異常處理

下一章將詳細展開托管/非托管環境下的具體代碼模式與異常處理細節。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved