hibernate缓存机制详细介绍
hibernate的緩存機制,包括一級緩存(session級別)、二級緩存(sessionFactory級別)。
一:hibernate的 N+1問題
list()獲得對象:
如果通過list()方法來獲得對象,毫無疑問,hibernate會發出一條sql語句,將所有的對象查詢出來,這點相信大家都能理解
Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.rid as rid2_, student0_.sex as sex2_ from t_student student0_ limit ?
?
那么,我們再來看看iterator()這種情況
iterator()獲得對象
在執行完上述的測試用例后,我們來看看控制臺的輸出,看會發出多少條 sql 語句:
?
我們看到,當如果通過iterator()方法來獲得我們對象的時候,hibernate首先會發出1條sql去查詢出所有對象的 id 值,當我們如果需要查詢到某個對象的具體信息的時候,hibernate此時會根據查詢出來的 id 值再發sql語句去從數據庫中查詢對象的信息,這就是典型的?N+1?的問題。
那么這種 N+1 問題我們如何解決呢,其實我們只需要使用 list() 方法來獲得對象即可。但是既然可以通過 list() 我們就不會出現 N+1的問題,那么我們為什么還要保留 iterator()這種形式呢?我們考慮這樣一種情況,如果我們需要在一個session當中要兩次查詢出很多對象,此時我們如果寫兩條 list()時,hibernate此時會發出兩條 sql 語句,而且這兩條語句是一樣的,但是我們如果第一條語句使用 list(),而第二條語句使用 iterator()的話,此時我們也會發兩條sql語句,但是第二條語句只會將查詢出對象的id,所以相對應取出所有的對象而已,顯然這樣可以節省內 存,而如果再要獲取對象的時候,因為第一條語句已經將對象都查詢出來了,此時會將對象保存到session的一級緩存中去,所以再次查詢時,就會首先去緩 存中查找,如果找到,則不發sql語句了。這里就牽涉到了接下來這個概念:hibernate的一級緩存。
二、一級緩存(session級別)
我們來看看hibernate提供的一級緩存:
我們來看看控制臺輸出:
?
Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.rid as rid2_,student0_.sex as sex2_ from t_student student0_ limit ?
我們看到此時hibernate僅僅只會發出一條 sql 語句,因為第一行代碼就會將整個的對象查詢出來,放到session的一級緩存中去,當我如果需要再次查詢學生對象時,此時首先會去緩存中看是否存在該對 象,如果存在,則直接從緩存中取出,就不會再發sql了,但是要注意一點:hibernate的一級緩存是session級別的,所以如果session關閉后,緩存就沒了,此時就會再次發sql去查數據庫。
?
Hibernate: select student0_.id as id2_2_, student0_.name as name2_2_, student0_.sex as sex2_2_, student0_.rid as rid2_2_, classroom1_.id as id1_0_, classroom1_.name as name1_0_, classroom1_.sid as sid1_0_, special2_.id as id0_1_, special2_.name as name0_1_, special2_.type as type0_1_ from t_student student0_ left outer join t_classroom classroom1_ on student0_.rid=classroom1_.id left outer join t_special special2_ on classroom1_.sid=special2_.id where student0_.id=?
我們看到此時會發出兩條sql語句,因為session關閉以后,一級緩存就不存在了,所以如果再查詢的時候,就會再發sql。要解決這種問題,我們應該怎么做呢?這就要我們來配置hibernate的二級緩存了,也就是sessionFactory級別的緩存。
三、二級緩存(sessionFactory級別)
?
使用hibernate二級緩存,我們首先需要對其進行配置,配置步驟如下:
1.hibernate并沒有提供相應的二級緩存的組件,所以需要加入額外的二級緩存包,常用的二級緩存包是EHcache。這個我們在下載好的 hibernate的lib->optional->ehcache下可以找到(我這里使用的hibernate4.1.7版本),然后將里 面的幾個jar包導入即可。
2.在hibernate.cfg.xml配置文件中配置我們二級緩存的一些屬性:
我這里使用的是hibernate4.1.7版本,如果是使用hibernate3的版本的話,那么二級緩存的提供類則要配置成這個:
3.配置hibernate的二級緩存是通過使用 ehcache的緩存包,所以我們需要創建一個 ehcache.xml 的配置文件,來配置我們的緩存信息,將其放到項目根目錄下
?
這樣我們的二級緩存配置就算完成了,接下來我們來通過測試用例測試下我們的二級緩存是否起作用
①二級緩存是sessionFactory級別的緩存
TestCase1:
?
因為二級緩存是sessionFactory級別的緩存,我們看到,在配置了二級緩存以后,當我們session關閉以后,我們再去查詢對象的時候,此時hibernate首先會去二級緩存中查詢是否有該對象,有就不會再發sql了。
②二級緩存緩存的僅僅是對象,如果查詢出來的是對象的一些屬性,則不會被加到緩存中去
TestCase2:
?
我們看到這個測試用例,如果我們只是取出對象的一些屬性的話,則不會將其保存到二級緩存中去,因為二級緩存緩存的僅僅是對象。
③通過二級緩存來解決 N+1 的問題
TestCase3:
?
?
當我們如果需要查詢出兩次對象的時候,可以使用二級緩存來解決N+1的問題。
④二級緩存會緩存 hql 語句嗎?
TestCase4:
?
?
Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.sex as sex2_, student0_.rid as rid2_ from t_student student0_ limit ? Hibernate: select student0_.id as id2_, student0_.name as name2_,student0_.sex as sex2_, student0_.rid as rid2_ from t_student student0_ limit ? 我們看到,當我們如果通過 list() 去查詢兩次對象時,二級緩存雖然會緩存查詢出來的對象,但是我們看到發出了兩條相同的查詢語句,這是因為二級緩存不會緩存我們的hql查詢語句,要想解決這個問題,我們就要配置我們的查詢緩存了。
四、查詢緩存(sessionFactory級別)
我們如果要配置查詢緩存,只需要在hibernate.cfg.xml中加入一條配置即可:
?
然后我們如果在查詢hql語句時要使用查詢緩存,就需要在查詢語句后面設置這樣一個方法:
?
如果是在annotation中,我們還需要在這個類上加上這樣一個注解:@Cacheable
接下來我們來通過測試用例來看看我們的查詢緩存
①查詢緩存也是sessionFactory級別的緩存
TestCase1:
?
?
Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.sex as sex2_, student0_.rid as rid2_ from t_student student0_ limit ? 我們看到,此時如果我們發出兩條相同的語句,hibernate也只會發出一條sql,因為已經開啟了查詢緩存了,并且查詢緩存也是sessionFactory級別的 ②只有當 HQL 查詢語句完全相同時,連參數設置都要相同,此時查詢緩存才有效 TestCase2:
?
Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.sex as sex2_, student0_.rid as rid2_ from t_student student0_ where student0_.name like ? limit ? Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.sex as sex2_, student0_.rid as rid2_ from t_student student0_ where student0_.name like ? limit ?我們看到,如果我們的hql查詢語句不同的話,我們的查詢緩存也沒有作用 ③查詢緩存也能引起 N+1 的問題 查詢緩存也能引起 N+1 的問題,我們這里首先先將 Student 對象上的二級緩存先注釋掉:
?
TestCase4:
?
?
Hibernate: select student0_.id as id2_, student0_.name as name2_, student0_.sex as sex2_, student0_.rid as rid2_ from t_student student0_ where student0_.name like ? limit ?Hibernate: select student0_.id as id2_2_, student0_.name as name2_2_, student0_.sex as sex2_2_, student0_.rid as rid2_2_, classroom1_.id as id1_0_, classroom1_.name as name1_0_, classroom1_.sid as sid1_0_, special2_.id as id0_1_, special2_.name as name0_1_, special2_.type as type0_1_ from t_student student0_ left outer join t_classroom classroom1_ on student0_.rid=classroom1_.id left outer join t_special special2_ on classroom1_.sid=special2_.id where student0_.id=?Hibernate: select student0_.id as id2_2_, student0_.name as name2_2_, student0_.sex as sex2_2_, student0_.rid as rid2_2_, classroom1_.id as id1_0_, classroom1_.name as name1_0_, classroom1_.sid as sid1_0_, special2_.id as id0_1_, special2_.name as name0_1_, special2_.type as type0_1_ from t_student student0_ left outer join t_classroom classroom1_ on student0_.rid=classroom1_.id left outer join t_special special2_ on classroom1_.sid=special2_.id where student0_.id=?Hibernate: select student0_.id as id2_2_, student0_.name as name2_2_, student0_.sex as sex2_2_, student0_.rid as rid2_2_, classroom1_.id as id1_0_, classroom1_.name as name1_0_, classroom1_.sid as sid1_0_, special2_.id as id0_1_, special2_.name as name0_1_, special2_.type as type0_1_ from t_student student0_ left outer join t_classroom classroom1_ on student0_.rid=classroom1_.id left outer join t_special special2_ on classroom1_.sid=special2_.id where student0_.id=?Hibernate: select student0_.id as id2_2_, student0_.name as name2_2_, student0_.sex as sex2_2_, student0_.rid as rid2_2_, classroom1_.id as id1_0_, classroom1_.name as name1_0_, classroom1_.sid as sid1_0_, special2_.id as id0_1_, special2_.name as name0_1_, special2_.type as type0_1_ from t_student student0_ left outer join t_classroom classroom1_ on student0_.rid=classroom1_.id left outer join t_special special2_ on classroom1_.sid=special2_.id where student0_.id=?
我們看到,當我們將二級緩存注釋掉以后,在使用查詢緩存時,也會出現 N+1 的問題,為什么呢?
因為查詢緩存緩存的也僅僅是對象的id,所以第一條 sql 也是將對象的id都查詢出來,但是當我們后面如果要得到每個對象的信息的時候,此時又會發sql語句去查詢,所以,如果要使用查詢緩存,我們一定也要開啟我們的二級緩存,這樣就不會出現 N+1 問題了
?
轉載于:https://www.cnblogs.com/ipetergo/p/6803290.html
總結
以上是生活随笔為你收集整理的hibernate缓存机制详细介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java - 对象的创建
- 下一篇: 数据挖掘介绍以及模型参数详解