爲什麼要用Hibernate緩存?
Hibernate是一個持久層框架,經常訪問物理數據庫。使用緩存機制,是爲了降低應用程序對物理數據源訪問的頻次,從而提高應用程序的運行性能。
緩存內的數據是對物理數據源中的數據的複製,應用程序在運行時從緩存讀寫數據,在特定的時刻或事件會同步緩存和物理數據源的數據。
Hibernate緩存包括一級緩存和二級緩存。
Hibernate中對象三種狀態
瞬時態:transient,session沒有緩存對象,數據庫也沒有對應記錄。
OID特點:沒有值
持久態:persistent,session緩存對象,數據庫最終會有記錄。(事務沒有提交)
OID特點:有值
脫管態:detached,session沒有緩存對象,數據庫有記錄。
OID特點:有值
對象狀態轉換圖
Hibernate一級緩存
Hibernate一級緩存又稱爲“Session的緩存”,是默認開啓的。Session內置不能被卸載,Session的緩存是事務範圍的緩存(Session對象的生命週期通常對應一個數據庫事務或者一個應用事務)。一級緩存中,持久化類的每個實例都具有唯一的OI
證明一級緩存。
@Test
public void demo02(){
//證明一級緩存
Session session = factory.openSession();
session.beginTransaction();
//1 查詢 id = 1
User user = (User) session.get(User.class, 1);
System.out.println(user);
//2 再查詢 -- 不執行select語句,將從一級緩存獲得
User user2 = (User) session.get(User.class, 1);
System.out.println(user2);
session.getTransaction().commit();
session.close();
}
可以調用方法清除一級緩存(可以是對特定對象的)。
//清除
//session.clear();
session.evict(user);
一級緩存的管理
evit(Object obj) 將指定的持久化對象從一級緩存中清除,釋放對象所佔用的內存資源,指定對象從持久化狀態變爲脫管狀態,從而成爲遊離對象。
clear() 將一級緩存中的所有持久化對象清除,釋放其佔用的內存資源。
contains(Object obj) 判斷指定的對象是否存在於一級緩存中。
flush() 刷新一級緩存區的內容,使之與數據庫數據保持同步。
一級緩存的快照原理
* 使用id進行查詢數據庫,將查詢得到的結果放置到session一級緩存中,同時複製一份數據,放置到session的快照中
* 當使用tr.commit()的時候,同時清理session的一級緩存(flush)
* 當清理session一級緩存的時候,會使用OID判斷一級緩存中對象和快照中的對象進行比對
* 如果2個對象(一級緩存的對象和快照的對象)中的屬性發生變化,則執行update語句,此時更新數據庫,更新成一級緩存中的數據
* 如果2個對象中的屬性不發生變化,此時不執行update語句
作用:引入快照可以保證數據一致性,避免不要的update,減少數據庫的訪問壓力。
Hibernate二級緩存
Hibernate二級緩存又稱爲“SessionFactory的緩存”。緩存被應用範圍內的所有session共享,不同的session可以共享。這些session有可能是併發訪問緩存,因此必須對緩存進行更新。緩存的生命週期依賴於應用的生命週期,應用結束時,緩存也就結束了生命週期,二級緩存存在於應用程序範圍。
二級緩存是可選的,是一個可配置的插件,默認下SessionFactory不會啓用這個插件。
有如下代碼
@Test
public void testCache2() {
Session session1 = sf.openSession();//獲得Session1
session1.beginTransaction();
Category c = (Category)session1.load(Category.class, 1);
System.out.println(c.getName());
session1.getTransaction().commit();
session1.close();
Session session2 = sf.openSession();//獲得Session2
session2.beginTransaction();
Category c2 = (Category)session2.load(Category.class, 1);
System.out.println(c2.getName());
session2.getTransaction().commit();
session2.close();
}
不開啓二級緩存的情況下,因爲一個session不能取其他session的緩存,在上面的代碼中,我們重啓一個Session,第二次調用load或者get方法檢索同一個對象的時候會重新查找數據庫,會發select語句信息。如果要共享session的緩存,那麼可以考慮開啓二級緩存。
適合存放在二級緩存的數據
(1)經常被訪問
(2)改動不大
(3)數量有限
(4)不是很重要的數據,允許出現偶爾併發的數據。
在Hibernate中使用EhCache來支持二級緩存
<!-- EHCache的配置,hibernate.cfg.xml -->
<hibernate-configuration>
<session-factory>
<!-- 設置二級緩存插件EHCache的Provider類-->
<property name="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
<!-- 啓動"查詢緩存" -->
<property name="hibernate.cache.use_query_cache">
true
</property>
</session-factory>
</hibernate-configuration>
<!-- ehcache.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<!--
緩存到硬盤的路徑
-->
<diskStore path="d:/ehcache"></diskStore>
<!--
默認設置
maxElementsInMemory : 在內存中最大緩存的對象數量。
eternal : 緩存的對象是否永遠不變。
timeToIdleSeconds :可以操作對象的時間。
timeToLiveSeconds :緩存中對象的生命週期,時間到後查詢數據會從數據庫中讀取。
overflowToDisk :內存滿了,是否要緩存到硬盤。
-->
<defaultCache maxElementsInMemory="200" eternal="false"
timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></defaultCache>
<!--
指定緩存的對象。
下面出現的的屬性覆蓋上面出現的,沒出現的繼承上面的。
-->
<cache name="com.suxiaolei.hibernate.pojos.Order" maxElementsInMemory="200" eternal="false"
timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></cache>
</ehcache>
二級緩存實現原理
我們可以把緩存看做是一個Map對象,它的Key用於存儲對象OID,Value用於存儲POJO。首先,當我們使用Hibernate從數據庫中查詢出數據,獲取檢索的數據後,Hibernate將檢索出來的對象的OID放入緩存中key 中,然後將具體的POJO放入value中,等待下一次再次向數據查詢數據時,Hibernate根據你提供的OID先檢索一級緩存,若有且配置了二級緩存,則檢索二級緩存,如果還沒有則才向數據庫發送SQL語句,然後將查詢出來的對象放入緩存中。
相關博文:
http://blog.csdn.net/tanga842428/article/details/52698657
https://www.jianshu.com/p/50964e92c5fb