文章轉(zhuǎn)載自:http://www.itsoku.com/article/257
Mybatis系列目標(biāo):從入門開始開始掌握一個高級開發(fā)所需要的Mybatis技能。
這是mybatis系列第12篇彩掐,源碼位于文章尾部构舟!
什么是緩存?
緩存就是存儲數(shù)據(jù)的一個地方(稱作:Cache),當(dāng)程序要讀取數(shù)據(jù)時,會首先從緩存中獲取侄榴,有則直接返回胖眷,否則從其他存儲設(shè)備中獲取,緩存最重要的一點就是從其內(nèi)部獲取數(shù)據(jù)的速度是非诚则颍快的,通過緩存可以加快數(shù)據(jù)的訪問速度。比如我們從db中獲取數(shù)據(jù)麦撵,中間需要經(jīng)過網(wǎng)絡(luò)傳輸耗時,db server從磁盤讀取數(shù)據(jù)耗時等,如果這些數(shù)據(jù)直接放在jvm對應(yīng)的內(nèi)存中免胃,訪問是不是會快很多音五。
mybatis中的緩存
通常情況下mybatis會訪問數(shù)據(jù)庫獲取數(shù)據(jù),中間涉及到網(wǎng)絡(luò)通信羔沙,數(shù)據(jù)庫從磁盤中讀取數(shù)據(jù)躺涝,然后將數(shù)據(jù)返回給mybatis,總的來說耗時還是挺長的扼雏,mybatis為了加快數(shù)據(jù)查詢的速度坚嗜,在其內(nèi)部引入了緩存來加快數(shù)據(jù)的查詢速度。
mybatis中分為一級緩存和二級緩存诗充。
一級緩存是SqlSession級別的緩存苍蔬,在操作數(shù)據(jù)庫時需要構(gòu)造 sqlSession對象,在對象中有一個數(shù)據(jù)結(jié)構(gòu)(HashMap)用于存儲緩存數(shù)據(jù)蝴蜓,不同的sqlSession之間的緩存數(shù)據(jù)區(qū)域(HashMap)是互相不影響的碟绑。
二級緩存是mapper級別的緩存,多個SqlSession去操作同一個Mapper的sql語句茎匠,多個SqlSession可以共用二級緩存格仲,二級緩存是跨SqlSession的。
下面我們詳細(xì)說一下一級緩存和二級緩存的各種用法和注意點诵冒。
一級緩存
一級緩存是SqlSession級別的緩存
每個SqlSession都有自己單獨的一級緩存
多個SqlSession之間的一級緩存是相互隔離的凯肋,互不影響,
mybatis中一級緩存是默認(rèn)自動開啟的
一級緩存工作原理:
在同一個SqlSession中去多次去執(zhí)行同樣的查詢汽馋,每次執(zhí)行的時候會先到一級緩存中查找侮东,如果緩存中有就直接返回,如果一級緩存中沒有相關(guān)數(shù)據(jù)豹芯,mybatis就會去db中進(jìn)行查找苗桂,然后將查找到的數(shù)據(jù)放入一級緩存中,第二次執(zhí)行同樣的查詢的時候告组,會發(fā)現(xiàn)緩存中已經(jīng)存在了煤伟,會直接返回。
一級緩存的存儲介質(zhì)是內(nèi)存木缝,是用一個HashMap來存儲數(shù)據(jù)的便锨,所以訪問速度是非常快的我碟。
一級緩存案例
案例sql腳本
DROP DATABASE IF EXISTS `javacode2018`;
CREATE DATABASE `javacode2018`;
USE `javacode2018`;
DROP TABLE IF EXISTS t_user;
CREATE TABLE t_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',30),(2,'張學(xué)友',50),(3,'劉德華',50);
下面是查詢用戶信息放案,返回一個list
<select id="getList1" resultType="com.javacode2018.chat05.demo9.model.UserModel" parameterType="map">
SELECT id,name,age FROM t_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>
對應(yīng)的mapper接口方法
List<UserModel> getList1(Map<String, Object> paramMap);
測試用例
com.javacode2018.chat05.demo9.Demo9Test#level1CacheTest1
/**
* 一級緩存測試
*
* @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);
}
}
上面的代碼在同一個SqlSession中去執(zhí)行了2次獲取用戶列表信息,2次查詢結(jié)果分別放在userModelList1
和userModelList2
矫俺,最終代碼中也會判斷這兩個集合是否相等吱殉,下面我們運(yùn)行一下看看會訪問幾次db掸冤?
運(yùn)行輸出
01:15.312 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Preparing: SELECT id,name,age FROM t_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=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50)]
01:15.367 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50)]
01:15.367 [main] INFO c.j.chat05.demo9.Demo9Test - true
從輸出中可以看出看到,sql只輸出了一次友雳,說明第一次會訪問數(shù)據(jù)庫稿湿,第二次直接從緩存中獲取的,最后輸出了一個true
押赊,也說明兩次返回結(jié)果是同一個對象饺藤,第二次直接從緩存中獲取數(shù)據(jù)的,加快了查詢的速度流礁。
清空一級緩存的3種方式
同一個SqlSession中查詢同樣的數(shù)據(jù)涕俗,mybatis默認(rèn)會從一級緩存中獲取,如果緩存中沒有神帅,才會訪問db再姑,那么我們?nèi)绾稳デ蹇找患壘彺婺兀瑥?qiáng)制讓查詢?nèi)ピL問db呢找御?
讓一級緩存失效有3種方式:
- SqlSession中執(zhí)行增元镀、刪、改操作萎坷,此時sqlsession會自動清理其內(nèi)部的一級緩存
- 調(diào)用SqlSession中的clearCache方法清理其內(nèi)部的一級緩存
- 設(shè)置Mapper xml中select元素的
flushCache
屬性值為true凹联,那么執(zhí)行查詢的時候會先清空一級緩存中的所有數(shù)據(jù)沐兰,然后去db中獲取數(shù)據(jù)
上面方式任何一種都會讓當(dāng)前SqlSession中的一級緩存失效哆档,進(jìn)而去db中獲取數(shù)據(jù),下面我們來分別演示這3中情況住闯。
方式1:增刪改讓一級緩存失效
當(dāng)執(zhí)行增刪改操時瓜浸,mybatis會將當(dāng)前SqlSession一級緩存中的所有數(shù)據(jù)都清除。
案例代碼:
com.javacode2018.chat05.demo9.Demo9Test#level1CacheTest2
/**
* 增刪改使一級緩存失效
*
* @throws IOException
*/
@Test
public void level1CacheTest2() 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);
//新增一條數(shù)據(jù)
mapper.insert1(UserModel.builder().id(100).name("路人").age(30).build());
//第二次查詢
List<UserModel> userModelList2 = mapper.getList1(null);
log.info("{}", userModelList2);
log.info("{}", userModelList1 == userModelList2);
}
}
上面同一個SqlSession中執(zhí)行了3個操作比原,同樣的查詢執(zhí)行了2次插佛,2次查詢中間夾了一個插入操作。
運(yùn)行輸出
21:55.097 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Preparing: SELECT id,name,age FROM t_user
21:55.135 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Parameters:
21:55.159 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - <== Total: 3
21:55.159 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50)]
21:55.161 [main] DEBUG c.j.c.d.mapper.UserMapper.insert1 - ==> Preparing: INSERT INTO t_user (id,name,age) VALUES (?,?,?)
21:55.162 [main] DEBUG c.j.c.d.mapper.UserMapper.insert1 - ==> Parameters: 100(Integer), 路人(String), 30(Integer)
21:55.165 [main] DEBUG c.j.c.d.mapper.UserMapper.insert1 - <== Updates: 1
21:55.166 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Preparing: SELECT id,name,age FROM t_user
21:55.166 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Parameters:
21:55.167 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - <== Total: 4
21:55.168 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=100, name=路人, age=30)]
21:55.168 [main] INFO c.j.chat05.demo9.Demo9Test - false
從輸出中可以看出2次查詢都訪問了db量窘,并且兩次查詢的結(jié)果是不一樣的雇寇,兩個集合也不相等,插入數(shù)據(jù)讓緩存失效是可以理解的蚌铜,插入操作可能會改變數(shù)據(jù)庫中的數(shù)據(jù)锨侯,所以如果再從緩存中去獲取,可能獲取到的數(shù)據(jù)和db中的數(shù)據(jù)不一致的情況冬殃,mybatis為了避免這種情況囚痴,在執(zhí)行插入操作的時候,會將SqlSession中的一級緩存清空审葬。當(dāng)然刪除和修改也同樣會改變db中的數(shù)據(jù)深滚,如果在同一個SqlSession中去執(zhí)行刪除或者修改數(shù)據(jù)的時候奕谭,mybatis也一樣會清除一級緩存中的所有數(shù)據(jù),刪除和修改大家自己可以寫2個例子試試痴荐,看看是否也會清理一級緩存中的數(shù)據(jù)血柳。
方式2:SqlSession.clearCache清理一級緩存
SqlSession.clearCache()方法會將當(dāng)前SqlSession一級緩存中的所有數(shù)據(jù)清除。
案例代碼:
com.javacode2018.chat05.demo9.Demo9Test#level1CacheTest3
/**
* 調(diào)用sqlSession.clearCache()清理一級緩存
*
* @throws IOException
*/
@Test
public void level1CacheTest3() 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);
//調(diào)用clearCache方法清理當(dāng)前SqlSession中的緩存
sqlSession.clearCache();
//第二次查詢
List<UserModel> userModelList2 = mapper.getList1(null);
log.info("{}", userModelList2);
log.info("{}", userModelList1 == userModelList2);
}
}
運(yùn)行輸出
31:21.937 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Preparing: SELECT id,name,age FROM t_user
31:21.966 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Parameters:
31:21.985 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - <== Total: 4
31:21.985 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=100, name=路人, age=30)]
31:21.988 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Preparing: SELECT id,name,age FROM t_user
31:21.988 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Parameters:
31:21.989 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - <== Total: 4
31:21.989 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=100, name=路人, age=30)]
31:21.990 [main] INFO c.j.chat05.demo9.Demo9Test - false
從輸出中可以看出蹬昌,2次同樣的查詢都訪問了db混驰。
方式3:Select元素的flushCache置為true
將Mapper xml中select元素的flushCache
屬性置為true的時候,每次執(zhí)行這個select元素對應(yīng)的查詢之前皂贩,mybatis會將當(dāng)前SqlSession中一級緩存中的所有數(shù)據(jù)都清除栖榨。
案例代碼
新增一個select元素的查詢,將flushCache元素置為true明刷,注意:select元素這個屬性的默認(rèn)值是false婴栽。
<select id="getList2" flushCache="true" resultType="com.javacode2018.chat05.demo9.model.UserModel" parameterType="map">
SELECT id,name,age FROM t_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>
對應(yīng)測試用例
com.javacode2018.chat05.demo9.Demo9Test#level1CacheTest4
/**
* 將Mapper xml中select元素的flushCache屬性置為true的時候,每次執(zhí)行這個select元素對應(yīng)的查詢之前辈末,mybatis會將當(dāng)前SqlSession中一級緩存中的所有數(shù)據(jù)都清除愚争。
*
* @throws IOException
*/
@Test
public void level1CacheTest4() throws IOException {
String mybatisConfig = "demo9/mybatis-config.xml";
this.before(mybatisConfig);
try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//查詢1:getList1
log.info("查詢1 start");
log.info("查詢1:getList1->{}", mapper.getList1(null));
//查詢2:getList1
log.info("查詢2 start");
log.info("查詢2:getList1->{}", mapper.getList1(null));
//查詢3:getList2
log.info("查詢3 start");
log.info("查詢3:getList2->{}", mapper.getList2(null));
//查詢4:getList2
log.info("查詢4 start");
log.info("查詢4:getList2->{}", mapper.getList2(null));
//查詢5:getList1
log.info("查詢5 start");
log.info("查詢5:getList1->{}", mapper.getList1(null));
}
}
注意上面的代碼,代碼中有5次查詢挤聘,第1次轰枝、第2次、第5次查詢調(diào)用的都是getList1组去,這個查詢對應(yīng)的mapper xml中的select元素的flushCache屬性沒有設(shè)置鞍陨,默認(rèn)是false;而第3次和第4次查詢調(diào)用的是getList2从隆,getList2這個查詢對應(yīng)的mapper xml中的select(id=getList2)诚撵,它的flushCache屬性設(shè)置的是true,說明第3和第4次查詢會清空當(dāng)前一級緩存中所有數(shù)據(jù)键闺。
最終效果應(yīng)該是查詢1訪問db拿去數(shù)據(jù)寿烟,然后將其丟到一級緩存中,查詢2會直接從一級緩存中拿到數(shù)據(jù)辛燥,而查詢3走的是getList2筛武,發(fā)現(xiàn)flushCache為true,會先清空一級緩存中所有數(shù)據(jù)挎塌,也就是此時查詢1放入緩存的數(shù)據(jù)會被清理掉徘六,然后查詢3會訪問db獲取數(shù)據(jù),然后丟到緩存中勃蜘;而查詢4走的是getList2硕噩,發(fā)現(xiàn)flushCache為true,會先清空緩存缭贡,所以3放入一級緩存的數(shù)據(jù)會被清空炉擅,然后導(dǎo)致查詢4也會訪問db辉懒,查詢5去一級緩存中查詢數(shù)據(jù),因為查詢1和2放入緩存的數(shù)據(jù)都被查詢3清空了谍失,所以導(dǎo)致查詢5發(fā)現(xiàn)一級緩存中沒有數(shù)據(jù)眶俩,也會訪問db去獲取數(shù)據(jù)。
我們來運(yùn)行一下看看是否和我們分析的一致快鱼。
運(yùn)行輸出
20:10.872 [main] INFO c.j.chat05.demo9.Demo9Test - 查詢1 start
20:11.164 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Preparing: SELECT id,name,age FROM t_user
20:11.195 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Parameters:
20:11.216 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - <== Total: 4
20:11.216 [main] INFO c.j.chat05.demo9.Demo9Test - 查詢1:getList1->[UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=100, name=路人, age=30)]
20:11.218 [main] INFO c.j.chat05.demo9.Demo9Test - 查詢2 start
20:11.218 [main] INFO c.j.chat05.demo9.Demo9Test - 查詢2:getList1->[UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=100, name=路人, age=30)]
20:11.219 [main] INFO c.j.chat05.demo9.Demo9Test - 查詢3 start
20:11.219 [main] DEBUG c.j.c.d.mapper.UserMapper.getList2 - ==> Preparing: SELECT id,name,age FROM t_user
20:11.219 [main] DEBUG c.j.c.d.mapper.UserMapper.getList2 - ==> Parameters:
20:11.222 [main] DEBUG c.j.c.d.mapper.UserMapper.getList2 - <== Total: 4
20:11.222 [main] INFO c.j.chat05.demo9.Demo9Test - 查詢3:getList2->[UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=100, name=路人, age=30)]
20:11.222 [main] INFO c.j.chat05.demo9.Demo9Test - 查詢4 start
20:11.223 [main] DEBUG c.j.c.d.mapper.UserMapper.getList2 - ==> Preparing: SELECT id,name,age FROM t_user
20:11.223 [main] DEBUG c.j.c.d.mapper.UserMapper.getList2 - ==> Parameters:
20:11.225 [main] DEBUG c.j.c.d.mapper.UserMapper.getList2 - <== Total: 4
20:11.225 [main] INFO c.j.chat05.demo9.Demo9Test - 查詢4:getList2->[UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=100, name=路人, age=30)]
20:11.225 [main] INFO c.j.chat05.demo9.Demo9Test - 查詢5 start
20:11.225 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Preparing: SELECT id,name,age FROM t_user
20:11.225 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Parameters:
20:11.230 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - <== Total: 4
20:11.230 [main] INFO c.j.chat05.demo9.Demo9Test - 查詢5:getList1->[UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=100, name=路人, age=30)]
大家認(rèn)真看一下上面的輸出颠印,查詢1/3/4/5訪問了db,查詢2從緩存中獲取的抹竹,和我們上面分析的過程一致线罕。
一級緩存使用總結(jié)
- 一級緩存是SqlSession級別的,每個人SqlSession有自己的一級緩存窃判,不同的SqlSession之間一級緩存是相互隔離的
- mybatis中一級緩存默認(rèn)是自動開啟的
- 當(dāng)在同一個SqlSession中執(zhí)行同樣的查詢的時候钞楼,會先從一級緩存中查找,如果找到了直接返回袄琳,如果沒有找到會去訪問db询件,然后將db返回的數(shù)據(jù)丟到一級緩存中,下次查詢的時候直接從緩存中獲取
- 一級緩存清空的3種方式:
- SqlSession中執(zhí)行增刪改會使一級緩存失效唆樊;
- 調(diào)用SqlSession.clearCache方法會使一級緩存失效宛琅;
- Mapper xml中的select元素的flushCache屬性置為true,那么執(zhí)行這個查詢會使一級緩存失效)
二級緩存
二級緩存的使用
一級緩存使用上存在局限性逗旁,必須要在同一個SqlSession中執(zhí)行同樣的查詢嘿辟,一級緩存才能提升查詢速度,如果想在不同的SqlSession之間使用緩存來加快查詢速度痢艺,此時我們需要用到二級緩存了仓洼。
二級緩存是mapper級別的緩存介陶,每個mapper xml有個namespace堤舒,二級緩存和namespace綁定的,每個namespace關(guān)聯(lián)一個二級緩存哺呜,多個SqlSession可以共用二級緩存舌缤,二級緩存是跨SqlSession的。
二級緩存默認(rèn)是沒有開啟的某残,需要我們在mybatis全局配置文件中進(jìn)行開啟:
<settings>
<!-- 開啟二級緩存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
上面配置好了以后国撵,還需要在對應(yīng)的mapper xml加上下面配置,表示這個mapper中的查詢開啟二級緩存:
<cache/>
配置就這么簡單玻墅。
一二級緩存共存時查詢原理
一二級緩存如果都開啟的情況下介牙,數(shù)據(jù)查詢過程如下:
- 當(dāng)發(fā)起一個查詢的時候,mybatis會先訪問這個namespace對應(yīng)的二級緩存澳厢,如果二級緩存中有數(shù)據(jù)則直接返回环础,否則繼續(xù)向下
- 查詢一級緩存中是否有對應(yīng)的數(shù)據(jù)囚似,如果有則直接返回,否則繼續(xù)向下
- 訪問db獲取需要的數(shù)據(jù)线得,然后放在當(dāng)前SqlSession對應(yīng)的一級緩存中饶唤,并且在本地內(nèi)存中的另外一個地方存儲一份(這個地方我們就叫TransactionalCache)
- 當(dāng)SqlSession關(guān)閉的時候,也就是調(diào)用SqlSession的close方法的時候贯钩,此時會將TransactionalCache中的數(shù)據(jù)放到二級緩存中募狂,并且會清空當(dāng)前SqlSession一級緩存中的數(shù)據(jù)。
二級緩存案例
mybatis全局配置文件開啟二級緩存配置
<settings>
<!-- 開啟二級緩存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
mapper xml中使用cache元素開啟二級緩存
<!-- 啟用二級緩存 -->
<cache/>
<select id="getList1" resultType="com.javacode2018.chat05.demo9.model.UserModel" parameterType="map">
SELECT id,name,age FROM t_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>
測試用例
com.javacode2018.chat05.demo9.Demo9Test#level2CacheTest1
/**
* 二級緩存測試
*
* @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祸穷,運(yùn)行一下看看效果。
執(zhí)行輸出
34:36.574 [main] DEBUG c.j.chat05.demo9.mapper.UserMapper - Cache Hit Ratio [com.javacode2018.chat05.demo9.mapper.UserMapper]: 0.0
34:36.831 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Preparing: SELECT id,name,age FROM t_user
34:36.864 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Parameters:
34:36.883 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - <== Total: 4
34:36.883 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=100, name=路人, age=30)]
34:36.894 [main] DEBUG c.j.chat05.demo9.mapper.UserMapper - Cache Hit Ratio [com.javacode2018.chat05.demo9.mapper.UserMapper]: 0.5
34:36.895 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=100, name=路人, age=30)]
注意上面第一行日志輸出:
Cache Hit Ratio [com.javacode2018.chat05.demo9.mapper.UserMapper]: 0.0
對這行做一個解釋:com.javacode2018.chat05.demo9.mapper.UserMapper
是上面查詢訪問的mapper xml的namesapce的值勺三,去這個namespace對應(yīng)的二級緩存中去查詢數(shù)據(jù)粱哼,沒有查詢到,輸出中的0.0表示命中率檩咱,這次沒有命中揭措,所以命中率為0
然后就去db中訪問數(shù)據(jù)了,會將db中返回的數(shù)據(jù)放在一級緩存中刻蚯,第一次運(yùn)行完畢之后會自動調(diào)用SqlSession的close方法绊含,然后db中返回的數(shù)據(jù)會被丟到二級緩存中,第二次查詢的時候就直接從二級緩存中獲取到數(shù)據(jù)返回了炊汹,所以第二次查詢輸出如下:
34:36.894 [main] DEBUG c.j.chat05.demo9.mapper.UserMapper - Cache Hit Ratio [com.javacode2018.chat05.demo9.mapper.UserMapper]: 0.5
34:36.895 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=100, name=路人, age=30)]
2次查詢都去訪問了二級緩存躬充,第二次命中了,命中率為1/2=0.5
清空或者跳過二級緩存的3種方式
當(dāng)二級緩存開啟的時候讨便,在某個mapper xml中添加cache元素之后充甚,這個mapper xml中所有的查詢都默認(rèn)開啟了二級緩存,那么我們?nèi)绾吻蹇栈蛘咛^二級緩存呢霸褒?
3種方式如下:
- 對應(yīng)的mapper中執(zhí)行增刪改查會清空二級緩存中數(shù)據(jù)
- select元素的flushCache屬性置為true伴找,會先清空二級緩存中的數(shù)據(jù),然后再去db中查詢數(shù)據(jù)废菱,然后將數(shù)據(jù)再放到二級緩存中
- select元素的useCache屬性置為false技矮,可以使這個查詢跳過二級緩存,然后去查詢數(shù)據(jù)
下面我們來演示一下每種方式對應(yīng)的效果殊轴。
方式1:增刪改會清除二級緩存中的數(shù)據(jù)
下面我們主要演示一下新增對二級緩存的影響衰倦。
案例代碼
com.javacode2018.chat05.demo9.Demo9Test#level2CacheTest2
/**
* 增刪改會清除二級緩存中的數(shù)據(jù)
*
* @throws IOException
*/
@Test
public void level2CacheTest2() throws IOException {
String mybatisConfig = "demo9/mybatis-config1.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);
}
try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//新增一條數(shù)據(jù)
mapper.insert1(UserModel.builder().id(Integer.valueOf(System.nanoTime() % 100000 + "")).name("路人").age(30).build());
}
try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<UserModel> userModelList1 = mapper.getList1(null);
log.info("{}", userModelList1);
}
}
上面使用了3個不同的SqlSession,第一次和第三次都調(diào)用了getList1執(zhí)行查詢旁理,中間執(zhí)行了一個插入操作樊零,mybatis執(zhí)行插入的時候,會先清除當(dāng)前namespace對應(yīng)的二級緩存中的數(shù)據(jù)孽文,所以上面2次查詢最終都會訪問db驻襟,來運(yùn)行一下看看效果十性。
運(yùn)行輸出
23:00.620 [main] DEBUG c.j.chat05.demo9.mapper.UserMapper - Cache Hit Ratio [com.javacode2018.chat05.demo9.mapper.UserMapper]: 0.0
23:00.900 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Preparing: SELECT id,name,age FROM t_user
23:00.924 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Parameters:
23:00.948 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - <== Total: 3
23:00.948 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50)]
23:00.951 [main] DEBUG c.j.c.d.mapper.UserMapper.insert1 - ==> Preparing: INSERT INTO t_user (id,name,age) VALUES (?,?,?)
23:00.953 [main] DEBUG c.j.c.d.mapper.UserMapper.insert1 - ==> Parameters: 79600(Integer), 路人(String), 30(Integer)
23:00.955 [main] DEBUG c.j.c.d.mapper.UserMapper.insert1 - <== Updates: 1
23:00.959 [main] DEBUG c.j.chat05.demo9.mapper.UserMapper - Cache Hit Ratio [com.javacode2018.chat05.demo9.mapper.UserMapper]: 0.0
23:00.959 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Preparing: SELECT id,name,age FROM t_user
23:00.959 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Parameters:
23:00.961 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - <== Total: 4
23:00.961 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=79600, name=路人, age=30)]
23:00.961 [main] DEBUG c.j.c.d.mapper.UserMapper.insert1 - ==> Preparing: INSERT INTO t_user (id,name,age) VALUES (?,?,?)
23:00.962 [main] DEBUG c.j.c.d.mapper.UserMapper.insert1 - ==> Parameters: 94100(Integer), 路人(String), 30(Integer)
23:00.967 [main] DEBUG c.j.c.d.mapper.UserMapper.insert1 - <== Updates: 1
從輸出中可以看出,2次查詢都訪問了db塑悼。上面演示的是插入會清空二級緩存的數(shù)據(jù)劲适,同樣刪除和修改也會先清除當(dāng)前namespace對應(yīng)的二級緩存中的數(shù)據(jù)。
方式2:select元素的flushCache屬性置為true
當(dāng)將mapper xml中select元素的flushCache屬性置為true厢蒜,會先清空二級緩存中的數(shù)據(jù)霞势,然后再去db中查詢數(shù)據(jù),然后將數(shù)據(jù)再放到二級緩存中斑鸦。
代碼
<select id="getList2" flushCache="true" resultType="com.javacode2018.chat05.demo9.model.UserModel"
parameterType="map">
SELECT id,name,age FROM t_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>
測試用例
com.javacode2018.chat05.demo9.Demo9Test#level2CacheTest3
/**
* 當(dāng)將mapper xml中select元素的flushCache屬性置為true愕贡,會先清空二級緩存中的數(shù)據(jù),然后再去db中查詢數(shù)據(jù)巷屿,然后將數(shù)據(jù)再放到二級緩存中
*
* @throws IOException
*/
@Test
public void level2CacheTest3() throws IOException {
String mybatisConfig = "demo9/mybatis-config1.xml";
this.before(mybatisConfig);
//先查詢2次getList1,getList1第二次會從二級緩存中拿到數(shù)據(jù)
log.info("getList1查詢");
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);
}
}
//getList2的flushCache為true固以,所以查詢之前會先將對應(yīng)的二級緩存中的所有數(shù)據(jù)清空,所以二次都會訪問db
log.info("getList2查詢");
for (int i = 0; i < 2; i++) {
try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<UserModel> userModelList1 = mapper.getList2(null);
log.info("{}", userModelList1);
}
}
//二級緩存中沒有g(shù)etList1需要查找的數(shù)據(jù)了嘱巾,所以這次訪問getList1會去訪問db
log.info("getList1查詢");
try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<UserModel> userModelList1 = mapper.getList1(null);
log.info("{}", userModelList1);
}
}
運(yùn)行輸出
02:51.560 [main] INFO c.j.chat05.demo9.Demo9Test - getList1查詢
02:51.842 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Preparing: SELECT id,name,age FROM t_user
02:51.871 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Parameters:
02:51.891 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - <== Total: 4
02:51.892 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=5100, name=路人, age=30)]
02:51.905 [main] DEBUG c.j.chat05.demo9.mapper.UserMapper - Cache Hit Ratio [com.javacode2018.chat05.demo9.mapper.UserMapper]: 0.5
02:51.906 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=5100, name=路人, age=30)]
02:51.906 [main] INFO c.j.chat05.demo9.Demo9Test - getList2查詢
02:51.906 [main] DEBUG c.j.chat05.demo9.mapper.UserMapper - Cache Hit Ratio [com.javacode2018.chat05.demo9.mapper.UserMapper]: 0.3333333333333333
02:51.907 [main] DEBUG c.j.c.d.mapper.UserMapper.getList2 - ==> Preparing: SELECT id,name,age FROM t_user
02:51.907 [main] DEBUG c.j.c.d.mapper.UserMapper.getList2 - ==> Parameters:
02:51.909 [main] DEBUG c.j.c.d.mapper.UserMapper.getList2 - <== Total: 4
02:51.909 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=5100, name=路人, age=30)]
02:51.910 [main] DEBUG c.j.chat05.demo9.mapper.UserMapper - Cache Hit Ratio [com.javacode2018.chat05.demo9.mapper.UserMapper]: 0.5
02:51.913 [main] DEBUG c.j.c.d.mapper.UserMapper.getList2 - ==> Preparing: SELECT id,name,age FROM t_user
02:51.913 [main] DEBUG c.j.c.d.mapper.UserMapper.getList2 - ==> Parameters:
02:51.914 [main] DEBUG c.j.c.d.mapper.UserMapper.getList2 - <== Total: 4
02:51.914 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=5100, name=路人, age=30)]
02:51.915 [main] INFO c.j.chat05.demo9.Demo9Test - getList1查詢
02:51.915 [main] DEBUG c.j.chat05.demo9.mapper.UserMapper - Cache Hit Ratio [com.javacode2018.chat05.demo9.mapper.UserMapper]: 0.4
02:51.915 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Preparing: SELECT id,name,age FROM t_user
02:51.915 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Parameters:
02:51.917 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - <== Total: 4
02:51.917 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=5100, name=路人, age=30)]
第一次查詢訪問db憨琳,第二次查詢從二級緩存中獲取了數(shù)據(jù),第3和第4查詢訪問的是getList2旬昭,這個查詢會清空二級緩存中的數(shù)據(jù)篙螟,直接去db中查詢,查詢4執(zhí)行完畢之后问拘,二級緩存中只有第四次查詢的數(shù)據(jù)遍略,第5次查詢?nèi)etList1中獲取數(shù)據(jù),此時二級緩存中沒有骤坐,所以直接去db中獲取了绪杏。
方式3:select元素的useCache置為false跳過二級緩存,但是不會情況二級緩存數(shù)據(jù)
新增一個select3查詢纽绍,將useCache置為false蕾久,如下:
<select id="getList3" useCache="false" resultType="com.javacode2018.chat05.demo9.model.UserModel"
parameterType="map">
SELECT id,name,age FROM t_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>
對應(yīng)的用例代碼
/**
* select元素的useCache置為false跳過二級緩存,但是不會情況二級緩存數(shù)據(jù)
*
* @throws IOException
*/
@Test
public void level2CacheTest4() throws IOException {
String mybatisConfig = "demo9/mybatis-config1.xml";
this.before(mybatisConfig);
//第一次查詢訪問getList1顶岸,會將數(shù)據(jù)丟到二級緩存中
try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<UserModel> userModelList1 = mapper.getList1(null);
log.info("{}", userModelList1);
}
//getList3對應(yīng)的select的useCache為false腔彰,會跳過二級緩存叫编,所以會直接去訪問db
for (int i = 0; i < 2; i++) {
try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<UserModel> userModelList1 = mapper.getList3(null);
log.info("{}", userModelList1);
}
}
//下面的查詢又去執(zhí)行了getList1辖佣,由于上面的第一次查詢也是訪問getList1會將數(shù)據(jù)放在二級緩存中,所以下面的查詢會從二級緩存中獲取到數(shù)據(jù)
try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<UserModel> userModelList1 = mapper.getList1(null);
log.info("{}", userModelList1);
}
}
注意上面有4次查詢搓逾,第一次查詢訪問getList1卷谈,會將數(shù)據(jù)丟到二級緩存中,而第二三次查詢訪問的是getList3霞篡,getList3對應(yīng)的select的useCache為false世蔗,會跳過二級緩存端逼,所以會直接去訪問db,第四次查詢也是訪問getList1污淋,由于第一次查詢也是訪問getList1會將數(shù)據(jù)放在二級緩存中顶滩,所以第4次查詢直接從二級緩存中獲取到了數(shù)據(jù),運(yùn)行輸出:
13:38.454 [main] DEBUG c.j.chat05.demo9.mapper.UserMapper - Cache Hit Ratio [com.javacode2018.chat05.demo9.mapper.UserMapper]: 0.0
13:38.852 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Preparing: SELECT id,name,age FROM t_user
13:38.898 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - ==> Parameters:
13:38.929 [main] DEBUG c.j.c.d.mapper.UserMapper.getList1 - <== Total: 4
13:38.930 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=5100, name=路人, age=30)]
13:38.941 [main] DEBUG c.j.c.d.mapper.UserMapper.getList3 - ==> Preparing: SELECT id,name,age FROM t_user
13:38.942 [main] DEBUG c.j.c.d.mapper.UserMapper.getList3 - ==> Parameters:
13:38.945 [main] DEBUG c.j.c.d.mapper.UserMapper.getList3 - <== Total: 4
13:38.945 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=5100, name=路人, age=30)]
13:38.946 [main] DEBUG c.j.c.d.mapper.UserMapper.getList3 - ==> Preparing: SELECT id,name,age FROM t_user
13:38.946 [main] DEBUG c.j.c.d.mapper.UserMapper.getList3 - ==> Parameters:
13:38.952 [main] DEBUG c.j.c.d.mapper.UserMapper.getList3 - <== Total: 4
13:38.952 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=5100, name=路人, age=30)]
13:38.957 [main] DEBUG c.j.chat05.demo9.mapper.UserMapper - Cache Hit Ratio [com.javacode2018.chat05.demo9.mapper.UserMapper]: 0.5
13:38.957 [main] INFO c.j.chat05.demo9.Demo9Test - [UserModel(id=1, name=路人甲Java, age=30), UserModel(id=2, name=張學(xué)友, age=50), UserModel(id=3, name=劉德華, age=50), UserModel(id=5100, name=路人, age=30)]
從輸出中可以看出前面3次查詢都訪問db了寸爆,最后一次查詢訪問的是二級緩存命中了數(shù)據(jù)礁鲁。4次查詢,第1次和第4次會訪問二級緩存赁豆,中間2次跳過了二級緩存仅醇,二級緩存命中了1次,所以最后一次輸出的命中率是0.5
總結(jié)
- 一二級緩存訪問順序:一二級緩存都存在的情況下魔种,會先訪問二級緩存析二,然后再訪問一級緩存,最后才會訪問db节预,這個順序大家理解一下
- 將mapper xml中select元素的flushCache屬性置為false叶摄,最終會清除一級緩存所有數(shù)據(jù),同時會清除這個select所在的namespace對應(yīng)的二級緩存中所有的數(shù)據(jù)
- 將mapper xml中select元素的useCache置為false安拟,會使這個查詢跳過二級緩存
- 總體上來說使用緩存可以提升查詢效率准谚,這塊知識掌握了,大家可以根據(jù)業(yè)務(wù)自行選擇
案例代碼
鏈接:https://pan.baidu.com/s/1vt-MAX3oJOu9gyxZAhKkbg
提取碼:i8op
MyBatis系列
- MyBatis系列第1篇:MyBatis未出世之前我們那些痛苦的經(jīng)歷
- MyBatis系列第2篇:入門篇去扣,帶你感受一下mybatis獨特的魅力柱衔!
- MyBatis系列第3篇:Mybatis使用詳解(1)
- MyBatis系列第4篇:Mybatis使用詳解(2)
- Mybatis系列第5篇:Mapper接口多種方式傳參詳解、原理愉棱、源碼解析
- Mybatis系列第6篇:恕我直言唆铐,mybatis增刪改你未必玩得轉(zhuǎn)!
- Mybatis系列第7篇:各種查詢詳解
- Mybatis系列第8篇:自動映射,使用需謹(jǐn)慎奔滑!
- Mybatis系列第9篇:延遲加載艾岂、鑒別器、繼承怎么玩朋其?
- Mybatis系列第10篇:動態(tài)SQL王浴,這么多種你都會?
- Mybatis系列第11篇:類型處理器梅猿,這個你得會玩
更多好文章
- Java高并發(fā)系列(共34篇)
- MySql高手系列(共27篇)
- Maven高手系列(共10篇)
- 聊聊db和緩存一致性常見的實現(xiàn)方式
- 接口冪等性這么重要氓辣,它是什么?怎么實現(xiàn)袱蚓?
想了解更多精彩內(nèi)容請關(guān)注我的公眾號:路人甲Java