一、緩存的范圍分為3類:
1、事務范圍(單Session即一級緩存)
事務范圍的緩存只能被當前事務訪問,每個事務都有各自的緩存,緩存內的數據通常采用相互關聯的對象形式.緩存的生命周期依賴於事務的生命周期,只有當事務結束時,緩存的生命周期才會結束.事務范圍的緩存使用內存作為存儲介質,一級緩存就屬於事務范圍.
2、應用范圍(單SessionFactory即二級緩存)
應用程序的緩存可以被應用范圍內的所有事務共享訪問.緩存的生命周期依賴於應用的生命周期,只有當應用結束時,緩存的生命周期才會結束.應用范圍的緩存可以使用內存或硬盤作為存儲介質,二級緩存就屬於應用范圍.
3、集群范圍(多SessionFactory)
在集群環境中,緩存被一個機器或多個機器的進程共享,緩存中的數據被復制到集群環境中的每個進程節點,進程間通過遠程通信來保證緩存中的數據的一致,緩存中的數據通常采用對象的松散數據形式.
二、一級緩存:
1、使用一級緩存的目的是為了減少對數據庫的訪問次數,從而提升hibernate的執行效率;(當執行一次查詢操作的時候,執行第二次查詢操作,先檢查緩存中是否有數據,如果有數據就不查詢數據庫,直接從緩存中獲取數據);
2、Hibernate中的一級緩存,也叫做session的緩存,它可以在session范圍內減少數據庫的訪問次數,只在session范圍內有效,session關閉,一級緩存失敗;
3、一級緩存的特點,只在session范圍有效,作用時間短,效果不是特別明顯,在短時間內多次操作數據庫,效果比較明顯。
4、當調用session的save/saveOrUpdate/get/load/list/iterator方法的時候,都會把對象放入session緩存中;
5、session的緩存是由hibernate維護的,用戶不能操作緩存內容;如果想操作緩存內容,必須通過hibernate提供的evict/clear方法操作
6、緩存相關的方法(在什么情況下使用上面方法呢?批量操作情況下使用,如Session.flush();先與數據庫同步,Session.clear();再清空一級緩存內容):
session.flush();讓一級緩存與數據庫同步;
session.evict();清空一級緩存中指定的對象;
session.clear();清空一級緩存中所有的對象;
二、二級緩存 : 基於應用程序級別的緩存,作用在SessionFactory范圍內的, 可以跨多個session,即不同的session都可以訪問緩存。 Hibernate提供的二級緩存有默認的實現,且是一種可插配的緩存框架!如果用戶想用二級緩存,只需要在hibernate.cfg.xml中配置即可; 不想用,直接移除,不影響代碼。
1、在執行各種條件查詢時,如果所獲得的結果集為實體對象的集合,那么就會把所有的數據對象根據ID放入到二級緩存中。
2、當Hibernate根據ID訪問數據對象的時候,首先會從Session一級緩存中查找,如果查不到並且配置了二級緩存,那么會從二級緩存中查找,如果還查不到,就會查詢數據庫,把結果按照ID放入到緩存中。
3、刪除、更新、增加數據的時候,同時更新緩存。
注意:
在通常情況下會將具有以下特征的數據放入到二級緩存中:
● 很少被修改的數據。
● 不是很重要的數據,允許出現偶爾並發的數據。
● 不會被並發訪問的數據。
● 常量數據。
● 不會被第三方修改的數據
而對於具有以下特征的數據則不適合放在二級緩存中:
● 經常被修改的數據。
● 財務數據,絕對不允許出現並發。
● 與其他應用共享的數據。
三、二級緩存配置步驟: 在默認情況下,Hibernate會使用EHCache作為二級緩存組件。但是,可以通過設置hibernate.cache.provider_class屬性,指定其他的緩存策略,該緩存策略必須實現org.hibernate.cache.CacheProvider接口。 通過實現org.hibernate.cache.CacheProvider接口可以提供對不同二級緩存組件的支持,此接口充當緩存插件與Hibernate之間的適配器。
|
組件
|
Provider類
|
類型
|
集群
|
查詢緩存
|
|
Hashtable
|
org.hibernate.cache.HashtableCacheProvider
|
內存
|
不支持
|
支持
|
|
EHCache
|
org.hibernate.cache.EhCacheProvider
|
內存,硬盤
|
不支持
|
支持
|
|
OSCache
|
org.hibernate.cache.OSCacheProvider
|
內存,硬盤
|
支持
|
支持
|
|
SwarmCache
|
org.hibernate.cache.SwarmCacheProvider
|
集群
|
支持
|
不支持
|
|
JBoss TreeCache
|
org.hibernate.cache.TreeCacheProvider
|
集群
|
支持
|
支持
|
1.添加jar包,如ehcache-core.jar
2.在hibernate.cfg.xml開啟二級緩存Xml代碼
<!-- a. 開啟二級緩存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<!-- b. 指定使用哪一個緩存框架(默認提供的) -->
<property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
<!-- 開啟查詢緩存 -->
<property name="hibernate.cache.use_query_cache">true</property>
<!-- c. 指定哪一些類,需要加入二級緩存 -->
<class-cache usage="read-write" class="com.bie.lesson11.Dept"/>
<class-cache usage="read-only" class="com.bie.lesson11.Employee"/>
<!-- 集合緩存[集合緩存的元素對象,也加加入二級緩存] -->
<collection-cache usage="read-write" collection="com.bie.lesson11.Dept.emps"/>
3.ehcache.xml配置(可選)
cache參數詳解:
● name:指定區域名
● maxElementsInMemory :緩存在內存中的最大數目
● maxElementsOnDisk:緩存在磁盤上的最大數目
● eternal :設置是否永遠不過期
● overflowToDisk : 硬盤溢出數目
● timeToIdleSeconds :對象處於空閑狀態的最多秒數后銷毀
● timeToLiveSeconds :對象處於緩存狀態的最多秒數后銷毀
● memoryStoreEvictionPolicy:緩存算法,有LRU(默認)、LFU、LFU
關於緩存算法,常見有三種:
● LRU:(Least Rencently Used)新來的對象替換掉使用時間算最近很少使用的對象
● LFU:(Least Frequently Used)替換掉按命中率高低算比較低的對象
● LFU:(First In First Out)把最早進入二級緩存的對象替換掉
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<!--如果緩存中的對象存儲超過指定的緩存數量的對象存儲的磁盤地址-->
<diskStore path="D:/ehcache"/>
<!-- 默認cache:如果沒有對應的特定區域的緩存,就使用默認緩存 -->
<defaultCache maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="false"/>
<!-- 指定區域cache:通過name指定,name對應到Hibernate中的區域名即可-->
<cache name="cn.javass.h3test.model.UserModel"
eternal="false"
maxElementsInMemory="100"
timeToIdleSeconds="1200"
timeToLiveSeconds="1200"
overflowToDisk="false">
</cache>
</ehcache>
4.實體類的映射文件配置,如user.hbm.xml的配置
<?xml version="1.0" encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
<hibernate-mapping>
<class>
<!-- 設置該持久化類的二級緩存並發訪問策略 read-only read-write nonstrict-read-write transactional-->
<class name="cn.java.test.model.User" table="TBL_USER">
<cache usage="read-write"/>
......
</class>
</hibernate-mapping>
(1)其中二級緩存並發訪問策略有三種:
a.在user.hbm.xml中添加<cache usage="read-write"/>
b.在hibernate.cfg.xml中添加<class-cache usage="read-write" class="com.hb.test.User"/>
c.用Hibernate注解配置緩存實體類
(2) 二級緩存策略
● READ_ONLY:實體只讀緩存
只讀緩存不允許更新,將報錯Can't write to a readonly object。
允許新增,(從2.0以后新增直接添加到二級緩存)
//確保數據庫中有標識符為1的FarmModel
FarmModel farm = (FarmModel) session.get(FarmModel.class, 1);
//如果修改將報錯,只讀緩存不允許修改
//farm.setName("aaa");
● NONSTRICT_READ_WRITE:實體非嚴格讀/寫緩存
允許更新,更新后緩存失效,需再查詢一次。
允許新增,新增記錄自動加到二級緩存中。
整個過程不加鎖。
● READ_WRITE:實體讀/寫緩存
允許更新,更新后自動同步到緩存。
允許新增,新增記錄后自動同步到緩存。
保證read committed隔離級別及可重復讀隔離級別(通過時間戳實現)
整個過程加鎖,如果當前事務的時間戳早於二級緩存中的條目的時間戳,說明該條目已經被別的
事務修改了,此時重新查詢一次數據庫,否則才使用緩存數據,因此保證可重復讀隔離級別。
讀寫緩存和不嚴格讀寫緩存在實現上的區別在於,讀寫緩存更新緩存的時候會把緩存里面的數據換成一個鎖
● TRANSACTIONAL:實體事務緩存
緩存支持事務,發生異常的時候,緩存也能夠回滾,只支持jta環境
● Collection集合緩存
<hibernate-mapping>
<class name="cn.java.test.model.UserModel" table="TBL_USER">
<cache usage="read-write" />
<set name="farms" cascade="all" inverse="true" lazy="false">
<cache usage="read-write"/>
<key column="fk_user_id"/>
<one-to-many class="cn.java.test.model.FarmModel"/>
</set>
</class>
</hibernate-mapping>
