Hibernate 緩存分為兩級:一級緩存 (Session 級) 與 二級緩存 (SessionFactory 級)。理解其生命週期、作用域與配置方式,是優化 ORM 性能的關鍵。
save()、get()、load()、update()、saveOrUpdate()、查詢接口Session session = HibernateUtil.getSession();
session.beginTransaction();
// 第一次查詢:發送 SQL
User user1 = (User) session.get(User.class, 1);
System.out.println("用戶名:" + user1.getName());
// 輸出:Hibernate: select ... from tb_user_info where id=?
// 用戶名:xqh
// 第二次查詢:直接從一級緩存獲取,無 SQL
User user2 = (User) session.get(User.class, 1);
System.out.println("用戶名:" + user2.getName());
// 輸出:用戶名:xqh (無 SQL 打印)
session.getTransaction().commit();
HibernateUtil.closeSession(session);
load() 可能返回代理對象,首次訪問非 ID 屬性時才查詢數據庫get() 立即查詢並放入緩存session.clear() 或 session.evict(entity) 可手動清理<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>5.4.x</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.x</version>
</dependency>
<ehcache xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"
updateCheck="false" monitoring="autodetect">
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
memoryStoreEvictionPolicy="LRU"/>
<!-- 實體緩存配置 -->
<cache name="com.xqh.model.User"
maxEntriesLocalHeap="5000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"/>
<!-- 集合緩存配置 -->
<cache name="com.xqh.model.User.orders"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"/>
<!-- 查詢緩存配置 -->
<cache name="org.hibernate.cache.internal.StandardQueryCache"
maxEntriesLocalHeap="1000"
eternal="false"
timeToLiveSeconds="300"/>
<cache name="org.hibernate.cache.spi.UpdateTimestampsCache"
maxEntriesLocalHeap="5000"
eternal="true"/>
</ehcache>
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.factory_class">
org.hibernate.cache.ehcache.EhCacheRegionFactory
</property>
<property name="hibernate.cache.use_query_cache">true</property>
<property name="hibernate.generate_statistics">true</property>
<!-- 實體緩存策略 -->
<class-cache class="com.xqh.model.User" usage="read-write"/>
<class-cache class="com.xqh.model.Order" usage="read-only"/>
<!-- 集合緩存策略 -->
<collection-cache collection="com.xqh.model.User.orders" usage="read-write"/>
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@org.hibernate.annotations.Cache(region = "com.xqh.model.User")
public class User {
@Id @GeneratedValue
private Long id;
@OneToMany(mappedBy = "user")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<Order> orders = new HashSet<>();
// ...
}
public class SecondLevelCacheTest {
public static void main(String[] args) {
// Session 1
Session session1 = HibernateUtil.getSession();
session1.beginTransaction();
User user1 = (User) session1.get(User.class, 1L);
System.out.println("Session1 查詢: " + user1.getName());
session1.getTransaction().commit();
session1.close(); // 一級緩存銷毀
// Session 2 (新 Session)
Session session2 = HibernateUtil.getSession();
session2.beginTransaction();
User user2 = (User) session2.get(User.class, 1L);
System.out.println("Session2 查詢: " + user2.getName());
session2.getTransaction().commit();
session2.close();
// 輸出結果:
// Session1 查詢: Hibernate: select ... from user where id=?
// 用戶名:xqh
// Session2 查詢: 用戶名:xqh (無 SQL,命中二級緩存)
}
}
Query query = session.createQuery("FROM User WHERE name = :name");
query.setParameter("name", "xqh");
query.setCacheable(true); // 啟用查詢緩存
query.setCacheRegion("user.byName"); // 指定緩存區域
List<User> list = query.list();
| 策略 | 適用場景 | 一致性 | 性能 |
|---|---|---|---|
| read-only | 參考數據、字典表、靜態配置 | 最高 | 最佳 |
| read-write | 讀多寫少、可容忍短暫髒讀 | 強 (鎖機制) | 良好 |
| nonstrict-read-write | 極少更新、嚴格一致性要求低 | 最終一致 | 優 |
| transactional | 需完全 ACID、支持 JTA 事務 | 最高 | 一般 |
<collection-cache>sessionFactory.getCache().evictQueryRegion()@Version 支持樂觀鎖,配合 read-write 策略| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| Ehcache (RMI/JGroups) | 輕量、純 Java、無外部依賴 | 集群同步延遲、網絡分區風險 | 小型集群、低一致性要求 |
| Redis (Hibernate-Redis) | 高性能、原生集群、數據結構豐富 | 需維護 Redis 集群、序列化開銷 | 中大型應用、高併發場景 |
| Infinispan | JBoss 官方、分佈式、事務支持強 | 配置複雜、資源佔用較大 | 企業級應用、強一致性需求 |
| Hazelcast | 內存網格、自動發現、SQL 查詢 | 商業版功能收費 | 微服務架構、緩存即服務 |
合理配置 Hibernate 緩存可將數據庫訪問減少 60%-90%,但關鍵在於「緩存什麼、如何失效、如何監控」。建議從只讀參考數據開始,逐步擴展到核心業務實體,並建立完善的緩存監控儀表板。