在 Hibernate 框架中,Session 是應用程序操縱持久化數據的核心接口。它不僅提供了基本的 CRUD 方法,更內建了強大的一級緩存機制,並管理著持久化對象的完整生命週期。本文將系統梳理 Session 的緩存原理、四大對象狀態轉換、關鍵 API 的行為差異,並結合源碼與實戰測試,幫助開發者建立清晰的心智模型。
Session 內部維護著一個 Map 結構的緩存,稱為 Hibernate 一級緩存。緩存中的對象即為持久化對象,每個持久化對象與數據庫中的一條記錄一一對應。同一個 Session 實例中,相同 ID 的實體對象在內存中唯一存在,這是 Hibernate 保證對象一致性的基石。
從持久化角度看,Hibernate 將對象劃分為四種狀態:
@Test
public void testSessionCache() {
News news1 = (News) session.get(News.class, 1);
News news2 = (News) session.get(News.class, 1);
System.out.println(news1 == news2); // true,同一實例
}
輸出 SQL 顯示:第一次查詢發送 SELECT,第二次直接從緩存返回,無 SQL 產生。這證明了get()會優先檢查一級緩存。
flush() 強制將 Session 緩存中的修改同步到數據庫(發送 SQL),但不提交事務。關鍵觸發時機:
Transaction.commit() 內部調用)save() 瞬間(需立即生成 ID)session.flush()源碼追蹤:JdbcTransaction.beforeTransactionCommit() → managedFlush() → 觸發同步。
refresh() 會發送 SELECT 語句,用數據庫最新數據覆蓋緩存對象。常用於:數據庫被外部修改(觸發器、定時任務)後,需要同步到緩存。
注意:MySQL 默認隔離級別 REPEATABLE READ 可能導致同一事務內多次讀取結果一致,需在配置文件設置 hibernate.connection.isolation=2(READ COMMITTED)才能看到外部修改。
clear():清空整個 Session 緩存,所有持久化對象變為游離狀態。evict(obj):將指定對象從緩存移除,該對象變為游離狀態。| 特性 | save() | persist() |
|---|---|---|
| 返回值 | 返回生成的 ID | void |
| ID 非空對象 | 視為新對象,插入新記錄 | 拋出異常 |
| 事務邊界外 | 可能立即執行 INSERT | 不保證立即執行 |
ObjectNotFoundException。若 Session 已關閉再訪問代理屬性,拋出 LazyInitializationException。update():將游離對象變為持久化,無條件發送 UPDATE(可通過 select-before-update=true 優化)。若緩存中已有同 ID 對象,拋出異常。saveOrUpdate():根據 ID 判斷:ID 為 null → save();ID 不為 null → update()。若 ID 不為 null 但數據庫無記錄,拋出異常。刪除持久化或游離對象均可。刪除後對象變為刪除狀態,但 Java 端 ID 仍存在。若需將 ID 置 null,可配置 hibernate.use_identifier_rollback=true。
當需要調用存儲過程或執行原生 SQL 時,使用 session.doWork(new Work() { ... }) 獲取 Connection 直接操作。

核心轉換路徑:
save()/persist()/saveOrUpdate()update()/saveOrUpdate()/lock()/merge()evict()/clear()/close()delete()get() 判斷存在性,避免 load() 導致的異常處理複雜度。flush() 控制 SQL 發送時機,避免大事務中緩存過大導致 OOM。merge()(返回新持久化對象)而非 update(),避免同 ID 衝突。clear() 釋放緩存,防止內存洩漏。Session 作為 Hibernate 的核心,其緩存機制與狀態管理是高效、正確使用 ORM 的關鍵。深入理解一級緩存的生命週期、四大狀態的轉換規則、以及核心 API 的語義差異,能幫助開發者避開常見坑點,編寫出更健壯的數據訪問層代碼。