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

Hibernate 事務與併發控制 (二):JTA 整合、樂觀/悲觀鎖、異常處理

     編輯:Java 相關知識

托管環境下的事務策略

非托管環境 (Standalone / Servlet 容器)

Hibernate 自管理連接池,開發者手動控制事務邊界:


Session sess = factory.openSession();
Transaction tx = null;
try {
    tx = sess.beginTransaction();
    // 業務邏輯
    tx.commit();
} catch (RuntimeException e) {
    if (tx != null) tx.rollback();
    throw e;
} finally {
    sess.close(); // 釋放 JDBC 連接
}
  • commit() 自動觸發 flush(),無需顯式調用
  • close() 釋放連接回池,標誌 Session 結束
  • 代碼可移植至 JTA 環境 (只需換 TransactionFactory)
  • 異常處理應在應用頂層 (如 Servlet Filter、AOP),持久層只拋出
  • 配置 hibernate.transaction.factory_class = org.hibernate.transaction.JDBCTransactionFactory (默認)

JTA 環境 (應用服務器 / EJB)

數據源連接自動加入全局 JTA 事務,兩種整合模式:

BMT (Bean Managed Transaction)

代碼與非托管環境完全一致,但 TransactionFactory 換為 JTATransactionFactory:


// 同樣的 begin/commit/rollback 代碼
// 配置: hibernate.transaction.factory_class = org.hibernate.transaction.JTATransactionFactory
// 必須設置: hibernate.transaction.manager_lookup_class (如 JBoss, WebLogic, WebSphere 專用類)

CMT (Container Managed Transaction) - 推薦

事務聲明在 ejb-jar.xml 中配置,無編程事務代碼。關鍵配置:


// TransactionFactory
hibernate.transaction.factory_class = org.hibernate.transaction.CMTTransactionFactory
// 自動同步/關閉 Session (可選,強烈建議開啟)
hibernate.transaction.flush_before_completion = true
hibernate.transaction.auto_close_session = true

異常回滾:拋出未檢查異常 (RuntimeException) 容器自動標記回滾。無需使用 Hibernate Transaction API。

CMT 核心模式:getCurrentSession()


// 極簡用法
Session sess = factory.getCurrentSession(); // 綁定到 JTA 事務上下文
// 業務操作...
// 容器負責提交/回滾、Session 同步/關閉
  • Session 生命週期完全由 Hibernate 管理,綁定 JTA 事務
  • 同一 JTA 事務中多次調用返回同一 Session
  • 自動 flush_before_completion + auto_close_session

CMT 陷阱:ScrollableResults/Iterator 游標洩漏

JTA 規范限制導致 after_statement 連接釋放模式下,scroll()/iterate() 產生的游標無法自動關閉。必須在 finally 塊顯式關閉:


ScrollableResults sr = sess.createQuery("...").scroll();
try {
    while (sr.next()) { ... }
} finally {
    sr.close(); // 或 Hibernate.close(iterator)
}

異常處理體系

核心原則

  1. Session 拋出異常 → 立即 rollback() + close() + 拋棄 Session 實例
  2. 所有 Hibernate 異常視為不可恢復
  3. finally 塊必須調用 close()

異常層次


HibernateException (非檢查異常, 封裝大多數錯誤)
    └─ JDBCException (SQLException 封裝)
         ├─ JDBCConnectionException        -- 通訊失敗
         ├─ SQLGrammarException            -- SQL 語法錯誤
         ├─ ConstraintViolationException   -- 約束違反 (唯一鍵、外鍵等)
         ├─ LockAcquisitionException       -- 鎖獲取失敗
         └─ GenericJDBCException           -- 其他
  • JDBCException.getCause() 獲取原始 SQLException
  • 轉換由 SQLExceptionConverter 執行,可通過 dialect 或自定義實現配置
  • 非檢查異常設計:頂層統一處理 (記錄日誌、用戶友好提示、事務回滾),持久層不捕獲

樂觀並發控制 (Optimistic Concurrency Control)

唯一兼顧高併發與高擴展性的方案:版本號/時間戳檢測更新衝突,防止更新丟失。

三種實現層次

1. 應用級版本檢查 (最低效,類 EJB 2.x)


// 每次交互新 Session,顯式重新加載比較版本
Session sess = factory.openSession();
Transaction t = sess.beginTransaction();
int oldVer = foo.getVersion();
sess.load(foo, foo.getKey()); // 重新加載當前狀態
if (oldVer != foo.getVersion()) throw new StaleObjectStateException();
foo.setProperty("bar");
t.commit();
sess.close();

<version> 映射自動維護版本號。低併發環境可跳過檢查 (Last Commit Wins),但用戶可能無感知丟失更新。

2. 長生命周期 Session + 自動版本化 (最高效)


// foo 始終關聯同一 Session
sess.reconnect();           // 獲取新 JDBC 連接
Transaction t = sess.beginTransaction();
foo.setProperty("bar");
t.commit();                 // 同步+版本檢查
sess.disconnect();          // 歸還連接
  • Session 斷開連接等待用戶交互,數據庫訪問視角最高效
  • 無需重新加載對象,應用層無感知版本檢查
  • Session.lock(obj, LockMode.READ) 強制檢查只讀對象版本
  • 可用 hibernate.connection.release_mode 替代顯式 reconnect/disconnect
  • 缺點:Session 作為一級緩存累積髒數據,不適合長對話/大量數據;不能序列化傳遞到 Web 層,應由有狀態 EJB 持有

3. 脫管對象 + 自動版本化 (最常用,適合 Web)


// 每次請求新 Session,脫管對象重新關聯
foo.setProperty("bar");           // 脫管狀態修改
Session sess = factory.openSession();
Transaction t = sess.beginTransaction();
sess.saveOrUpdate(foo);           // 或 merge() 若可能已加載
t.commit();
sess.close();
  • 同步時自動版本檢查,衝突拋 StaleObjectStateException
  • 確信未修改可用 lock(obj, LockMode.READ) 替代 update() (繞過緩存、僅版本檢查)
  • 適合 session-per-request-with-detached-objects 模式

自動版本化行為定制

注意:all/dirty 需比較新舊狀態,只能用長 Session 模式,不能用脫管對象模式。

悲觀鎖定 (Pessimistic Locking)

Hibernate 從不在內存加鎖,完全依賴數據庫鎖機制。LockMode 定義級別:

映射屬性效果適用場景
optimistic-lock="false"屬性/集合髒時不增版本號非關鍵數據 (如瀏覽次數、最後登錄時間)
optimistic-lock="all"無版本/時間戳列,比較所有列遺留表無版本列、多應用共享表
optimistic-lock="dirty"僅比較髒列允許非交錯並發修改 (不同字段)
select-before-update="true"更新前先 SELECT 確認實際修改數據庫觸發器敏感、級聯更新產生無效 UPDATE
LockMode觸發方式SQL 對應
WRITEHibernate 自動 (更新/插入時)行級排他鎖 (隱式)
UPGRADESession.load(obj, LockMode.UPGRADE) / Query.setLockMode()SELECT ... FOR UPDATE
UPGRADE_NOWAITOracle 專用SELECT ... FOR UPDATE NOWAIT
READ隔離級別 Repeatable Read/Serializable / 顯式指定共享鎖 (數據庫依賴)
NONE事務結束 / 新加載對象無鎖

顯式鎖定 API


// 1. 加載時指定
Object obj = sess.load(Entity.class, id, LockMode.UPGRADE);

// 2. 已加載對象升級鎖
sess.lock(obj, LockMode.UPGRADE); // 執行版本檢查 + SELECT FOR UPDATE

// 3. 查詢級鎖定
Query q = sess.createQuery("from Entity where ...");
q.setLockMode(LockMode.UPGRADE);
List list = q.list();
  • UPGRADE/UPGRADE_NOWAIT:若對象已在較低鎖模式加載,調用 lock() 升級
  • READ/UPGRADE/UPGRADE_NOWAIT 均執行版本檢查
  • 數據庫不支持指定鎖模式 → Hibernate 自動降級替代,**不拋異常**,保證可移植性

總結建議

  1. 首選樂觀鎖:<version> + 脫管對象模式 (saveOrUpdate/merge),適配 95% Web 場景
  2. CMT + getCurrentSession():EJB/Spring 環境下最簡潔,事務聲明式、Session 自管
  3. 悲觀鎖慎用:僅在極高衝突業務 (如庫存扣減) 或遺留數據庫觸發器要求下使用
  4. 異常頂層處理:持久層拋出,AOP/Filter 捕獲回滾+日誌+用戶提示
  5. 避免長 Session 緩存膨脹:大批量操作用無狀態 Session / 存儲過程 / 手動 clear()/evict()
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved