承接上篇,本文深入非托管環境與 JTA 托管環境的事務處理差異、BMT/CMT 兩種整合策略、CurrentSessionContext 機制、異常處理分層原則,以及連線釋放模式對效能的影響。
獨立應用、Servlet 容器(Tomcat/Jetty)無 JTA 時,Hibernate 管理 JDBC 事務:
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
// 業務操作
doWork(session);
tx.commit(); // 自動觸發 session.flush()
} catch (RuntimeException e) {
if (tx != null) tx.rollback();
throw e; // 向上拋出,由上層統一處理
} finally {
session.close(); // 釋放 JDBC 連線
}
commit() 隱含 flush(),無需顯式呼叫close() 釋放連線,歸還連線池JDBCTransactionFactory)應用伺服器(WildFly、WebLogic、WebSphere)管理全域事務,Hibernate 透過 TransactionFactory 整合。
開發者在 Session Bean 中程式化控制事務邊界,代碼與非托管環境幾乎相同:
@Resource UserTransaction utx;
@PersistenceContext EntityManager em; // 或 SessionFactory
public void businessMethod() {
utx.begin();
try {
Session session = factory.openSession();
// 業務操作
doWork(session);
utx.commit();
} catch (Exception e) {
utx.rollback();
throw e;
} finally {
session.close();
}
}
配置:hibernate.transaction.factory_class = org.hibernate.transaction.JTATransactionFactory
事務邊界由容器根據部署描述符(或 @TransactionAttribute)自動管理:
@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRED) // 預設
public class OrderService {
@PersistenceContext EntityManager em;
public void placeOrder(Order order) {
em.persist(order); // 參與容器事務
// 無需 begin/commit/rollback
// 拋出 RuntimeException 容器自動標記 rollback-only
}
}
CMT 關鍵配置:
hibernate.transaction.factory_class = org.hibernate.transaction.CMTTransactionFactoryhibernate.transaction.manager_lookup_class:指定應用伺服器 JTA TransactionManager 查找類hibernate.current_session_context_class = jta:綁定 Session 到 JTA 事務上下文啟用自動同步與關閉(預設行為):
// 無需手動 open/close/commit Session session = factory.getCurrentSession(); // 綁定到 JTA 事務 doWork(session); // 事務提交時自動 flush、close
若需手動控制:設定 hibernate.transaction.flush_before_completion=false、hibernate.transaction.auto_close_session=false。
| 實作類別 | 適用環境 | 綁定機制 | 取得方式 |
|---|---|---|---|
| JTASessionContext | JTA/CMT | JTA 事務上下文 | getCurrentSession() |
| ThreadLocalSessionContext | 非托管/BMT | ThreadLocal | getCurrentSession() |
| ManagedSessionContext | 舊版/自定義 | 手動 bind/unbind | getCurrentSession() |
最佳實踐:DAO/Service 僅呼叫 sessionFactory.getCurrentSession(),由配置決定實際綁定策略,業務代碼零耦合環境。
HibernateException(unchecked)包裝所有底層錯誤ConstraintViolationException),轉換為領域異常(DuplicateEmailException)RuntimeException,檢查型異常不回滾(可配置)@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(DuplicateEmailException.class)
public ResponseEntity handleDuplicate(DuplicateEmailException e) {
return ResponseEntity.status(HttpStatus.CONFLICT).body(new ErrorResponse("Email already exists"));
}
@ExceptionHandler(StaleObjectStateException.class)
public ResponseEntity handleOptimisticLock(StaleObjectStateException e) {
return ResponseEntity.status(HttpStatus.CONFLICT).body(new ErrorResponse("Data modified by another user, please refresh"));
}
}
| 模式 (hibernate.connection.release_mode) | 行為 | 適用場景 |
|---|---|---|
| auto (預設) | JTA: after_statement; 非JTA: after_transaction | 通用 |
| on_close | Session.close() 時釋放 | 舊版相容、長對話 |
| after_transaction | 事務提交/回滾時釋放 | 非托管環境標準 |
| after_statement | 每個 statement 執行後立即釋放 | JTA 環境、高併發短事務 |
關鍵限制:after_statement 模式下,ScrollableResults 與 Iterator(scroll()/iterate() 產生)的底層游標無法自動關閉,必須在 finally 區塊顯式關閉。
除樂觀鎖(@Version),Hibernate 提供 LockMode 支援資料庫行級悲觀鎖(SELECT ... FOR UPDATE):
// 明確鎖定
session.lock(entity, LockMode.PESSIMISTIC_WRITE);
// 或查詢時鎖定
session.createQuery("from Order o where o.id=:id")
.setParameter("id", id)
.setLockMode("o", LockMode.PESSIMISTIC_WRITE)
.uniqueResult();
注意:悲觀鎖增加資料庫鎖持有時間,降低併發,僅在極高衝突場景短暫使用。
掌握這些核心機制,你就能駕馭 Hibernate 在高併發、分散式、雲原生環境下穩健運行,專注於業務創新而非基礎設施坑點。