歡迎來到 Hibernate 快取 – 第一級快取範例教程。最近,我們查看了 Hibernate 架構、hibernate 對映 以及如何使用 HQL 以面向對象的方式觸發 SQL 查詢。今天我們將深入研究 Hibernate 的一個重要方面 – Hibernate 快取。
Hibernate 快取
如果正確使用,Hibernate 快取可以在提高應用程序性能方面非常有用。快取的想法是減少數據庫查詢的次數,從而減少應用程序的吞吐量時間。Hibernate 提供不同類型的快取:
- 第一層快取: Hibernate第一層快取與Session物件相關聯。Hibernate第一層快取預設為啟用,且無法停用。然而,Hibernate提供方法,可以從快取中刪除選定的物件或完全清除快取。在Session中快取的任何物件對其他Session不可見,並且當Session關閉時,所有快取的物件也將遺失。
- 第二層快取: Hibernate第二層快取預設為停用,但我們可以透過配置啟用它。目前EHCache和Infinispan提供Hibernate第二層快取的實現,我們可以使用它們。我們將在下一個Hibernate快取教程中深入探討此內容。
- 查詢快取: Hibernate還可以快取查詢的結果集。Hibernate查詢快取不會快取快取中實體的狀態,它只快取標識符值和值類型的結果。因此,它應始終與第二層快取一起使用。
Hibernate快取 – 第一層快取範例
對於我的hibernate一級緩存示例程序,我使用與HibernateCacheExample.java
package com.journaldev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.journaldev.hibernate.model.Employee;
import com.journaldev.hibernate.util.HibernateUtil;
public class HibernateCacheExample {
public static void main(String[] args) throws InterruptedException {
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.getCurrentSession();
Transaction tx = session.beginTransaction();
//獲取id = 1的員工
Employee emp = (Employee) session.load(Employee.class, new Long(1));
printData(emp,1);
//等待一段時間以更改後端數據
Thread.sleep(10000);
//再次提取相同數據,檢查沒有查詢被觸發的日誌
Employee emp1 = (Employee) session.load(Employee.class, new Long(1));
printData(emp1,2);
//創建新會話
Session newSession = sessionFactory.openSession();
//獲取id = 1的員工, notice the logs for query
Employee emp2 = (Employee) newSession.load(Employee.class, new Long(1));
printData(emp2,3);
//開始:除去Hibernate一級緩存中的特定對象的清除示例
//獲取id = 2的員工,第一次因此日誌中有查詢
Employee emp3 = (Employee) session.load(Employee.class, new Long(2));
printData(emp3,4);
//清除id = 1的員工對象
session.evict(emp);
System.out.println("Session Contains Employee with id=1?"+session.contains(emp));
//由於將對象從一級緩存中刪除,您將在日誌中看到查詢
Employee emp4 = (Employee) session.load(Employee.class, new Long(1));
printData(emp4,5);
//此對象仍然存在,因此您不會在日誌中看到查詢
Employee emp5 = (Employee) session.load(Employee.class, new Long(2));
printData(emp5,6);
//結束:清除示例
//開始:清除示例以從一級緩存中刪除所有內容
session.clear();
Employee emp6 = (Employee) session.load(Employee.class, new Long(1));
printData(emp6,7);
Employee emp7 = (Employee) session.load(Employee.class, new Long(2));
printData(emp7,8);
System.out.println("Session Contains Employee with id=2?"+session.contains(emp7));
tx.commit();
sessionFactory.close();
}
private static void printData(Employee emp, int count) {
System.out.println(count+":: Name="+emp.getName()+", Zipcode="+emp.getAddress().getZipcode());
}
}
當我們運行上面的示例時,輸出包含大量與Hibernate相關的信息。但是,我們主要感興趣的是我們代碼的具體輸出以及Hibernate為加載數據而觸發的查詢。輸出片段如下。
Hibernate Configuration loaded
Hibernate serviceRegistry created
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
1:: Name=Pankaj, Zipcode=95129
2:: Name=Pankaj, Zipcode=95129
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
3:: Name=PankajK, Zipcode=95129
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
4:: Name=David, Zipcode=95051
Session Contains Employee with id=1?false
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
5:: Name=Pankaj, Zipcode=95129
6:: Name=David, Zipcode=95051
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
7:: Name=Pankaj, Zipcode=95129
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
8:: Name=David, Zipcode=95051
Session Contains Employee with id=2?true
在Hibernate中的一级缓存的重要点
从上面的程序中可以得出关于Hibernate一级缓存的重要观点:
- Hibernate一级缓存默认启用,不需要任何配置。
- Hibernate一级缓存是会话特定的,这就是为什么在同一会话中获取相同数据时不会发出查询,而在其他会话中会发出查询以加载数据。
- Hibernate一级缓存可能会存在旧值,正如你所见,我在程序中将其休眠了10秒,在此期间我更新了数据库中的值(从Pankaj到PankajK),但在同一会话中没有反映出来。但在其他会话中,我们得到了更新后的值。
- 我们可以使用会话的
evict()
方法从Hibernate一级缓存中移除单个对象。 - 我们可以使用会话的
clear()
方法清除缓存,即从缓存中删除所有对象。 - 我们可以使用会话的
contains()
方法检查对象是否存在于Hibernate缓存中,如果在缓存中找到对象,则返回true,否则返回false。 - 由于Hibernate将所有对象缓存到会话的一级缓存中,在运行大量查询或批量更新时,有必要定期清除缓存以避免内存问题。
這就是有關Hibernate緩存和一級緩存示例的全部內容,未來的文章中,我們將深入探討Hibernate二級緩存 – EHCache的實現。
Source:
https://www.digitalocean.com/community/tutorials/hibernate-caching-first-level-cache