JPA 1.0 (JSR 220) 僅定義了一級緩存 (Persistence Context),二級緩存完全由提供商自行實現,導致跨提供商遷移極難。JPA 2.0 (JSR 317, 2009) 引入標準化的 二級緩存 API (javax.persistence.Cache)、緩存模式枚舉、查詢提示,統一了緩存的基本操作與行為契約。本文深度解析 JPA 2.0 緩存規範、核心接口、啟用配置、以及與 Hibernate 實現的映射關係。
| 特性 | 一級緩存 (L1) | 二級緩存 (L2) |
|---|---|---|
| 對應概念 | Persistence Context | Shared Cache (EntityManagerFactory 級) |
| 作用域 | EntityManager (通常 = 請求/事務) | 跨 EntityManager、跨事務、集群可共享 |
| 生命週期 | EM 關閉即銷毀 | EMF 存活期間 (應用級) |
| 強制性 | 規範強制實現 | 可選 (提供商可不支援) |
| 存儲形式 | 管理態實體實例 | 實體數據 (脫管狀態)、關聯集合、查詢結果 |
@Stateless
public class EmpBean {
@PersistenceContext
EntityManager em; // 隨事務開始/結束
public Employee add(Employee e) {
em.persist(e); // e 變為 managed
return e; // 事務提交時 flush,返回 detached
}
public Employee update(Employee detached) {
return em.merge(detached); // 返回 managed 實例
}
}
find/JPQL 返回同一實例 (== 比較為 true)。@Stateful
public class ShoppingCart {
@PersistenceContext(type = PersistenceContextType.EXTENDED)
EntityManager em; // 隨 Stateful Bean 生命週期
private Order order; // 跨請求保持 managed
public void addItem(Item item) {
order.addItem(item); // 直接操作 managed 實體,無需 merge
}
}
em.clear()。<persistence-unit name="pu">
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
<properties>
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
</properties>
</persistence-unit>
shared-cache-mode 四種模式:
ALL:所有實體緩存 (除非 @Cacheable(false))。NONE:禁用二級緩存。ENABLE_SELECTIVE (推薦):僅 @Cacheable(true) 實體緩存。DISABLE_SELECTIVE:預設緩存,@Cacheable(false) 排除。
注意:@Cacheable 是 JPA 標準開關,具體並發策略/區域需用提供商註解 (@org.hibernate.annotations.Cache)。
Cache cache = emf.getCache(); // 或 em.getEntityManagerFactory().getCache()
// 檢查實體是否在緩存
boolean exists = cache.contains(User.class, 1001L);
// 逐出單個實體
cache.evict(User.class, 1001L);
// 逐出整個實體類 (含子類)
cache.evict(User.class);
// 清空整個二級緩存
cache.evictAll();
限制:標準 API 僅提供基礎逐出操作,無法查詢統計、配置 TTL、區域劃分、預熱等進階功能。
JPA 2.0 定義兩個枚舉,通過 Query.setHint() 或 EntityManager.find() 的 Map<String,Object> properties 傳遞:
| 枚舉 | 屬性鍵 | 可選值 | 語義 |
|---|---|---|---|
| CacheRetrieveMode | javax.persistence.cache.retrieveMode | USE (預設) / BYPASS | 讀取時是否使用緩存。BYPASS 直接查 DB,但不更新緩存。 |
| CacheStoreMode | javax.persistence.cache.storeMode | USE (預設) / BYPASS / REFRESH | 寫入時是否更新緩存。REFRESH 強制從 DB 讀取並刷新緩存。 |
// 強制繞過緩存讀取最新數據
em.find(User.class, id, Map.of("javax.persistence.cache.retrieveMode", CacheRetrieveMode.BYPASS));
// 批量導入時不污染緩存
query.setHint("javax.persistence.cache.storeMode", CacheStoreMode.BYPASS);
// 刷新緩存 (如數據庫被外部系統修改)
em.find(User.class, id, Map.of("javax.persistence.cache.storeMode", CacheStoreMode.REFRESH));
| 優點 | 缺點/風險 | 最佳適用場景 |
|---|---|---|
| 避免重複 DB 查詢,大幅降低延遲 | 佔用堆/堆外內存,需容量規劃 | 讀多寫少、數據相對穩定的實體 (字典、配置、用戶資料) |
| 減輕 DB 壓力,提升吞吐 | 可能讀到陳舊數據 (需 REFRESH/BYPASS 控制) | 參考數據、產品目錄、權限表 |
| 集群環境下共享緩存 (分布式提供商) | 頻繁更新實體導致失效風暴、網絡廣播開銷 | 只讀/極少寫、高併發讀場景 |
| 增加併發控制複雜度 (樂觀鎖版本衝突) |
| 特性 | JPA 2.0 標準 | Hibernate 原生擴展 |
|---|---|---|
| 並發策略 | 無 (由提供商定義) | READ_ONLY / READ_WRITE / NONSTRICT_READ_WRITE / TRANSACTIONAL |
| 緩存區域劃分 | 無標準化 | region 屬性、區域級配置 (TTL、大小、驅逐策略) |
| 查詢緩存 | 未標準化 | use_query_cache、setCacheable(true)、QueryCacheRegion |
| 時間戳緩存 | 無 | UpdateTimestampsCache 自動失效查詢緩存 |
| 統計/監控 | 無 | hibernate.generate_statistics、JMX、Micrometer 整合 |
| 分布式緩存 | 無標準 SPI | Infinispan/Hazelcast/Redis RegionFactory、JCache (JSR-107) 橋接 |
| 自定義緩存鍵 | 無 | @CacheKey、NaturalId 緩存 |
JPA 2.0 為二級緩存建立了「最小可用標準」:@Cacheable、shared-cache-mode、CacheRetrieveMode/StoreMode、Cache 接口。這對於可移植性要求高、場景簡單的專案已足夠。但實際生產環境中,並發策略選擇、區域劃分、查詢緩存、分布式部署、統計監控 等關鍵能力均屬於提供商擴展領域。Hibernate 作為參考實現,提供了最完整、成熟的擴展生態。建議採用「標準註解開關 + 提供商註解精細控制」的混合模式,兼得可移植性與功能豐富度。
下篇預告:Hibernate 緩存架構深度解析、主流提供商 (Ehcache/Infinispan/Hazelcast/Redis) 整合實戰、生產環境調優清單、JCache (JSR-107) 標準化實踐。