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 結束hibernate.transaction.factory_class = org.hibernate.transaction.JDBCTransactionFactory (默認)數據源連接自動加入全局 JTA 事務,兩種整合模式:
代碼與非托管環境完全一致,但 TransactionFactory 換為 JTATransactionFactory:
// 同樣的 begin/commit/rollback 代碼
// 配置: hibernate.transaction.factory_class = org.hibernate.transaction.JTATransactionFactory
// 必須設置: hibernate.transaction.manager_lookup_class (如 JBoss, WebLogic, WebSphere 專用類)
事務聲明在 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。
// 極簡用法
Session sess = factory.getCurrentSession(); // 綁定到 JTA 事務上下文
// 業務操作...
// 容器負責提交/回滾、Session 同步/關閉
flush_before_completion + auto_close_sessionJTA 規范限制導致 after_statement 連接釋放模式下,scroll()/iterate() 產生的游標無法自動關閉。必須在 finally 塊顯式關閉:
ScrollableResults sr = sess.createQuery("...").scroll();
try {
while (sr.next()) { ... }
} finally {
sr.close(); // 或 Hibernate.close(iterator)
}
rollback() + close() + 拋棄 Session 實例finally 塊必須調用 close()
HibernateException (非檢查異常, 封裝大多數錯誤)
└─ JDBCException (SQLException 封裝)
├─ JDBCConnectionException -- 通訊失敗
├─ SQLGrammarException -- SQL 語法錯誤
├─ ConstraintViolationException -- 約束違反 (唯一鍵、外鍵等)
├─ LockAcquisitionException -- 鎖獲取失敗
└─ GenericJDBCException -- 其他
JDBCException.getCause() 獲取原始 SQLExceptionSQLExceptionConverter 執行,可通過 dialect 或自定義實現配置唯一兼顧高併發與高擴展性的方案:版本號/時間戳檢測更新衝突,防止更新丟失。
// 每次交互新 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),但用戶可能無感知丟失更新。
// foo 始終關聯同一 Session
sess.reconnect(); // 獲取新 JDBC 連接
Transaction t = sess.beginTransaction();
foo.setProperty("bar");
t.commit(); // 同步+版本檢查
sess.disconnect(); // 歸還連接
Session.lock(obj, LockMode.READ) 強制檢查只讀對象版本hibernate.connection.release_mode 替代顯式 reconnect/disconnect
// 每次請求新 Session,脫管對象重新關聯
foo.setProperty("bar"); // 脫管狀態修改
Session sess = factory.openSession();
Transaction t = sess.beginTransaction();
sess.saveOrUpdate(foo); // 或 merge() 若可能已加載
t.commit();
sess.close();
StaleObjectStateExceptionlock(obj, LockMode.READ) 替代 update() (繞過緩存、僅版本檢查)session-per-request-with-detached-objects 模式| 映射屬性 | 效果 | 適用場景 |
|---|---|---|
optimistic-lock="false" | 屬性/集合髒時不增版本號 | 非關鍵數據 (如瀏覽次數、最後登錄時間) |
optimistic-lock="all" | 無版本/時間戳列,比較所有列 | 遺留表無版本列、多應用共享表 |
optimistic-lock="dirty" | 僅比較髒列 | 允許非交錯並發修改 (不同字段) |
select-before-update="true" | 更新前先 SELECT 確認實際修改 | 數據庫觸發器敏感、級聯更新產生無效 UPDATE |
| LockMode | 觸發方式 | SQL 對應 |
|---|---|---|
| WRITE | Hibernate 自動 (更新/插入時) | 行級排他鎖 (隱式) |
| UPGRADE | Session.load(obj, LockMode.UPGRADE) / Query.setLockMode() | SELECT ... FOR UPDATE |
| UPGRADE_NOWAIT | Oracle 專用 | SELECT ... FOR UPDATE NOWAIT |
| READ | 隔離級別 Repeatable Read/Serializable / 顯式指定 | 共享鎖 (數據庫依賴) |
| NONE | 事務結束 / 新加載對象 | 無鎖 |
// 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 均執行版本檢查<version> + 脫管對象模式 (saveOrUpdate/merge),適配 95% Web 場景clear()/evict()