為什么不推薦使用 MyBatis 二級緩存?
閱讀本文大概需要 6?分鐘。
來自:blog.csdn.net/xujingyiss/article/details/123481116
一級緩存
應(yīng)用場景
member_id字段查詢出會員表,最后進行數(shù)據(jù)整合。而如果訂單表中存在重復(fù)的member_id,就會出現(xiàn)很多重復(fù)查詢。生效的條件
必須是相同的會話 必須是同一個 mapper,即同一個 namespace 必須是相同的 statement,即同一個 mapper 中的同一個方法 必須是相同的 sql 和參數(shù) 查詢語句中間沒有執(zhí)行? session.clearCache()?方法查詢語句中間沒有執(zhí)行 insert/update/delete 方法(無論變動記錄是否與緩存數(shù)據(jù)有無關(guān)系)
與springboot集成時一級緩存不生效原因

SqlSessionUtils?的?getSqlSession?方法,在這個方法中會嘗試在事務(wù)管理器中獲取 SqlSession,如果沒有開啟事務(wù),那么就會 new 一個?DefaultSqlSession。
解決與springboot集成時一級緩存不生效問題
SqlSession,取不到才會去創(chuàng)建新的?SqlSession。所以可以猜測只要將方法開啟事務(wù),那么一級緩存就會生效。@Transactional?注解,看下效果:

SqlSessionUtils?中,在獲取到?SqlSession?后,會調(diào)用?registerSessionHolder方法注冊?SessionHolder?到事務(wù)管理器:
TransactionSynchronizationManager?的?bindResource?方法中操作的,將?SessionHolder?保存到線程本地變量(ThreadLocal) resources?中,這是每個線程獨享的。
BaseExecutor?中的?queryFromDatabase?方法中。執(zhí)行 doQuery 從數(shù)據(jù)庫中查詢數(shù)據(jù)后,會立馬緩存到?localCache(PerpetualCache類型)?中:
二級緩存
應(yīng)用場景
@RestController
@RequestMapping("item")
public?class?ItemController?{
?
????@Autowired
????private?ItemMapper?itemMapper;
?
????@GetMapping("/{id}")
????public?void?getById(@PathVariable("id")?Long?id)?{
????????System.out.println("====================?begin?====================");
????????Item?item?=?itemMapper.selectById(id);
????????System.out.println(JSON.toJSONString(item));
????}
?
}?

開啟的方法
cache-enabled?為 truemybatis-plus:
??configuration:
????cache-enabled:?true
@CacheNamespace?注解
Serializable?接口
生效的條件
當(dāng)會話提交或關(guān)閉之后才會填充二級緩存 必須是同一個 mapper,即同一個命名空間 必須是相同的 statement,即同一個 mapper 中的同一個方法 必須是相同的 SQL 語句和參數(shù) 如果? readWrite=true(默認(rèn)就是true),實體對像必須實現(xiàn)?Serializable?接口
緩存清除條件
只有修改會話提交之后,才會執(zhí)行清空操作 xml 中配置的 update 不能清空? @CacheNamespace?中的緩存數(shù)據(jù)任何一種增刪改操作都會清空整個? namespace?中的緩存
源碼中是如何填充二級緩存的?

TransactionalCache?的?flushPendingEntries?方法中填充二級緩存:
查詢時如何使用二級緩存?
MybatisCachingExecutor?的 query 方法,里面會從?TransactionalCacheManager?中嘗試根據(jù) key 獲取二級緩存的內(nèi)容。
PerpetualCache?中獲取緩存的:
LoggingCache?中:
為什么mybatis默認(rèn)不開啟二級緩存?
namespace(mapper)?為單位的,不同 namespace 下的操作互不影響。且 insert/update/delete 操作會清空所在?namespace?下的全部緩存。ItemMapper?以及?XxxMapper,在?XxxMapper?中做了表關(guān)聯(lián)查詢,且做了二級緩存。此時在?ItemMapper?中將 item 信息給刪了,由于不同 namespace 下的操作互不影響,XxxMapper?的二級緩存不會變,那之后再次通過?XxxMapper?查詢的數(shù)據(jù)就不對了,非常危險。@Mapper
@Repository
@CacheNamespace
public?interface?XxxMapper?{
?
????@Select("select?i.id?itemId,i.name?itemName,p.amount,p.unit_price?unitPrice?"?+
????????????"from?item?i?JOIN?payment?p?on?i.id?=?p.item_id?where?i.id?=?#{id}")
????List?getPaymentVO(Long?id) ;
?
}
?
?
@Autowired
private?XxxMapper?xxxMapper;
?
@Test
void?test()?{
?System.out.println("====================?查詢PaymentVO?====================");
?List?voList?=?xxxMapper.getPaymentVO(1L);
?System.out.println(JSON.toJSONString(voList.get(0)));
?System.out.println("====================??更新item表的name?====================?");
?Item?item?=?itemMapper.selectById(1);
?item.setName("java并發(fā)編程");
?itemMapper.updateById(item);
?System.out.println("====================??重新查詢PaymentVO?====================?");
?List?voList2?=?xxxMapper.getPaymentVO(1L);
?System.out.println(JSON.toJSONString(voList2.get(0)));
}
test()方法中前后兩次調(diào)用了?xxxMapper.getPaymentVO?方法,因為沒有加?@Transactional?注解,所以前后兩次查詢,是兩個不同的會話,第一次查詢完后,SqlSession?會自動 commit,所以二級緩存能夠生效;itemMapper?與?xxxMapper?不是同一個命名空間,所以?itemMapper?執(zhí)行的更新操作不會影響到?xxxMapper?的二級緩存;xxxMapper.getPaymentVO,發(fā)現(xiàn)取出的值是走緩存的,itemName?還是老的。但實際上?itemName?在上面已經(jīng)被改了!
推薦閱讀:
互聯(lián)網(wǎng)初中高級大廠面試題(9個G) 內(nèi)容包含Java基礎(chǔ)、JavaWeb、MySQL性能優(yōu)化、JVM、鎖、百萬并發(fā)、消息隊列、高性能緩存、反射、Spring全家桶原理、微服務(wù)、Zookeeper......等技術(shù)棧!
?戳閱讀原文領(lǐng)取!? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??朕已閱?

