查詢緩存的使用,主要是為了提高查詢?cè)L問速度。將用戶對(duì)同一數(shù)據(jù)的重復(fù)查詢過程簡(jiǎn)化,不再每次均從數(shù)據(jù)庫查詢獲取結(jié)果數(shù)據(jù),從而提高訪問速度。
MyBatis的查詢緩存機(jī)制,根據(jù)緩存區(qū)的作用域(生命周期)可劃分為兩種:一級(jí)緩存與二級(jí)緩存
一、一級(jí)查詢緩存
MyBatis一級(jí)緩存是基于org.apache.ibatis.cache.impl.PerpetualCache類的HashMap本地緩存,其作用域是Sqlsession。在同一個(gè)Sqlsession中兩次執(zhí)行相同的sql語句,第一次執(zhí)行完畢后,會(huì)將查詢結(jié)果寫入到緩存中,第二次會(huì)從緩存中直接獲取數(shù)據(jù),而不再到數(shù)據(jù)庫中進(jìn)行查詢,從而提高查詢效率。
當(dāng)一個(gè)Sqlsession結(jié)束后,該Sqlsession中的一級(jí)緩存也就不存在了。MyBatis默認(rèn)一級(jí)緩存是開啟狀態(tài),且不能關(guān)閉。

1.一級(jí)緩存的存在性證明
測(cè)試類:
//證明一級(jí)緩存的存在 @Test public void test01(){ //第一次查詢 Student student = dao.selectStudentById(2); System.out.println(student); //第二次查詢 Student student2 = dao.selectStudentById(2); System.out.println(student2); }
mapper:
<mapper namespace="com.hcx.dao.IStudentDao"> <select id=selectStudentById resultType="com.hcx.beans.Student"> select * from student where id=#{id} </select> </mapper>
控制臺(tái):
執(zhí)行完后,發(fā)現(xiàn)只執(zhí)行了一次從DB中的查詢,第二次的結(jié)果是直接輸出的。說明,第二次是從Sqlsession緩存中讀取的。

2.從緩存讀取數(shù)據(jù)的依據(jù)是sql的id
一級(jí)緩存緩存的是相同sql映射id的查詢結(jié)果,而非相同sql語句的查詢結(jié)果。因?yàn)镸yBatis內(nèi)部對(duì)于查詢緩存,無論是一級(jí)查詢還是二級(jí)查詢,其底層均使用一個(gè)hashmap實(shí)現(xiàn):key為sql的id相關(guān)內(nèi)容,value為從數(shù)據(jù)庫中查詢出的結(jié)果。
mapper:
<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接口:
public interface IStudentDao { Student selectStudentById(int id); Student selectStudentById2(int id); }
測(cè)試類:
//證明從一級(jí)緩存中讀取數(shù)據(jù)的依據(jù): //MyBatis:sql的id+sql語句 //hibernate:查詢結(jié)果對(duì)象的id @Test public void test02(){ Student student = dao.selectStudentById(2); System.out.println(student); Student student2 = dao.selectStudentById2(2); System.out.println(student2); }
控制臺(tái):
查看控制臺(tái),發(fā)現(xiàn)第二次查詢結(jié)果與第一次的完全相同,但第二次查詢并沒有從緩存中讀取數(shù)據(jù),而是直接從DB中進(jìn)行的查詢。這是因?yàn)閺木彺孀x取數(shù)據(jù)的依據(jù)是查詢sql的映射id,而非查詢結(jié)果。

3.增刪改對(duì)一級(jí)查詢緩存的影響
增刪改操作,無論是否進(jìn)行提交Sqlsession.commit(),均會(huì)清空一級(jí)查詢緩存,使查詢?cè)俅螐腄B中select。
測(cè)試類:
@Test public void test03(){ Student student = dao.selectStudentById(2); System.out.println(student); //增刪改操作都會(huì)清空一級(jí)緩存,無論是否提交 dao.insertStudent(new Student("趙六",26,96.6)); Student student2 = dao.selectStudentById(2); System.out.println(student2); }
控制臺(tái):

二、內(nèi)置二級(jí)查詢緩存
MyBatis查詢緩存的作用域是根據(jù)映射文件mapper的namespace劃分的,相同namespace的mapper查詢數(shù)據(jù)存放在同一個(gè)緩存區(qū)域。不同namespace下的數(shù)據(jù)互不干擾。
無論是一級(jí)緩存還是二級(jí)緩存,都是按照namespace進(jìn)行分別存放的。但一、二級(jí)緩存的不同之處在于,Sqlsession一旦關(guān)閉,則Sqlsession中的數(shù)據(jù)將不存在,即一級(jí)緩存就不復(fù)存在。而二級(jí)緩存的生命周期會(huì)與整個(gè)應(yīng)用同步,與Sqlsession是否關(guān)閉無關(guān)。
使用二級(jí)緩存的目的,不是共享數(shù)據(jù),因?yàn)镸yBatis從緩存中讀取數(shù)據(jù)的依據(jù)是sql的id,而非查詢出的對(duì)象。所以,二級(jí)緩存中的數(shù)據(jù)不是為了在多個(gè)查詢之間共享(所有查詢中只要查詢結(jié)果中存在該對(duì)象的,就直接從緩存中讀取,這是對(duì)數(shù)據(jù)的共享,hibernate中的緩存就是為了共享,但MyBatis不是),而是為了延長(zhǎng)該查詢結(jié)果的保存時(shí)間,提高系統(tǒng)性能。
1.二級(jí)緩存用法
二級(jí)緩存的使用只需要完成兩步:
序列化實(shí)體
在mapper映射文件中添加<cache/>標(biāo)簽
1.實(shí)體序列化
要求查詢結(jié)果所涉及到的實(shí)體類要實(shí)現(xiàn)java.io.Serializable接口。若該實(shí)體類存在父類,或其具有域?qū)傩裕瑒t父類與域?qū)傩灶愐惨獙?shí)現(xiàn)序列化接口。
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)簽