(4)一起来看下mybatis框架的缓存原理吧
本文是作者原创,版权归作者所有.若要转载,请注明出处.本文只贴我觉得比较重要的源码,其他不重要非关键的就不贴了
我们知道.使用缓存可以更快的获取数据,避免频繁直接查询数据库,节省资源.
MyBatis缓存有一级缓存和二级缓存.
1.一级缓存也叫本地缓存,默认开启,在一个sqlsession内有效.当在同一个sqlSession里面发出同样的sql查询请求,Mybatis会直接从缓存中查找。如果没有则从数据库查找
下面我们贴一下一级缓存的测试结果
String resource = "mybatis.xml";
//读取配置文件,生成读取流
InputStream inputStream = Resources.getResourceAsStream(resource);
//返回的DefaultSqlSessionFactory是SqlSessionFactory接口的实现类,
//这个类只有一个属性,就是Configuration对象,Configuration对象用来存放读取xml配置的信息
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//SqlSession是与数据库打交道的对象 SqlSession对象中有上面传来的Configuration对象,
//SqlSession对象还有executor处理器对象,executor处理器有一个dirty属性,默认为false
//返回的DefaultSqlSession是SqlSession接口的实现类
SqlSession sqlSession = sqlSessionFactory.openSession();
//SqlSession sqlSession2 = sqlSessionFactory.openSession();
//通过动态代理实现接口 ,用动态代理对象去帮我们执行SQL
//这里生成mapper实际类型是org.apache.ibatis.binding.MapperProxy
DemoMapper mapper = sqlSession.getMapper(DemoMapper.class);
DemoMapper mapper2 = sqlSession.getMapper(DemoMapper.class);
//这里用生成的动态代理对象执行
String projId="0124569b738e405fb20b68bfef37f487";
String sectionName="标段";
List<ProjInfo> projInfos = mapper.selectAll(projId, sectionName);
List<ProjInfo> projInfos2 = mapper2.selectAll(projId, sectionName);
System.out.println(projInfos.hashCode());//这里和下面那条的查询结果的hashcode是一样的
System.out.println(projInfos2.hashCode());//
sqlSession.close(); sqlSession = sqlSessionFactory.openSession();
DemoMapper mapper5 = sqlSession.getMapper(DemoMapper.class);
List<ProjInfo> projInfos5 = mapper5.selectAll(projId, sectionName);
System.out.println(projInfos5.hashCode());//这里和上面两条的查询结果的hashcode是不一样的

好,我们可以看到,上面两条的查询结果的hashcode一样,第三条不一样,一级缓存生效了
2.二级缓存是mapper级别的,就是说二级缓存是以Mapper配置文件的namespace为单位创建的。
二级缓存默认是不开启的,需要手动开启二级缓存,如下需要在mybatis配置文件中的settting标签里面加入开启
<settings>
<!-- 开启二级缓存。value值填true -->
<setting name="cacheEnabled" value="true"/>
<!-- 配置默认的执行器。SIMPLE 就是普通的执行器;
REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。默认SIMPLE -->
<setting name="defaultExecutorType" value="SIMPLE"/>
</settings>
实现二级缓存的时候,MyBatis要求返回的对象必须是可序列化的,如图

还要在mapper,xml文件中添加cache标签,标签里的readOnly属性需填true 如下:
<cache readOnly="true" ></cache>
<select id="selectAll" resultType="com.lusaisai.po.ProjInfo">
SELECT id AS sectionId,section_name AS sectionName,proj_id AS projId
FROM b_proj_section_info
WHERE proj_id=#{projId} AND section_name LIKE CONCAT('%',#{sectionName},'%')
</select>
我们再来看下二级缓存的运行结果

我们可以看到二级缓存也生效了.
下面来看看缓存的源码,大家猜底层是使用什么实现的,前面的流程就不一一看了,主要贴一下缓存的代码,
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
//这个跟进去看戏,应该是真正的jdbc操作了
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
我们可以看到,上面的生成了一个缓存的key,具体怎么实现的就先不看了,看下key这个对象有什么属性吧

我们可以看到,key对象有这条sql语句除了结果之外的所有信息,还有hashcode等等,我们继续跟进去
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//MappedStatement的作用域是全局共享的,这里的cache是接口,有多个实现类
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
//前面是缓存处理跟进去
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
我们看下Cache 的实现类

有很多,这里就不深究了,继续往下看,我们看下list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql)这行代码,这是处理一级缓存的方法,点进去看下
这里用到我们前面生成的key了
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//进这里,继续跟
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
我们关注下list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;这行代码,点进去看下
@Override
public Object getObject(Object key) {
return cache.get(key);
}
我截个图,看下上图的PerpetualCache对象底层是怎么实现的

好,很明显了,这里一级缓存是用的hashmap实现的.
下面我们看下二级缓存的代码,上面的List<E> list = (List<E>) tcm.getObject(cache, key);这行代码跟进去
public Object getObject(Cache cache, CacheKey key) {
return getTransactionalCache(cache).getObject(key);
}
继续跟
@Override
public Object getObject(Object key) {
// issue #116
Object object = delegate.getObject(key);
if (object == null) {
entriesMissedInCache.add(key);
}
// issue #146
if (clearOnCommit) {
return null;
} else {
return object;
}
}
截个图看下delegate对象的属性,很多层

我们可以看到所有数据都在这里了.网上找的图

再来一段缓存的关键代码,这里就是包装了一层层对象的代码
private Cache setStandardDecorators(Cache cache) {
try {
MetaObject metaCache = SystemMetaObject.forObject(cache);
if (size != null && metaCache.hasSetter("size")) {
metaCache.setValue("size", size);
}
if (clearInterval != null) {
cache = new ScheduledCache(cache);
((ScheduledCache) cache).setClearInterval(clearInterval);
}
if (readWrite) {
cache = new SerializedCache(cache);
}
cache = new LoggingCache(cache);
cache = new SynchronizedCache(cache);
if (blocking) {
cache = new BlockingCache(cache);
}
return cache;
} catch (Exception e) {
throw new CacheException("Error building standard cache decorators. Cause: " + e, e);
}
}
好,再来张图总结一下

最后,如果我们和spring整合,那么此时mybatis一级缓存就会失效,因为sqlsession交给spring管理,会自动关闭session.
关于如何和spring整合就后面再来讲吧,下面一段时间,我会先研究一下spring源码,spring专题见啦
(4)一起来看下mybatis框架的缓存原理吧的更多相关文章
- MyBatis:二级缓存原理分析
MyBatis从入门到放弃七:二级缓存原理分析 前言 说起mybatis的一级缓存和二级缓存我特意问了几个身边的朋友他们平时会不会用,结果没有一个人平时业务场景中用. 好吧,那我暂且用来学习源码吧.一 ...
- mybatis两级缓存原理剖析
https://blog.csdn.net/zhurhyme/article/details/81064108 对于mybatis的缓存认识一直有一个误区,所以今天写一篇文章帮自己订正一下.mybat ...
- 手写mybatis框架-增加缓存&事务功能
前言 在学习mybatis源码之余,自己完成了一个简单的ORM框架.已完成基本SQL的执行和对象关系映射.本周在此基础上,又加入了缓存和事务功能.所有代码都没有copy,如果也对此感兴趣,请赏个Sta ...
- CodeIgniter框架的缓存原理分解
用缓存的目的:(手册上叙述如下,已经写得很清楚了) Codeigniter 支持缓存技术,以达到最快的速度. 尽管CI已经相当高效了,但是网页中的动态内容.主机的内存CPU 和数据库读取速度等因素直接 ...
- Spring Boot + Mybatis + Redis二级缓存开发指南
Spring Boot + Mybatis + Redis二级缓存开发指南 背景 Spring-Boot因其提供了各种开箱即用的插件,使得它成为了当今最为主流的Java Web开发框架之一.Mybat ...
- Mybatis拦截器实现原理深度分析
1.拦截器简介 拦截器可以说使我们平时开发经常用到的技术了,Spring AOP.Mybatis自定义插件原理都是基于拦截器实现的,而拦截器又是以动态代理为基础实现的,每个框架对拦截器的实现不完全相同 ...
- mybatis源码专题(2)--------一起来看下使用mybatis框架的insert语句的源码执行流程吧
本文是作者原创,版权归作者所有.若要转载,请注明出处.本文以简单的insert语句为例 1.mybatis的底层是jdbc操作,我们先来回顾一下insert语句的执行流程,如下 执行完后,我们看下数据 ...
- SpringBoot 下 mybatis 的缓存
背景: 说起 mybatis,作为 Java 程序员应该是无人不知,它是常用的数据库访问框架.与 Spring 和 Struts 组成了 Java Web 开发的三剑客--- SSM.当然随着 Spr ...
- 【Mybatis框架】查询缓存(一级缓存)
做Java的各位程序员们,估计SSH和SSM是我们的基础必备框架.也就是说我们都已经至少接触过了这两套常见的集成框架.当我们用SSH的时候,相信很多人都接触过hibernate的两级缓存,同样,相对应 ...
随机推荐
- NOIP2003[提高组] 加分二叉树 题解
题意 给出一个有n个节点的二叉树的中序遍历,以当前节点为根的树的分数等于左节点分数* 右节点分数+根节点分数,叶子节点的分数等于它本身,求最大分数,以及分数最大的树的先序遍历 一道区间dp题,因为要求 ...
- bzoj5072 小A的树 题解
题意 给出一棵 n 个点的树,每个点有黑白两种颜色.q 次询问,每次 询问给出 x 和 y,问能否选出一个 x 个点的联通子图,使得其中 黑点数目为 y. 范围 n ≤ 5000,q ≤ 10^5 其 ...
- Java静态代码块、代码块及构造函数执行顺序
根据以下程序进行分析 定义一个父类 package sas.LearnJava; public class ExcuteOrderTest { { System.out.println("我 ...
- TypeScript中是使用强类型函数作为参数
class Foo { save(callback: (n: number) => any) : void { callback(42) } multipleCallbacks(firstCal ...
- Spinner列表选择框
Spinner首先它是一个弹出式的列表选择框,由于间接继承了ViewGroup,所以它可以当做一个容器使用; 如果我们可以明确下拉列表中的列表项, 则可以不需要编写代码, 只需要为spinner指定a ...
- Windows7版本了解
1.win7简介Windows7是微软软件公司研发的标准版本,也就是所说的正版安装版或原版.安装版安装用时较长,安装步骤比较复杂,但是系统运行稳定.正版系统由微软刻成光盘出售,买品牌机,或一般大一点的 ...
- 来几道水题 d050: 妳那裡現在幾點了?
减去15即可(注意这个数小于15的情况) 题目:珊珊到了美国犹他州的杨百翰大学之后,文文禁不住对她的思念,常常想打电话给她,却又担心在美国的她是不是在睡觉.好不容易鼓起勇气打通了电话,第一句就先问:「 ...
- Kubernetes v1.16 发布 | 云原生生态周报 Vol. 20
作者:心贵.进超.元毅.心水.衷源.洗兵 业界要闻 Kubernetes v1.16 发布 在这次发布中值得关注的一些特性和 Feature: CRD 正式进入 GA 阶段: Admission We ...
- 使用Bookinfo应用测试Kuma服务网格
最近,开源API管理平台Kong服务供应商近日放出了新的开源项目Kuma.本文尝试将 bookinfo 应用部署在 Kuma 网格中,以便帮助大家更好的理解 Kuma 项目. Kuma是能用于管理 ...
- IDEA基础设置
1.防止自动更新 去掉 2.文件的隐藏 3.字体的修改: 4.字体格式 5.快捷键设置 --写出方法名,用Ctrl+Shift+Enter键补全. --移动当前行代码,用Ctrl+Shift+上.下. ...