首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Hibernate 二級緩存完整實戰:Ehcache 整合、類/集合/查詢緩存配置、並發策略、時間戳區域、批量處理最佳實踐

     編輯:Java 綜合教學

Hibernate 二級緩存完整實戰:Ehcache 整合、類/集合/查詢緩存配置、並發策略、時間戳區域、批量處理最佳實踐

二級緩存是 SessionFactory 級別的進程/叢集範圍緩存,能大幅減少資料庫查詢。本文以 Ehcache 為例,系統講解整合步驟、四種並發策略、類緩存/集合緩存/查詢緩存三大維度配置、時間戳緩存區域原理、批量處理建議與完整驗證代碼。

一、二級緩存架構與並發策略

策略對應隔離級別適用場景Ehcache 支援
Read-Only (只讀)Serializable參考數據、從不修改✅ 最快、無鎖
Nonstrict-Read-Write (非嚴格讀寫)Read Uncommitted極少修改、允許髒讀✅
Read-Write (讀寫)Read Committed常規業務、需一致性✅ 推薦預設
Transactional (事務型)Repeatable Read高一致性、JTA 環境❌ Ehcache 不支援 (需 JBoss Cache/Infinispan)

二、Ehcache 整合四步驟

1. Maven 依賴

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-ehcache</artifactId>
    <version>5.6.15.Final</version> <!-- 對應 Hibernate 版本 -->
</dependency>
<!-- net.sf.ehcache:ehcache 會傳遞引入 -->

2. hibernate.cfg.xml 啟用並指定 RegionFactory

<property name="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>

3. 配置實體類/集合緩存 (兩種方式擇一)

方式 A:hibernate.cfg.xml 內 <class-cache>/<collection-cache>

<class-cache class="com.example.Department" usage="read-write"/>
<class-cache class="com.example.Employee" usage="read-write"/>
<collection-cache collection="com.example.Department.emps" usage="read-write"/>

方式 B:映射文件 .hbm.xml 內 <cache>

<class name="Department" table="department">
    <cache usage="read-write"/>
    <set name="emps" inverse="true">
        <cache usage="read-write"/> <!-- 集合緩存 -->
        <key column="dept_id"/>
        <one-to-many class="Employee"/>
    </set>
</class>

4. ehcache.xml 微調策略 (放在 classpath 根目錄)

<ehcache xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="ehcache.xsd">
    <diskStore path="java.io.tmpdir"/>
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        memoryStoreEvictionPolicy="LRU"/>

    <!-- 針對特定區域 (實體全限定名) 精細控制 -->
    <cache name="com.example.Employee"
        maxElementsInMemory="5000"
        eternal="false"
        timeToIdleSeconds="300"
        timeToLiveSeconds="600"
        overflowToDisk="true"/>

    <cache name="com.example.Department.emps"
        maxElementsInMemory="2000"
        eternal="false"
        timeToIdleSeconds="180"
        timeToLiveSeconds="300"
        overflowToDisk="true"/>
</ehcache>

ehcache.xml 關鍵屬性解析

屬性含義
maxElementsInMemory記憶體中最多緩存對象數,超過按策略淘汰或溢寫磁碟
eternaltrue=永不過期 (忽略 TTL/TTI),false=依 timeToLive/Idle 過期
timeToIdleSeconds (TTI)對象閒置最大秒數,超過則過期淘汰
timeToLiveSeconds (TTL)對象生存最大秒數 (從建立起算),必須 ≥ TTI
overflowToDisk記憶體滿時是否溢寫磁碟 (path 由 diskStore 指定)
memoryStoreEvictionPolicyLRU (最近最少使用) / LFU (最不常用) / FIFO

三、三大緩存維度驗證代碼與 SQL 分析

1. 實體緩存 (Entity Cache)

@Test
public void testEntityCache() {
    // 第一次查詢
    Employee e1 = session.get(Employee.class, 1L); // 發 SQL: select ...
    tx.commit(); session.close();

    // 新 Session (模擬二級緩存命中)
    session = sf.openSession(); tx = session.beginTransaction();
    Employee e2 = session.get(Employee.class, 1L); // **無 SQL**,直接從二級緩存取
    assertSame(e1, e2); // 同一物件實例 (Ehcache 返回實際物件)
}

2. 集合緩存 (Collection Cache) - 關鍵:必須同時緩存集合擁有者與元素實體

@Test
public void testCollectionCache() {
    // 僅開啟 Department 類緩存
    Department d1 = session.get(Department.class, 1L); // SQL: select dept
    d1.getEmps().size(); // SQL: select emps (集合未緩存)

    // 同時開啟 Department 類緩存 + Department.emps 集合緩存 + Employee 實體緩存
    Department d2 = session.get(Department.class, 1L); // 無 SQL (類命中)
    d2.getEmps().size(); // 無 SQL (集合命中,返回 Employee ID 列表)
    // 但遍歷 Employee 仍會逐個查詢 → **必須同時開啟 Employee 實體緩存**
    for (Employee e : d2.getEmps()) {
        System.out.println(e.getName()); // 若 Employee 有二級緩存 → 無 SQL
    }
}

3. 查詢緩存 (Query Cache) - 依賴二級緩存 + 時間戳區域

@Test
public void testQueryCache() {
    // 必須啟用 hibernate.cache.use_query_cache=true
    Query q = session.createQuery("from Employee");
    q.setCacheable(true); // 關鍵:標記此查詢可緩存

    List<Employee> list1 = q.list(); // 發 SQL,結果存入查詢緩存 (key: 查詢語句+參數, value: ID 列表)
    List<Employee> list2 = q.list(); // **無 SQL**,從查詢緩存取 ID 列表 → 再從實體二級緩存取物件

    // 修改數據後,時間戳區域更新,查詢緩存自動失效
    Employee e = session.get(Employee.class, 1L);
    e.setName("Modified");
    // 提交事務 → 更新時間戳區域
    List<Employee> list3 = q.list(); // 發新 SQL (查詢緩存已失效)
}

時間戳緩存區域原理

  • 每個表對應一個時間戳條目,記錄最後異動時間 (INSERT/UPDATE/DELETE)。
  • 查詢緩存條目攜帶執行時刻的表時間戳快照。
  • 再次查詢時,比對當前表時間戳與快照,若表已更新 → 緩存失效 → 重新查詢。
  • 這機制保證查詢緩存**最終一致性**,無需手動清理。

四、Session 管理模式與批量處理

Session 管理三種模式

  1. 綁定線程:CurrentSessionContext + hibernate.current_session_context_class=thread,sessionFactory.getCurrentSession() 自動參與事務、隨事務關閉。
  2. 綁定 JTA 事務:jta 模式,應用伺服器管理事務邊界。
  3. 手動管理:openSession()、beginTransaction()、commit()、close() 完全手控。

批量處理最佳實踐

@Test
public void testBatchInsert() {
    // 方案 1:JDBC 批次 (推薦大數據量,繞過 Hibernate 機制)
    session.doWork(connection -> {
        try (PreparedStatement ps = connection.prepareStatement("INSERT INTO employee (name, salary) VALUES (?, ?)")) {
            for (int i = 0; i < 10000; i++) {
                ps.setString(1, "Emp" + i);
                ps.setDouble(2, 5000 + i);
                ps.addBatch();
                if (i % 500 == 0) ps.executeBatch(); // 分批提交
            }
            ps.executeBatch();
        }
    });

    // 方案 2:Hibernate 批次 (較小量、需實體邏輯)
    // hibernate.jdbc.batch_size=50 (配置檔)
    // session.setJdbcBatchSize(50);
    // for (int i=0; i<1000; i++) { session.save(new Employee(...)); if (i%50==0) { session.flush(); session.clear(); } }
}

五、總結:二級緩存配置檢查清單

  1. ☐ hibernate-ehcache 依賴版本與 Hibernate 核心一致。
  2. ☐ cache.use_second_level_cache=true + EhCacheRegionFactory。
  3. ☐ 實體類 <class-cache usage="read-write"/> 或 <cache usage="read-write"/>。
  4. ☐ 集合關係 <collection-cache collection="Owner.field" usage="read-write"/> + **元素實體同樣開啟類緩存**。
  5. ☐ 查詢緩存:use_query_cache=true + query.setCacheable(true)。
  6. ☐ ehcache.xml 針對熱點區域調整 TTL/TTI/淘汰策略/磁碟溢寫。
  7. ☐ 叢集環境需換用支援分散式的 RegionFactory (Infinispan/Hazelcast/Terracotta)。
  8. ☐ 批量寫入用 JDBC doWork + PreparedStatement.addBatch(),避免一級緩存 OOM。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved