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

Hibernate 緩存機制深度解析:一級緩存、二級緩存與查詢緩存實戰

     編輯:Java 綜合教學

Hibernate 緩存架構概覽

Hibernate 提供兩級緩存:

  • 一級緩存(Session 級):事務範圍緩存,由 Hibernate 自動管理,不可關閉。
  • 二級緩存(SessionFactory 級):進程範圍緩存,需顯式啟用。分為內置緩存(唯讀,載入映射檔初始化 SQL)與外置緩存(可讀寫,資料庫資料副本,儲存於記憶體或硬碟)。

一級緩存詳解

核心作用

  1. 減少資料庫存取頻率
  2. 保證資料庫記錄與緩存物件一致性(透過髒檢查同步)

自動清理時機

  • 呼叫 Transaction.commit() 時(先清理再提交)
  • 執行查詢操作導致持久化物件屬性變化時
  • 顯式呼叫 Session.flush()

手動管理 API

  • evict(Object obj):清除特定持久化物件
  • clear():清空所有持久化物件

二級緩存配置與使用

啟用步驟

  1. 引入 Ehcache 相關 JAR(hibernate-ehcache)與 ehcache.xml 至類路徑
  2. 配置 hibernate.cfg.xml:
    <property name="cache.use_second_level_cache">true</property>
    <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
    <class-cache usage="read-write" class="com.example.Employee"/>
  3. 或在 .hbm.xml 中配置:<cache usage="read-write"/>

集合級二級緩存

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

<set name="emps" ...>
  <cache usage="read-write"/>
  ...
</set>

注意:集合元素對應的持久化類別也必須啟用二級緩存,否則會產生 N+1 查詢問題。

Ehcache 配置範例(ehcache.xml)

<ehcache>
  <diskStore path="d:\\tempDirectory"/>
  <defaultCache maxElementsInMemory="10000" eternal="false"
      timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"/>
  <cache name="com.example.Employee" maxElementsInMemory="1"
      eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="600" overflowToDisk="true"/>
  <cache name="com.example.Department.emps" maxElementsInMemory="1000"
      eternal="true" timeToIdleSeconds="0" timeToLiveSeconds="0" overflowToDisk="false"/>
</ehcache>

查詢緩存

預設對 HQL/QBC 無效,需雙重啟用:

  1. hibernate.cfg.xml:<property name="cache.use_query_cache">true</property>
  2. 程式碼:query.setCacheable(true) 或 criteria.setCacheable(true)

查詢緩存依賴二級緩存。

實體類別與映射檔範例

Department.java / Department.hbm.xml

// Department.java
public class Department {
    private Integer id;
    private String name;
    private Set<Employee> emps = new HashSet<>();
    // getters/setters/toString
}

// Department.hbm.xml
<class name="Department" table="GG_DEPARTMENT">
  <id name="id"><generator class="native"/></id>
  <property name="name"/>
  <set name="emps" table="GG_EMPLOYEE" inverse="true" lazy="true">
    <key><column name="DEPT_ID"/></key>
    <one-to-many class="Employee"/>
  </set>
</class>

Employee.java / Employee.hbm.xml

// Employee.java
public class Employee {
    private Integer id;
    private String name;
    private float salary;
    private String email;
    private Department dept;
    // getters/setters/toString/constructors
}

// Employee.hbm.xml
<class name="Employee" table="GG_EMPLOYEE">
  <id name="id"><generator class="native"/></id>
  <property name="name"/>
  <property name="salary"/>
  <property name="email"/>
  <many-to-one name="dept" class="Department">
    <column name="DEPT_ID"/>
  </many-to-one>
</class>

完整 hibernate.cfg.xml 配置

<session-factory>
  <!-- DB 連線設定 -->
  <property name="connection.username">scott</property>
  <property name="connection.password">java</property>
  <property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
  <property name="connection.url">jdbc:oracle:thin:@localhost:1521:orcl</property>
  <property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
  <property name="show_sql">true</property>
  <property name="format_sql">true</property>
  <property name="hbm2ddl.auto">update</property>

  <!-- 二級緩存 -->
  <property name="cache.use_second_level_cache">true</property>
  <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
  <property name="cache.use_query_cache">true</property>

  <mapping resource="Department.hbm.xml"/>
  <mapping resource="Employee.hbm.xml"/>
  <class-cache usage="read-write" class="com.example.Employee"/>
  <class-cache usage="read-write" class="com.example.Department"/>
  <collection-cache usage="read-write" collection="com.example.Department.emps"/>
</session-factory>

查詢緩存優化:iterate() vs list()

// list() 查詢所有欄位,反射組裝完整物件
List<Employee> emps = query.list();

// iterate() 僅查 ID,再從二級緩存取物件(需二級緩存命中率高)
Iterator<Employee> empIt = query.iterate();
while (empIt.hasNext()) {
    System.out.println(empIt.next().getName());
}

適用場景:資料表欄位多、二級緩存命中率高。若緩存未命中會逐筆查資料庫,效能反而更差。

時間戳緩存區域機制

  • QueryCache 區域:儲存查詢結果
  • UpdateTimestampCache 區域:儲存相關表異動時間戳
  • 查詢前比較兩區域時間戳,若 T2 > T1 則捨棄緩存重查
Query query = session.createQuery("FROM Employee");
query.setCacheable(true);
List<Employee> emps = query.list();

Employee emp = session.get(Employee.class, 100);
emp.setSalary(30000);  // 觸發 update,更新時間戳

emps = query.list();  // 重新查詢資料庫
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved