這是mybatis系列第11篇浴滴。沒看前文的建議先去【Java冢狐】公眾號(hào)中查看前文瘸洛,方便理解和掌握守伸。
什么是緩存斤吐?
緩存就是存儲(chǔ)數(shù)據(jù)的一個(gè)地方(稱作:Cache)站粟,當(dāng)程序要讀取數(shù)據(jù)時(shí)黍图,會(huì)首先從緩存中獲取,有則直接返回奴烙,否則從其他存儲(chǔ)設(shè)備中獲取雌隅,緩存最重要的一點(diǎn)就是從其內(nèi)部獲取數(shù)據(jù)的速度是非常快的缸沃,通過緩存可以加快數(shù)據(jù)的訪問速度恰起。比如我們從db中獲取數(shù)據(jù),中間需要經(jīng)過網(wǎng)絡(luò)傳輸耗時(shí)趾牧,db server從磁盤讀取數(shù)據(jù)耗時(shí)等检盼,如果這些數(shù)據(jù)直接放在jvm對(duì)應(yīng)的內(nèi)存中,訪問是不是會(huì)快很多翘单。
mybatis中的緩存
mybatis中分為一級(jí)緩存和二級(jí)緩存吨枉。
- 一級(jí)緩存是SqlSession級(jí)別的緩存,在操作數(shù)據(jù)庫(kù)時(shí)需要構(gòu)造 sqlSession對(duì)象哄芜,在對(duì)象中有一個(gè)數(shù)據(jù)結(jié)構(gòu)(HashMap)用于存儲(chǔ)緩存數(shù)據(jù)貌亭,不同的sqlSession之間的緩存數(shù)據(jù)區(qū)域(HashMap)是互相不影響的。
- 二級(jí)緩存是mapper級(jí)別的緩存认臊,多個(gè)SqlSession去操作同一個(gè)Mapper的sql語(yǔ)句圃庭,多個(gè)SqlSession可以共用二級(jí)緩存,二級(jí)緩存是跨SqlSession的失晴。
一級(jí)緩存
一級(jí)緩存是SqlSession級(jí)別的緩存剧腻,每個(gè)SqlSession都有自己?jiǎn)为?dú)的一級(jí)緩存,多個(gè)SqlSession之間的一級(jí)緩存是相互隔離的涂屁,互不影響书在,mybatis中一級(jí)緩存是默認(rèn)自動(dòng)開啟的。
一級(jí)緩存工作原理:在同一個(gè)SqlSession中去多次去執(zhí)行同樣的查詢拆又,每次執(zhí)行的時(shí)候會(huì)先到一級(jí)緩存中查找儒旬,如果緩存中有就直接返回栏账,如果一級(jí)緩存中沒有相關(guān)數(shù)據(jù),mybatis就會(huì)去db中進(jìn)行查找栈源,然后將查找到的數(shù)據(jù)放入一級(jí)緩存中发笔,第二次執(zhí)行同樣的查詢的時(shí)候,會(huì)發(fā)現(xiàn)緩存中已經(jīng)存在了凉翻,會(huì)直接返回了讨。一級(jí)緩存的存儲(chǔ)介質(zhì)是內(nèi)存,是用一個(gè)HashMap來(lái)存儲(chǔ)數(shù)據(jù)的制轰,所以訪問速度是非城凹疲快的。
一級(jí)緩存案例
案例sql腳本
DROP DATABASE IF EXISTS `mybatisdemo`;
CREATE DATABASE `mybatisdemo`;
USE `mybatisdemo`;
DROP TABLE IF EXISTS user;
CREATE TABLE user(
id int AUTO_INCREMENT PRIMARY KEY COMMENT '用戶id',
name VARCHAR(32) NOT NULL DEFAULT '' COMMENT '用戶名',
age SMALLINT NOT NULL DEFAULT 1 COMMENT '年齡'
) COMMENT '用戶表';
INSERT INTO t_user VALUES (1,'Java冢狐',18),(2,'Java',100),(3,'冢狐',23);
下面是查詢用戶信息垃杖,返回一個(gè)list
<select id="getList1" resultType="com.zhonghu.chat12.demo9.model.UserModel" parameterType="map">
SELECT id,name,age FROM user
<where>
<if test="id!=null">
AND id = #{id}
</if>
<if test="name!=null and name.toString()!=''">
AND name = #{name}
</if>
<if test="age!=null">
AND age = #{age}
</if>
</where>
</select>
對(duì)應(yīng)的mapper接口方法
List<UserModel> getList1(Map<String, Object> paramMap);
測(cè)試用例
com.zhonghu.chat12.demo9.Demo9Test#level1CacheTest1
/**
* 一級(jí)緩存測(cè)試
*
* @throws IOException
*/
@Test
public void level1CacheTest1() throws IOException {
String mybatisConfig = "demo9/mybatis-config.xml";
this.before(mybatisConfig);
try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//第一次查詢
List<UserModel> userModelList1 = mapper.getList1(null);
log.info("{}", userModelList1);
//第二次查詢
List<UserModel> userModelList2 = mapper.getList1(null);
log.info("{}", userModelList2);
log.info("{}", userModelList1 == userModelList2);
}
}
上面的代碼在同一個(gè)SqlSession中去執(zhí)行了2次獲取用戶列表信息男杈,2次查詢結(jié)果分別放在userModelList1和userModelList2,最終代碼中也會(huì)判斷這兩個(gè)集合是否相等调俘,下面我們運(yùn)行一下看看會(huì)訪問幾次db伶棒。
運(yùn)行輸出
01:15.312 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Preparing: SELECT id,name,age FROM user
01:15.340 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Parameters:
01:15.364 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - <== Total: 3
01:15.364 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=Java冢狐, age=18), UserModel(id=2, name=Java, age=100), UserModel(id=3, name=冢狐, age=23)]
01:15.367 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=Java冢狐, age=18), UserModel(id=2, name=Java, age=100), UserModel(id=3, name=冢狐, age=23)]
01:15.367 [main] INFO c.j.chat05.demo9.Demo9Test - true
從輸出中可以看出看到,sql只輸出了一次彩库,說明第一次會(huì)訪問數(shù)據(jù)庫(kù)肤无,第二次直接從緩存中獲取的,最后輸出了一個(gè)true骇钦,也說明兩次返回結(jié)果是同一個(gè)對(duì)象宛渐,第二次直接從緩存中獲取數(shù)據(jù)的,加快了查詢的速度眯搭。
清空一級(jí)緩存的3種方式
同一個(gè)SqlSession中查詢同樣的數(shù)據(jù)窥翩,mybatis默認(rèn)會(huì)從一級(jí)緩存中獲取,如果緩存中沒有鳞仙,才會(huì)訪問db寇蚊,那么如果我們希望不走緩存而是直接去訪問數(shù)據(jù)庫(kù),又該如何操作呢棍好?
讓一級(jí)緩存失效有3種方式:
- SqlSession中執(zhí)行增仗岸、刪、改操作梳玫,此時(shí)sqlsession會(huì)自動(dòng)清理其內(nèi)部的一級(jí)緩存
- 調(diào)用SqlSession中的clearCache方法清理其內(nèi)部的一級(jí)緩存
- 設(shè)置Mapper xml中select元素的flushCache屬性值為true爹梁,那么執(zhí)行查詢的時(shí)候會(huì)先清空一級(jí)緩存中的所有數(shù)據(jù),然后去db中獲取數(shù)據(jù)
上面方式任何一種都會(huì)讓當(dāng)前SqlSession中的以及緩存失效提澎,進(jìn)而去db中獲取數(shù)據(jù)。
一級(jí)緩存使用總結(jié)
- 一級(jí)緩存是SqlSession級(jí)別的念链,每個(gè)人SqlSession有自己的一級(jí)緩存盼忌,不同的SqlSession之間一級(jí)緩存是相互隔離的
- mybatis中一級(jí)緩存默認(rèn)是自動(dòng)開啟的
- 當(dāng)在同一個(gè)SqlSession中執(zhí)行同樣的查詢的時(shí)候积糯,會(huì)先從一級(jí)緩存中查找,如果找到了直接返回谦纱,如果沒有找到會(huì)去訪問db看成,然后將db返回的數(shù)據(jù)丟到一級(jí)緩存中,下次查詢的時(shí)候直接從緩存中獲取
- 一級(jí)緩存清空的3種方式(1:SqlSession中執(zhí)行增刪改會(huì)使一級(jí)緩存失效跨嘉;2:調(diào)用SqlSession.clearCache方法會(huì)使一級(jí)緩存失效川慌;3:Mapper xml中的select元素的flushCache屬性置為true,那么執(zhí)行這個(gè)查詢會(huì)使一級(jí)緩存失效)
二級(jí)緩存
二級(jí)緩存的使用
一級(jí)緩存使用上存在局限性祠乃,必須要在同一個(gè)SqlSession中執(zhí)行同樣的查詢梦重,一級(jí)緩存才能提升查詢速度,如果想在不同的SqlSession之間使用緩存來(lái)加快查詢速度亮瓷,此時(shí)我們需要用到二級(jí)緩存了琴拧。
二級(jí)緩存是mapper級(jí)別的緩存,每個(gè)mapper xml有個(gè)namespace嘱支,二級(jí)緩存和namespace綁定的蚓胸,每個(gè)namespace關(guān)聯(lián)一個(gè)二級(jí)緩存,多個(gè)SqlSession可以共用二級(jí)緩存除师,二級(jí)緩存是跨SqlSession的沛膳。
二級(jí)緩存默認(rèn)是沒有開啟的,需要我們?cè)趍ybatis全局配置文件中進(jìn)行開啟:
<settings>
<!-- 開啟二級(jí)緩存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
上面配置好了以后汛聚,還需要在對(duì)應(yīng)的mapper xml加上下面配置于置,表示這個(gè)mapper中的查詢開啟二級(jí)緩存:
<cache/>
配置就這么簡(jiǎn)單。
一二級(jí)緩存共存時(shí)查詢?cè)?/h4>
一二級(jí)緩存如果都開啟的情況下贞岭,數(shù)據(jù)查詢過程如下:
- 當(dāng)發(fā)起一個(gè)查詢的時(shí)候八毯,mybatis會(huì)先訪問這個(gè)namespace對(duì)應(yīng)的二級(jí)緩存,如果二級(jí)緩存中有數(shù)據(jù)則直接返回瞄桨,否則繼續(xù)向下
- 查詢一級(jí)緩存中是否有對(duì)應(yīng)的數(shù)據(jù)话速,如果有則直接返回,否則繼續(xù)向下
- 訪問db獲取需要的數(shù)據(jù)芯侥,然后放在當(dāng)前SqlSession對(duì)應(yīng)的二級(jí)緩存中泊交,并且在本地內(nèi)存中的另外一個(gè)地方存儲(chǔ)一份(這個(gè)地方我們就叫TransactionalCache)
- 當(dāng)SqlSession關(guān)閉的時(shí)候,也就是調(diào)用SqlSession的close方法的時(shí)候柱查,此時(shí)會(huì)將TransactionalCache中的數(shù)據(jù)放到二級(jí)緩存中廓俭,并且會(huì)清空當(dāng)前SqlSession一級(jí)緩存中的數(shù)據(jù)。
二級(jí)緩存案例
mybatis全局配置文件開啟二級(jí)緩存配置
<settings>
<!-- 開啟二級(jí)緩存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
mapper xml中使用cache元素開啟二級(jí)緩存
<!-- 啟用二級(jí)緩存 -->
<cache/>
<select id="getList1" resultType="com.zhonghu.chat12.demo9.model.UserModel" parameterType="map">
SELECT id,name,age FROM user
<where>
<if test="id!=null">
AND id = #{id}
</if>
<if test="name!=null and name.toString()!=''">
AND name = #{name}
</if>
<if test="age!=null">
AND age = #{age}
</if>
</where>
</select>
測(cè)試用例
com.zhonghu.chat12.demo9.Demo9Test#level2CacheTest1
/**
* 二級(jí)緩存測(cè)試
*
* @throws IOException
*/
@Test
public void level2CacheTest1() throws IOException {
String mybatisConfig = "demo9/mybatis-config1.xml";
this.before(mybatisConfig);
for (int i = 0; i < 2; i++) {
try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<UserModel> userModelList1 = mapper.getList1(null);
log.info("{}", userModelList1);
}
}
}
上面執(zhí)行了2次查詢唉工,每次查詢都是新的SqlSession研乒。
清空或者跳過二級(jí)緩存的3種方式
當(dāng)二級(jí)緩存開啟的時(shí)候,在某個(gè)mapper xml中添加cache元素之后淋硝,這個(gè)mapper xml中所有的查詢都默認(rèn)開啟了二級(jí)緩存雹熬,那么我們?nèi)绾吻蹇栈蛘咛^二級(jí)緩存呢宽菜?3種方式如下:
- 對(duì)應(yīng)的mapper中執(zhí)行增刪改查會(huì)清空二級(jí)緩存中數(shù)據(jù)
- select元素的flushCache屬性置為true,會(huì)先清空二級(jí)緩存中的數(shù)據(jù)竿报,然后再去db中查詢數(shù)據(jù)铅乡,然后將數(shù)據(jù)再放到二級(jí)緩存中
- select元素的useCache屬性置為true,可以使這個(gè)查詢跳過二級(jí)緩存烈菌,然后去查詢數(shù)據(jù)
總結(jié)
一二級(jí)緩存訪問順序:一二級(jí)緩存都存在的情況下阵幸,會(huì)先訪問二級(jí)緩存,然后再訪問一級(jí)緩存芽世,最后才會(huì)訪問db挚赊,這個(gè)順序大家理解一下
將mapper xml中select元素的flushCache屬性置為false,最終會(huì)清除一級(jí)緩存所有數(shù)據(jù)捂襟,同時(shí)會(huì)清除這個(gè)select所在的namespace對(duì)應(yīng)的二級(jí)緩存中所有的數(shù)據(jù)
將mapper xml中select元素的useCache置為false咬腕,會(huì)使這個(gè)查詢跳過二級(jí)緩存
-
總體上來(lái)說使用緩存可以提升查詢效率,這塊知識(shí)掌握了葬荷,大家可以根據(jù)業(yè)務(wù)自行選擇
最后
- 如果覺得看完有收獲涨共,希望能關(guān)注一下,順便給我點(diǎn)個(gè)贊宠漩,這將會(huì)是我更新的最大動(dòng)力举反,感謝各位的支持
- 歡迎各位關(guān)注我的公眾號(hào)【java冢狐】,專注于java和計(jì)算機(jī)基礎(chǔ)知識(shí)扒吁,保證讓你看完有所收獲火鼻,不信你打我
- 求一鍵三連:點(diǎn)贊、轉(zhuǎn)發(fā)雕崩、在看魁索。
- 如果看完有不同的意見或者建議,歡迎多多評(píng)論一起交流盼铁。感謝各位的支持以及厚愛粗蔚。
——我是冢狐,和你一樣熱愛編程饶火。
歡迎關(guān)注公眾號(hào)“ Java冢狐”鹏控,獲取最新消息