一級(jí)緩存
- Mybatis的一級(jí)緩存存在于sqlSession的生命周期中,在同一個(gè)SqlSession中查詢時(shí),MyBatis會(huì)把執(zhí)行方法和參數(shù)通過(guò)算法 生成緩存的鍵值,將鍵值和查詢結(jié)果存入一個(gè)Map對(duì)象中。如果同一個(gè)SqlSession中執(zhí)行的方法和參數(shù)完全一致,就會(huì)直接去Map中的鍵值匹配??梢酝ㄟ^(guò)設(shè)置方法中的flushCache屬性取消一級(jí)緩存。
- 當(dāng)執(zhí)行delete、update、insert后都會(huì)清空一級(jí)緩存。
二級(jí)緩存
- MyBatist的二級(jí)緩存非常強(qiáng)大,存在于SqlSessionFactory的生命周期中。當(dāng)存在多個(gè)SqlSessionFactory時(shí),它們的緩存都是綁定在各自的對(duì)象上的,緩存數(shù)據(jù)一般情況下不能相痛!只有使用如Redis這樣的緩存數(shù)據(jù)庫(kù)時(shí),才能共享緩存。
使用方法:
在Mapper.xml的<mapper>便簽中添加<cache/>。
默認(rèn)的二級(jí)緩存有如下的效果:
- 映射語(yǔ)句中的所以SELECT語(yǔ)句將會(huì)被緩存。
- 映射語(yǔ)句中的所以INSERT、UPDATE、DELETE語(yǔ)句會(huì)刷新緩存。
- 緩存會(huì)使用Least Recently Used算法回收。
- 根據(jù)時(shí)間表刷新,緩存不會(huì)以任何的時(shí)間順序刷新。//只是默認(rèn)的情況
- 緩存會(huì)存儲(chǔ)集合或?qū)ο蟮?024個(gè)引用。(無(wú)論方法返回的什么類型的值)
- 緩存都會(huì)視為read/write。意味著對(duì)象檢索不是共享的,而且可以安全地被調(diào)用者修改,而不干擾其他調(diào)用者或線程所做的修改。
使用方法:
- 在接口中添加標(biāo)簽@CacheNamespace
或者
- 在mapper.xml中添加<cache/>
實(shí)例:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="false"/>
使用二級(jí)緩存:
- 配置好二級(jí)緩存之后,當(dāng)調(diào)用所以的select查詢方法時(shí),二級(jí)緩存就會(huì)開(kāi)始起作用了。注意的是,由于配置的是可讀可寫(xiě)緩存,而MyBatis使用SerializedCache序列化緩存來(lái)實(shí)現(xiàn)可讀可寫(xiě)緩存類,并通過(guò)序列化和反序列化來(lái)保證通過(guò)緩存獲取數(shù)據(jù)時(shí),得到的是一個(gè)新的實(shí)例.因此,如果配置只讀緩存,MyBatis就會(huì)使用Map來(lái)存儲(chǔ)緩存值,這種情況下,從緩存里得到的都是同一個(gè)對(duì)象。
- 因?yàn)槭褂每勺x寫(xiě)緩存,所以這個(gè)緩存類要求所有被序列化的對(duì)象必須實(shí)現(xiàn)Serializable接口。
測(cè)試代碼:
@Test
public void testL2Cache() {
SqlSession sqlSession = getSqlSession();
SysRole sysRole = null;
try {
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
sysRole = roleMapper.selectById(1l);
sysRole.setRoleName("new name");
SysRole sysRole1 = roleMapper.selectById(1l);
Assert.assertEquals("new name", sysRole1.getRoleName());
//一級(jí)緩存,兩個(gè)查詢相同的對(duì)象是相同的
Assert.assertEquals(sysRole, sysRole1);
} finally {
sqlSession.close();
}
System.out.println("開(kāi)始另一個(gè)查詢");
sqlSession = getSqlSession();
try {
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
SysRole sysRole1 = roleMapper.selectById(1l);
Assert.assertEquals("new name", sysRole1.getRoleName());
Assert.assertNotEquals(sysRole, sysRole1);
SysRole sysRole2 = roleMapper.selectById(1l);
// sysRole1 sysRole2是兩個(gè)反序列化得到的結(jié)果,是不相同的實(shí)例
Assert.assertNotEquals(sysRole1, sysRole2);
} finally {
sqlSession.close();
}
}
運(yùn)行結(jié)果截圖:

二級(jí)緩存
- 日志中存在好幾條以Cache Hit Ratio開(kāi)頭的語(yǔ)句,這行日志后面輸出的值為當(dāng)前執(zhí)行方法的緩存命中率。但是在這個(gè)例子中并沒(méi)有真正的讀寫(xiě)安全。因?yàn)闇y(cè)試中加入了一行不該有的代碼,sysRole.setRoleName("new name");按照常理應(yīng)該更新數(shù)據(jù),更新后會(huì)清空一級(jí)、二級(jí)緩存,這樣在第二部分的代碼中就不出出現(xiàn)查詢結(jié)果的roleName都是“new name”的結(jié)果。所以想要安全使用,就要避免出現(xiàn)毫無(wú)意義的修改。這樣就可以避免認(rèn)為的臟數(shù)據(jù),避免緩存和數(shù)據(jù)庫(kù)的數(shù)據(jù)不一致。