查詢緩存的使用,主要是為了提高查詢訪問速度。將用戶對同一數(shù)據(jù)的重復(fù)查詢過程簡化,不再每次均從數(shù)據(jù)庫查詢獲取結(jié)果數(shù)據(jù),從而提高訪問速度。
mybatis的查詢緩存機制,根據(jù)緩存區(qū)的作用域(生命周期)可劃分為兩種:一級緩存與二級緩存
一、一級查詢緩存
mybatis一級緩存是基于org.apache.ibatis.cache.impl.perpetualcache類的hashmap本地緩存,其作用域是sqlsession。在同一個sqlsession中兩次執(zhí)行相同的sql語句,第一次執(zhí)行完畢后,會將查詢結(jié)果寫入到緩存中,第二次會從緩存中直接獲取數(shù)據(jù),而不再到數(shù)據(jù)庫中進(jìn)行查詢,從而提高查詢效率。
當(dāng)一個sqlsession結(jié)束后,該sqlsession中的一級緩存也就不存在了。mybatis默認(rèn)一級緩存是開啟狀態(tài),且不能關(guān)閉。
1.一級緩存的存在性證明
測試類:
1
2
3
4
5
6
7
8
9
10
|
//證明一級緩存的存在 @test public void test01(){ //第一次查詢 student student = dao.selectstudentbyid( 2 ); system.out.println(student); //第二次查詢 student student2 = dao.selectstudentbyid( 2 ); system.out.println(student2); } |
mapper:
1
2
3
4
5
|
<mapper namespace= "com.hcx.dao.istudentdao" > <select id=selectstudentbyid resulttype= "com.hcx.beans.student" > select * from student where id=#{id} </select> </mapper> |
控制臺:
執(zhí)行完后,發(fā)現(xiàn)只執(zhí)行了一次從db中的查詢,第二次的結(jié)果是直接輸出的。說明,第二次是從sqlsession緩存中讀取的。
2.從緩存讀取數(shù)據(jù)的依據(jù)是sql的id
一級緩存緩存的是相同sql映射id的查詢結(jié)果,而非相同sql語句的查詢結(jié)果。因為mybatis內(nèi)部對于查詢緩存,無論是一級查詢還是二級查詢,其底層均使用一個hashmap實現(xiàn):key為sql的id相關(guān)內(nèi)容,value為從數(shù)據(jù)庫中查詢出的結(jié)果。
mapper:
1
2
3
4
5
6
7
8
|
<mapper namespace= "com.hcx.dao.istudentdao" > <select id=selectstudentbyid resulttype= "com.hcx.beans.student" > select * from student where id=#{id} </select> <select id= "selectstudnetbyid2" resulttype= "com.hcx.beans.student" > select id,name,age,score,birthday from student where id=#{id} </select> </mapper> |
dao接口:
1
2
3
4
|
public interface istudentdao { student selectstudentbyid( int id); student selectstudentbyid2( int id); } |
測試類:
1
2
3
4
5
6
7
8
9
10
11
|
//證明從一級緩存中讀取數(shù)據(jù)的依據(jù): //mybatis:sql的id+sql語句 //hibernate:查詢結(jié)果對象的id @test public void test02(){ student student = dao.selectstudentbyid( 2 ); system.out.println(student); student student2 = dao.selectstudentbyid2( 2 ); system.out.println(student2); } |
控制臺:
查看控制臺,發(fā)現(xiàn)第二次查詢結(jié)果與第一次的完全相同,但第二次查詢并沒有從緩存中讀取數(shù)據(jù),而是直接從db中進(jìn)行的查詢。這是因為從緩存讀取數(shù)據(jù)的依據(jù)是查詢sql的映射id,而非查詢結(jié)果。
3.增刪改對一級查詢緩存的影響
增刪改操作,無論是否進(jìn)行提交sqlsession.commit(),均會清空一級查詢緩存,使查詢再次從db中select。
測試類:
1
2
3
4
5
6
7
8
9
|
@test public void test03(){ student student = dao.selectstudentbyid( 2 ); system.out.println(student); //增刪改操作都會清空一級緩存,無論是否提交 dao.insertstudent( new student( "趙六" , 26 , 96.6 )); student student2 = dao.selectstudentbyid( 2 ); system.out.println(student2); } |
控制臺:
二、內(nèi)置二級查詢緩存
mybatis查詢緩存的作用域是根據(jù)映射文件mapper的namespace劃分的,相同namespace的mapper查詢數(shù)據(jù)存放在同一個緩存區(qū)域。不同namespace下的數(shù)據(jù)互不干擾。
無論是一級緩存還是二級緩存,都是按照namespace進(jìn)行分別存放的。但一、二級緩存的不同之處在于,sqlsession一旦關(guān)閉,則sqlsession中的數(shù)據(jù)將不存在,即一級緩存就不復(fù)存在。而二級緩存的生命周期會與整個應(yīng)用同步,與sqlsession是否關(guān)閉無關(guān)。
使用二級緩存的目的,不是共享數(shù)據(jù),因為mybatis從緩存中讀取數(shù)據(jù)的依據(jù)是sql的id,而非查詢出的對象。所以,二級緩存中的數(shù)據(jù)不是為了在多個查詢之間共享(所有查詢中只要查詢結(jié)果中存在該對象的,就直接從緩存中讀取,這是對數(shù)據(jù)的共享,hibernate中的緩存就是為了共享,但mybatis不是),而是為了延長該查詢結(jié)果的保存時間,提高系統(tǒng)性能。
1.二級緩存用法
二級緩存的使用只需要完成兩步:
序列化實體
在mapper映射文件中添加<cache/>標(biāo)簽
1.實體序列化
要求查詢結(jié)果所涉及到的實體類要實現(xiàn)java.io.serializable接口。若該實體類存在父類,或其具有域?qū)傩裕瑒t父類與域?qū)傩灶愐惨獙崿F(xiàn)序列化接口。
1
2
3
4
5
6
|
public class student implements serializable{ private integer id; private string name; private int age; private double score; } |
2.mapper映射文件中添加<cache/>標(biāo)簽
在mapper映射文件中的<mapper/>標(biāo)簽中添加<cache/>子標(biāo)簽
1
2
3
4
5
6
|
<mapper namespace= "com.hcx.dao.istudentdao" > <cache/> <select id=selectstudentbyid resulttype= "com.hcx.beans.student" > select * from student where id=#{id} </select> </mapper> |
3.二級緩存的配置
為<cache/>標(biāo)簽添加一些相關(guān)屬性設(shè)置,可以對二級緩存的運行性能進(jìn)行控制。若不指定設(shè)置,則均保持默認(rèn)值。
1
2
|
<cache eviction= "ifio" flushinterval= "10800000" readonly= "true" size= "512" /> |
eviction:逐出策略。當(dāng)二級緩存中的對象達(dá)到最大值時,就需要通過逐出策略將緩存中的對象移出緩存。默認(rèn)為lru。常用的策略有fifo和lru
flushinterval:刷新緩存的時間間隔,單位毫秒。這里的刷新緩存即清空緩存。一般不指定,即當(dāng)執(zhí)行增刪改時刷新緩存。
readonly:設(shè)置緩存中數(shù)據(jù)是否只讀。只讀的緩存會給所有調(diào)用者返回緩存對象的相同實例,因此這些對象不能被修改,這提供了很重要的性能優(yōu)勢。但讀寫的緩存會返回緩存對象的拷貝。這會慢一些,但是安全,因此默認(rèn)是false。
size:二級緩存中可以存放的最多對象個數(shù)。默認(rèn)為1024個。
2.二級緩存的存在性證明
對于映射文件中的同一個查詢,肯定是同一個namespace中的查詢。在一次查詢后,將sqlsession關(guān)閉,再進(jìn)行一次相同查詢,發(fā)現(xiàn)并沒有到db中進(jìn)行select查詢,說明二級緩存是存在的。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//證明二級緩存的存在 @test public void test01(){ //第一次查詢 student student = dao.selectstudentbyid( 2 ); system.out.println(student); sqlsession.close(); sqlsession = mybatisutils.getsqlsession(); dao = sqlsession.getmapper(istudentdao. class ); //第二次查詢 student student2 = dao.selectstudentbyid( 2 ); system.out.println(student2); } |
查看控制臺:
cache hit ratio表示緩存命中率。開啟二級緩存后,每執(zhí)行一次查詢,系統(tǒng)都會計算一次二級緩存的命中率。第一次查詢也是先從緩存中查詢,只不過緩存中一定是沒有的。所以會再從db中查詢。由于二級緩存中不存在該數(shù)據(jù),所以命中率為0.但第二次查詢是從二級緩存中讀取的,所以這一次的命中率為1/2=0.5。當(dāng)然,若有第三次查詢,則命中率為1/3=0.66
3.增刪改對二級緩存的影響
增刪改操作,無論是否進(jìn)行提交sqlsession.commit(),均會清空一級、二級緩存,使查詢再次從db中select。
測試類:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@testpublic void test02(){ //第一次查詢 student student = dao.selectstudentbyid( 2 ); system.out.println(student); sqlsession.close(); sqlsession = mybatisutils.getsqlsession(); dao = sqlsession.getmapper(istudentdao. class ); //插入 dao.insertstudent( new student( "" , 0 , 0 )); //第二次查詢 student student2 = dao.selectstudentbyid( 2 ); system.out.println(student2); } |
控制臺:
注意,在第二次查詢時的緩存命中率為0.5,但還是從db中查詢了。說明在緩存中與該查詢相對應(yīng)的key是存在的,但其value被清空。而value被清空的原因是前面執(zhí)行了對db的增刪改操作,所以不會從緩存中直接將null值返回,而是從db中進(jìn)行查詢。
說明:
二級緩存的清空,實質(zhì)上是對所查找key對應(yīng)的value置為null,而非將<key,value>對,即entry對象刪除。
從db中進(jìn)行select查詢的條件是:緩存中根本不存在這個key或者緩存中存在該key所對應(yīng)的entry對象,但value為null。
設(shè)置增刪改操作不刷新二級緩存:
若要使某個增、刪或改操作不清空二級緩存,則需要在其<insert/>或<delete/>或<update/>中添加屬性flushcache="false",默認(rèn)為true。
1
2
3
|
<insert id= "insertstudent" flushcache= "false" > insert into student(name,age,score) values(#{name},#{age},#{score}) </insert> |
4.二級緩存的關(guān)閉
二級緩存默認(rèn)為開啟狀態(tài)。若要將其關(guān)閉,則需要進(jìn)行相關(guān)設(shè)置。
根據(jù)關(guān)閉的范圍大小,可以分為全局關(guān)閉和局部關(guān)閉
1.全局關(guān)閉(在配置文件中設(shè)置)
全局關(guān)閉是指整個應(yīng)用的二級緩存全部關(guān)閉,所有查詢均不使用二級緩存。全局開關(guān)設(shè)置在主配置文件的全局設(shè)置<settings/>中,該屬性為cacheenabled,設(shè)置為false,則關(guān)閉;設(shè)置為true,則開啟,默認(rèn)值為true。即二級緩存默認(rèn)是開啟的。
1
2
3
4
|
<!-- 關(guān)閉二級緩存 --> <settings> <setting name= "cacheenabled" value= "false" /> </settings> |
2.局部關(guān)閉(在映射文件的每個select中設(shè)置)
局部關(guān)閉指整個應(yīng)用的二級緩存是開啟的,但只是針對某個<select/>查詢,不使用二級緩存。此時可以單獨只關(guān)閉該<select/>標(biāo)簽的二級緩存。
在該要關(guān)閉二級緩存的<select/>標(biāo)簽中,將其屬性usecache設(shè)置為false,即可關(guān)閉該查詢的二級緩存。該屬性默認(rèn)為true,即每個<select/>查詢的二級緩存默認(rèn)是開啟的。
1
2
3
4
|
<!--usecache= "false" 對當(dāng)前sql的二級緩存的局部關(guān)閉 --> <select id=selectstudentbyid usecache= "false" resulttype= "com.hcx.beans.student" > select * from student where id=#{id} </select> |
5.二級緩存的使用原則
1.只能在一個命名空間下使用二級緩存
由于二級緩存中的數(shù)據(jù)是基于namespace的,即不同namespace中的數(shù)據(jù)互不干擾。在多個namespace中若均存在對同一個表的操作,那么這多個namespace中的數(shù)據(jù)可能就會出現(xiàn)不一致現(xiàn)象。
2.在單表上使用二級緩存
如果一個表與其它表有關(guān)聯(lián)關(guān)系,那么久非常有可能存在多個namespace對同一數(shù)據(jù)的操作。而不同namespace中的數(shù)據(jù)互補干擾,所以就有可能出現(xiàn)多個namespace中的數(shù)據(jù)不一致現(xiàn)象。
3.查詢多于修改時使用二級緩存
在查詢操作遠(yuǎn)遠(yuǎn)多于增刪改操作的情況下可以使用二級緩存。因為任何增刪改操作都將刷新二級緩存,對二級緩存的頻繁刷新將降低系統(tǒng)性能。
三、ehcache二級查詢緩存
mybatis允許使用第三方緩存產(chǎn)品。ehcache就是其中一種。
注意:使用ehcache二級緩存,實體類無需實現(xiàn)序列化接口。
1.導(dǎo)入jar包
2.添加ehcache.xml
解壓ehcache的核心jar包ehcache-core-2.6.8.jar,將其中的一個配置文件ehcache-failsafe.xml直接放到項目的src目錄下,并更名為ehcache.xml
(1)<diskstore/>標(biāo)簽
指定一個文件目錄,當(dāng)內(nèi)存空間不夠,需要將二級緩存中數(shù)據(jù)寫到硬盤上時,會寫到這個指定目錄中。其值一般為java.io.tmpdir,表示當(dāng)前系統(tǒng)的默認(rèn)文件臨時目錄。
當(dāng)前文件系統(tǒng)的默認(rèn)文件臨時目錄,可以通過system.property()方法查看:
1
2
3
4
5
|
@test public void test(){ string path = system.getproperty( "java.io.tmpdir" ); system.out.println(path); } |
(2)<defaultcache/>標(biāo)簽
3.啟用ehcache緩存機制
在映射文件的mapper中的<cache/>中通過type指定緩存機制為ehcache緩存。默認(rèn)為mybatis內(nèi)置的二級緩存org.apache.ibatis.cache.impl.perpetualcache。
1
2
3
4
5
6
|
<mapper namespace= "com.hcx.dao.istudentdao" > <cache type= "org.mybatis.caches.ehcache.ehcachecache" /> <select id=selectstudentbyid resulttype= "com.hcx.beans.student" > select * from student where id=#{id} </select> </mapper> |
4.ehcache在不同mapper中的個性化設(shè)置
在ehcache.xml中設(shè)置的屬性值,會對該項目中所有使用ehcache緩存機制的緩存區(qū)域起作用。一個項目中可以有多個mapper,不同的mapper有不同的緩存區(qū)域。對于不同緩存區(qū)域也可進(jìn)行專門針對于當(dāng)前區(qū)域的個性設(shè)置,可通過指定不同mapper的<cache>屬性值來設(shè)置。
<cache>屬性值的優(yōu)先級高于ehcache.xml中的屬性值。
1
2
3
4
5
6
|
<mapper namespace= "com.hcx.dao.istudentdao" > <cache type= "org.mybatis.caches.ehcache.ehcachecache" /> <property name= "maxelementsinmemory" value= "5000" /> <property name= "timetoidleseconds" value= "240" /> </cache> </mapper> |
以上所述是小編給大家介紹的mybatis查詢緩存實例詳解,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對服務(wù)器之家網(wǎng)站的支持!
原文鏈接:http://www.jianshu.com/p/90a34862dcdc