Hibernate 提供了多種數據獲取方式(Session.load、Session.get、Query.list、Query.iterator),每種方式與一級緩存、二級緩存、查詢緩存的交互機制截然不同。選擇恰當的策略,直接決定應用程式的效能表現。本文將系統梳理這四種獲取方式的緩存行為,幫助開發者做出最優決策。
| 緩存層級 | 作用域 | 生命週期 | 可配置性 | Key/Value 結構 |
|---|---|---|---|---|
| 一級緩存 | Session | Session 關閉即銷毀 | 不可配置(內建) | Key: PO 主鍵 ID / Value: PO 實例 |
| 二級緩存 | SessionFactory | 與應用生命週期一致(進程/集群級) | 可配置(class-cache、collection-cache) | Key: PO 主鍵 ID / Value: PO 實例 |
| 查詢緩存 | SessionFactory | 與應用生命週期一致 | 可配置(預設關閉,cache.use_query_cache=true 啟用) | Key: 查詢語句+參數+分頁 / Value: 結果集或 ID 列表 |
關鍵差異:
class-cache:實體屬性任意變更即自動更新collection-cache:僅當集合元素增減時自動更新,元素屬性變更不觸發更新select po.property 存完整結果集;from Po 僅存主鍵 ID 列表,後續透過 Session.load 組裝對象,節省記憶體。<class-cache>)lazy="true":直接發 SQL 查詢資料庫,填充一級、二級緩存lazy="true":返回代理對象,首次訪問非 ID 屬性時才觸發資料庫查詢實戰提醒:長 Session 場景下,需適時呼叫 session.clear() 或 session.evict(entity) 強制刷新一級緩存(特別是單向關聯維護方)。二級緩存會在實體增刪改時自動失效更新。
流程與 load 幾乎相同,唯一差別在於:緩存均未命中時,直接查詢資料庫,不返回代理。適合需要確認實體是否存在的場景。
cacheable="true")Session.load(id) 組裝對象(可能觸發 N 次查詢)注意:實體數據任意變更會導致相關查詢緩存整體失效(清空)。
執行 SELECT id FROM ... 僅獲取符合條件的 ID 列表;iterator.next() 時才逐個呼叫 Session.load(id) 載入實體。
適用場景:擁有高命中率二級緩存、查詢參數多變(導致查詢緩存命中率低)時,iterator 避免了重複構建查詢緩存 Key 的開銷,效率優於 list。
show_sql=true 觀察實際 SQL 執行情況,據此調整策略