Hibernate 提供兩級緩存體系,位於持久化層,存儲數據庫數據副本,減少物理訪問:
| 維度 | 一級緩存 (Session) | 二級緩存 (SessionFactory) |
|---|---|---|
| 作用域 | 事務級 | 進程/集群級 |
| 生命週期 | Session 關閉即銷毀 | 應用運行期間持久 |
| 存儲形式 | 對象圖 | 脫管數據 |
| 並發策略 | 無需配置 | 四種可選 |
| 啟用方式 | 強制啟用 | 需配置插件 |
適合:極少修改、非關鍵業務、參考數據(如字典表)、單線程訪問
不適合:頻繁更新、財務核心數據、跨應用共享數據
隔離級別越高,並發性能越低,按業務選型。
SELECT * FROM table WHERE ... 獲取完整對象關鍵限制:僅對 ID 查詢 生效,條件查詢需開啟 Query Cache。
基於 Query Key(SQL + 參數 + 分頁信息)緩存結果集 ID 列表:
配置:<class name="User" table="user" lazy="true">
原理:session.load() 返回 CGLIB 動態代理對象 (User$$EnhancerByCGLIB$$...),屬性為 null。首次調用 getter 觸發回調,發送 SQL 加載真實對象並填充 CGLIB$CALLBACK_0.target。
關鍵點:僅 load() 生效,get() 立即查詢。
配置:<set name="addresses" lazy="true">
原理:Hibernate 自實現集合類型 (PersistentSet),內部維護 數據索引(ID 列表)。首次調用 iterator() 或 size() 觸發加載。
緩存策略陷阱:
<set lazy="true">
<cache usage="read-only"/> <!-- 僅緩存索引,不緩存實體! -->
<key column="user_id"/>
<one-to-many class="Address"/>
</set>
二次查詢時:找到索引 → 實體不在緩存 → 發送 N 條 SELECT * FROM address WHERE id=?。
修正:實體類也需配置 <class name="Address" ...><cache usage="read-write"/> 或集合配置 usage="read-write"。
配置:<property name="resume" type="java.sql.Clob" lazy="true"/>
原理:構建期增強字節碼,注入 CGLIB 回調。查詢時自動排除大字段:
SELECT id, age, name FROM user WHERE name='zx'; -- 首次查詢 SELECT resume FROM user WHERE id=1; -- 訪問 resume 時觸發
解決大對象(CLOB/BLOB)讀取性能痛點,避免手動拆表。
<class ...><cache usage="read-write"/><set ...><cache usage="read-write"/> + 實體也要配緩存hibernate.cache.use_query_cache=true + query.setCacheable(true)hibernate.bytecode.use_reflection_optimizer=false 並配置增強儀器LazyInitializationException → 使用 Open Session in View 模式或 DTO 轉換hibernate-enhance-maven-plugin深度掌握緩存與延遲加載,是 Hibernate 從「能用」到「好用」的關鍵分水嶺。