MyBatis一级缓存源码解析
本文将探讨一级缓存命中场景以,一级缓存源码实现
MyBatis缓存概述
myBatis中存在两个缓存,一级缓存和二级缓存。
-
一级缓存:也叫做会话级缓存,生命周期仅存在于当前会话,不可以直接关关闭。但可以通过flushCache和localCacheScope对其做相应控制。
-
二级缓存:也叫应用级性缓存,缓存对象存在于整个应用周期,而且可以跨线程使用。
关于二级缓存将在后续章节,详细说明。文本先聚焦一级缓存。首先来看如何才能命中一级缓存。
一级缓存的命中场景
关于一级缓存的命中可大致分为两个场景,满足特定命中参数,第二不触发清空方法。
缓存命中参数:
-
SQL与参数相同:
-
同一个会话:
-
相同的MapperStatement ID:
-
RowBounds行范围相同:
触发清空缓存
-
手动调用clearCache
-
执行提交回滚
-
执行update
-
配置flushCache=true
-
缓存作用域为Statement
一级缓存源码解析
回顾上节课内容,MyBatis执行过程如下图:
本文所要论述的一级缓存逻辑就存在于 BaseExecutor (基础执行器)里面。当会话接收到查询请求之后,会交给执行器的Query方法,在这里会通过 Sql、参数、分页条件等参数创建一个缓存key,在基于这个key去 PerpetualCache中查找对应的缓存值,如果有主直接返回。没有就会查询数据库,然后在填充缓存。
另外通过上图你也看了,最终缓存的实现非常简单,就是一个HashMap。
一级缓存的清空
缓存的清空对应BaseExecutor中的 clearLocalCache.方法。只要找到调用该方法地方,就知道哪些场景中会清空缓存了。
-
update: 执行任意增删改
-
select:查询又分为两种情况清空,一前置清空,即配置了flushCache=true。2后置清空,配置了缓存作用域为statement 查询结束合会清空缓存。
-
commit:提交前清空
-
Rolback:回滚前清空
注意:clearLocalCache 不是清空某条具体数据,而清当前会话下所有一级缓存数据。
MyBatis集成Spring后一级缓存失效的问题?
很多人发现,集成一级缓存后会话失效了,以为是spring Bug ,真正原因是Spring 对SqlSession进行了封装,通过SqlSessionTemplae ,使得每次调用Sql,都会重新构建一个SqlSession,具体参见SqlSessionInterceptor。而根据前面所学,一级缓存必须是同一会话才能命中,所以在这些场景当中不能命中。
怎么解决呢?给Spring 添加事物 即可。添加事物之后,SqlSessionInterceptor(会话拦截器)就会去判断两次请求是否在同一事物当中,如果是就会共用同一个SqlSession会话来解决。