Hibernate 二級緩存 (Second-Level Cache) 是 SessionFactory 級別的實體/查詢緩存,能顯著減少數據庫訪問。Ehcache 作為 Hibernate 經典默認緩存提供者,成熟穩定、配置靈活。本文系統講解註解驅動配置、五大併發策略選型、區域自定義、磁盤持久化、查詢緩存協同,附完整可運行代碼與控制台驗證。
org.hibernate
hibernate-ehcache
5.6.15.Final
org.hibernate.cache.ehcache.EhCacheRegionFactory
true
true
org.hibernate.cache.ehcache.EhCacheRegionFactory
/ehcache.xml
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY) // 關鍵:類級別啟用緩存
@Table(name = "t_region") // 只讀參考數據:省份、字典、配置
public class Region {
@Id @GeneratedValue
private Long id;
private String name;
private String code;
// getter/setter...
}
| 策略枚舉 | 適用場景 | 併發機制 | 性能 | 數據一致性 | 典型實體 |
|---|---|---|---|---|---|
| NONE | 不緩存 (默認) | - | - | - | - |
| READ_ONLY | **絕不修改**的參考數據 | 無鎖,直接讀緩存 | **最高** | 強一致 (數據不變) | 字典、配置、省市區、枚舉表 |
| NONSTRICT_READ_WRITE | 極少修改 (小時級以上),允許臟讀 | 異步失效,無嚴格鎖 | 高 | 最終一致 (短暫不一致) | 文章分類、標簽、低頻配置 |
| READ_WRITE | 頻繁讀寫,需嚴格一致性 | **時間戳/版本鎖**,事務提交前加鎖 | 中 | 強一致 (事務隔離) | 用戶資料、訂單、商品庫存 |
| TRANSACTIONAL | JTA 分佈式事務環境 | XA 事務參與,緩存即資源管理器 | 低 | 強一致 (分佈式) | 跨數據源業務、分佈式事務 |
選型鐵律:能用 READ_ONLY 絕不用其它 → 修改極低用 NONSTRICT_READ_WRITE → 必須強一致用 READ_WRITE → 分佈式事務才用 TRANSACTIONAL。
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
maxElementsInMemory="5000"
eternal="true"
overflowToDisk="false"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
/>
// 1. 實體已配置 @Cache
// 2. SessionFactory 級別開啟 use_query_cache=true
// 3. 查詢時顯式啟用
Session session = sessionFactory.openSession();
Query query = session.createQuery("from Region where code = :code", Region.class);
query.setParameter("code", "BJ");
query.setCacheable(true); // 關鍵:啟用查詢緩存
query.setCacheRegion("custom.region.query"); // 可選:指定自定義查詢緩存區域
List list = query.list(); // 首次查數據庫,後續走緩存
// 驗證:控制台只打印一次 SQL
// Hibernate: select ... from t_region where code=?
// Region: Beijing
// Region: Beijing (第二次無 SQL)
// 第一次查詢
Hibernate: select r.id, r.name, r.code from t_region r where r.code=?
Region: Beijing
// 第二次查詢 (命中查詢緩存 + 實體緩存)
// 無 SQL 輸出!
Region: Beijing
// 磁盤文件檢查 (overflowToDisk=true 且內存滿時)
/tmp/
com.example.entity.Region.data (實體緩存數據)
com.example.entity.User.data
org.hibernate.cache.internal.StandardQueryCache.data (查詢緩存)
org.hibernate.cache.spi.UpdateTimestampsCache.data (時間戳)
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "user.cache.region")
public class User { ... }
// 查詢指定區域
query.setCacheRegion("user.cache.region"); // 實體緩存區域名
// 或獨立查詢緩存區域
query.setCacheRegion("custom.query.region");
| 坑點 | 現象 | 解決 |
|---|---|---|
忘記 use_query_cache=true | 查詢緩存不生效,每次發 SQL | 配置文件明確啟用 |
實體無 @Cache | 二級緩存不存儲實體,查詢緩存無法命中實體 | 實體類添加註解 |
| READ_WRITE 實體無版本鎖 | 併發修改導致緩存不一致 | 實體添加 @Version 樂觀鎖 |
| 集合緩存未配置 | 一對多集合每次查數據庫 | 關聯端添加 @Cache |
| 緩存區域名稱衝突 | 不同實體共用區域導致數據混亂 | 顯式指定 region 屬性 |
| 集群環境單機 Ehcache | 多節點緩存不一致 | 遷移至 Ehcache 3 + Terracotta / Redis / Hazelcast 分佈式緩存 |
| 事務提交前緩存可見 | 未提交數據被其他事務讀到 (READ_WRITE 有鎖保護) | 確保事務隔離級別、正確使用 @Transactional |
Hibernate + Ehcache 二級緩存通過實體註解 @Cache + 併發策略選型 + 區域精細配置 + 查詢緩存協同,實現透明的數據庫訪問加速。核心原則:參考數據 READ_ONLY、業務數據 READ_WRITE+版本鎖、查詢緩存顯式啟用、集群環境換分佈式緩存。配置完畢後,通過控制台 SQL 輸出驗證「二次查詢無 SQL」是最直觀的成功標誌。