Hibernate 提供兩級緩存:
Transaction.commit() 時(先清理再提交)Session.flush()evict(Object obj):清除特定持久化物件clear():清空所有持久化物件hibernate-ehcache)與 ehcache.xml 至類路徑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"/>
.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>
<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 無效,需雙重啟用:
hibernate.cfg.xml:<property name="cache.use_query_cache">true</property>query.setCacheable(true) 或 criteria.setCacheable(true)查詢緩存依賴二級緩存。
// 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
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>
<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>
// 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(); // 重新查詢資料庫