這篇博客將學習hibernate的緩存策略,緩存是hibernate提高性能的主要方法。
在hibernate中緩存主要分為:session的緩存,sessionFactory的緩存,其中,sessionFactory緩存又分為:內置緩存和外置緩存,關於緩存有一下幾點需要說明:
1.session的緩存和sessionFactory的內置緩存,都是內置的,成為hibernate的一級緩存。
2.sessionFactory的內置緩存中包含了映射元數據的預定義sql。
3.sessionFactory的外置緩存是一個可以配置的插件。默認是不開啟的,需要我們手動配置,外置緩存的數據是數據庫數據的拷貝,外置緩存的存放地方可以是內存或者硬盤。
內置緩存是不能取消的,均由session來管理
session緩存:緩存普通的屬性
sessionFactory緩存:緩存對象類型的數據
當session關閉的時候,sessionFactory中緩存的對象將被清空。
外置緩存也叫二級緩存,是可以配置的,二級緩存緩存的是實體對象。
1.get方法測試
UserInfo userInfo = (UserInfo) session.get(UserInfo.class,10);
System.out.println(userInfo.getUserName());
System.out.println(userInfo.getUserName());
System.out.println(userInfo.getUserName());
System.out.println(userInfo.getUserName());
System.out.println(userInfo.getUserName());
這裡我們獲得id=10的userinfo對象,然後連續四次得到該對象的username屬性,sql語句打印如下:
可以發現只有hibernate只發出了一條sql查詢語句。
2.load方法測試
如果我們將上面通過get方法得到的實體類對象,改為load方法來加載,同樣的,hibernate會只發出一條查詢語句。
3.list方法測試
Session session = factory.openSession();
String hql = "from UserInfo";
List<UserInfo>lists = session.createQuery(hql).list();
UserInfo userInfo = (UserInfo) session.load(UserInfo.class,10);
System.out.println(userInfo.getUserName());
UserInfo userInfo2 = (UserInfo) session.load(UserInfo.class,10);
System.out.println(userInfo2.getUserName());
System.out.println(userInfo.getUserName());
System.out.println(userInfo.getUserName());
System.out.println(userInfo.getUserName());
此時hibernate同樣只發出一條sql語句。
可以發現,首先查詢所有數據,然後將其緩存到session中,再次查詢的時候,不需要在從數據庫當中查詢。
為什麼hibernate查詢出來的數據會緩存到session當中,我們先來看個例子:
Session session = factory.openSession();
String hql = "from UserInfo";
List<UserInfo>lists = session.createQuery(hql).list();
session.close();
session = factory.openSession();
UserInfo userInfo = (UserInfo) session.load(UserInfo.class,10);
System.out.println(userInfo.getUserName());
UserInfo userInfo2 = (UserInfo) session.load(UserInfo.class,10);
System.out.println(userInfo2.getUserName());
System.out.println(userInfo.getUserName());
System.out.println(userInfo.getUserName());
System.out.println(userInfo.getUserName());
這裡我只是在查詢完成所有數據以後,增加了一條關閉session的語句。此時當再次load方法查詢的時候,又會重新發出一條sql語句。這充分說明查詢出的數據是緩存到session當中的。
Session session = factory.openSession();
String hql = "from UserInfo";
List<UserInfo>lists = session.createQuery(hql).list();
for (UserInfo userInfo : lists) {
System.out.println(userInfo.getUserName());
}
List<UserInfo>lists2 = session.createQuery(hql).list();
for (UserInfo userInfo : lists2) {
System.out.println(userInfo.getUserName());
}
這裡我兩次通過list方法查詢數據。hibernate發出的sql如下:
可以看出。list只能給緩存中存放數據,但是再次查詢的時候還是會發出sql查詢所有的語句。
剛才說了,list是只能給緩存中存放數據,但是卻不能從緩存當中取數據。iterator是可以的,那麼iterator是怎麼做到的呢??看下面的代碼:
String hql = "from UserInfo";
Iterator<UserInfo>itertor = session.createQuery(hql).iterate();
while (itertor.hasNext()) {
System.out.println(itertor.next().getUserName());
}
Iterator<UserInfo>itertor2 = session.createQuery(hql).iterate();
while (itertor2.hasNext()) {
System.out.println(itertor2.next().getUserName());
}
這裡兩次通過iterator查詢所有的數據,此時hibernate發出的sql語句如下圖:
我們現在已經知道了,list是可以給緩存當中存放數據的,可是不可以從緩存當中取數據,list查詢只發出一條sql查詢所有的語句即可,而iterator是可以從緩存當中讀取數據的,如果使用iterator查詢所有數據,會首先查詢所有的id,然後根據id一次發出sql語句,查詢每一條記錄,這樣無形當中就降低了查詢效率,那麼我們可以根據這些特性,將list查詢和iterator查詢結合起來使用。
我們可以首先使用list查詢所有,這樣只會發出一條查詢語句,不必想iterator那樣發出n+1條sql語句,提升了查詢效率,第一次之後查詢所有數據的時候,就可以直接使用iterator來查詢,這樣可以只查詢所有的id,然後根據id從緩存當中取數據,代碼如下:
String hql = "from UserInfo";
List<UserInfo>lists = session.createQuery(hql).list();
for (UserInfo userInfo : lists) {
System.out.println(userInfo.getUserName());
}
Iterator<UserInfo>itertor = session.createQuery(hql).iterate();
while (itertor.hasNext()) {
System.out.println(itertor.next().getUserName());
}
此時hibernate發出的sql和我們預期的一樣:
在hibernate中,二級緩存默認是打開的,一般來講,hibernate的二級緩存是和應用的生命周期是一樣的,由於二級緩存的數據每一個session都可以進行訪問和更改,所以如果出現高並發的時候,有可能會出現問題。
1.添加ecache所需的jar文件:
在hibernate-release-4.3.10.Final\lib\optional\ehcache目錄下有這樣三個jar文件,需要加入到工程中:
slf4j-api-1.6.1.jar
hibernate-ehcache-4.3.10.Final.jar
ehcache-core-2.4.3.jar
2.配置二級緩存:
在hibernate.cfg.xml文件中進行配置:
<!-- 配置緩存提供類 -->
<property name="cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
<!-- 啟用查詢緩存 -->
<property name="cache.use_query_cache">true</property>
所謂查詢緩存就是讓hibernate緩存list,iterator,createQuery等方法的查詢結果集,如果沒有打開查詢緩存,hibernate將只緩存load方法獲得的單個持久化對象。
注意:在打開查詢緩存 之後,調用query.list()之前,必須顯示調用query.setCacheable(true)來標識該查詢使用緩存。
3.建立ehcache.xml,在類路徑下:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
</ehcache>
說明一下:
diskStore表示設置cache文件的存放目錄,有如下三種配置:
1.user.home 表示用戶的主目錄
2.user.home 表示用戶當前的工作目錄
3.java.io.tmpdir 默認的temp文件目錄
maxElementsInMemory :在內存中最大的對象緩存數量
eternal :設置是否為永久緩存,如果是永久緩存,則timeout將被忽略。
可選的屬性:
timeToIdleSeconds:表示設置元素過期前的空閒時間
timeToLiveSeconds:表示設置元素過期前的活動時間
diskPersistent:表示是否disk store在虛擬機啟動時持久化。默認為false
diskExpiryThreadIntervalSeconds:表示運行disk終結線程的時間,默認為120秒
memoryStoreEvictionPolicy:表示策略關於Eviction
4.配置映射文件
在需要被緩存的類對應的映射文件中配置hibernate的緩存策略
<cache usage="read-write"/><!-- 配置hibernate緩存策略 -->
這裡我配置的緩存策略是可讀寫的。該配置有如下一些值:
1.read-only :表示無需修改的數據。
2.read-write :需要更新數據
3.nonstrict-read-write 非嚴格讀寫緩存策略。只需要偶爾更新數據,並且也不需要十分嚴格的事物隔離。
5.編寫測試代碼:
Query query = session.createQuery("from UserInfo");
query.setCacheable(true)
List<UserInfo> list = query.list();
for (UserInfo userinfo : list) {
System.out.println(userinfo.getUid()+"---"+userinfo.getUname());
}
session.close();
Session session2 = sf.openSession();
UserInfo userinfo =(UserInfo)session2.get(UserInfo.class, 1); System.out.println(userinfo.getUid()+"***"+userinfo.getUname());
這裡,雖然第一次查詢完成後session就已經關閉,但是由於配置了二級緩存,所以當session2再次查詢的時候,是不需要再發出sql語句從數據庫當中查詢的。
關於hibernate的緩存的學習就到這裡了。