本文主要是个人学习mybatis缓存的学习笔记,主要有以下几个知识点

1.一级缓存配置信息

2.一级缓存源码学习笔记

3.二级缓存配置信息

4.二级缓存源码

5.一级缓存、二级缓存总结

1.一级缓存配置:

一级缓存是SqlSession级别的,同一个sqlSession执行多次相同的查询语句时,第二次会从缓存中获取数据,不查询数据库;一级缓存是无法关闭的,默认是SESSION级别

mybatis一级缓存默认是开启的,一级缓存有两个配置值,SESSION级别和STATEMENT级别,这两个级别的区别是:

如果是SESSION级别(默认值),那么在同一个sqlSession中,都会共享这个缓存

如果是STATEMENT级别,那么会把一级缓存中的缓存清空,也就是说,同一个sqlSession,多次请求同一个sql,每次都会查询数据库

默认是session级别,此时为开启二级缓存:

如果把一级缓存的级别设置为STATEMENT,那么同一个sqlSession执行多次相同的SQL,都会查询数据库:

那下面,我们来源码中验证

2.一级缓存源码:

1.首先会根据statement等参数,生成缓存中需要用到的key  org.apache.ibatis.executor.CachingExecutor#createCacheKey

2.根据key从一级缓存中获取值,一级缓存是PerpetualCache这个类

org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)

3.如果从一级缓存中取到值,就不查询数据库;否则查询数据库

    org.apache.ibatis.executor.BaseExecutor#handleLocallyCachedOutputParameters

  4.如果一级缓存中没有取到值,就从数据库中查询,并将数据库中查询到的数据,存到一级缓存中

    org.apache.ibatis.executor.BaseExecutor#queryFromDatabase

  5.判断缓存级别是否是STATEMENT,如果是,将一级缓存清空

 @SuppressWarnings("unchecked")
@Override
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;
}

   这段源码中,第14行,是尝试从一级缓存中根据key,取值;如果不为null,就从一级缓存中取值,第18行,是从数据中查询数据;

   在 queryFromDatabase()方法中,会将数据库中查询到的数据,put到一级缓存中

   源码中的第29行,就是对一级缓存是否是STATEMENT级别做判断,如果是,就clear;

  我们再来说,insert、update、delete,这三种操作,都会走update,

  org.apache.ibatis.executor.BaseExecutor#update

  

 @Override
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
clearLocalCache();
return doUpdate(ms, parameter);
}

可以看到,执行doUpdate()方法之前,会先clear一级缓存

3.二级缓存配置

二级缓存是mapper级别的,需要配置才会开启

需要在mybatis的配置文件中,增加以下配置

 <settings>
<setting name="cacheEnabled" value="true"/>
</settings>

并且在mapper.xml文件中增加

<cache></cache>

这样,二级缓存就开启了

4.二级缓存源码

二级缓存的源码,首先我们要看 org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)  这个方法

 @Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, parameterObject, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

在第7行,首先会判断,当前sql,是否开启useCache这个属性,对于select 默认是true;update、insert、delete默认是 false;这个点的源码是在解析xml的时候,最终会把每个select、update、insert、delete节点

build成一个mappedStatement,在build之前,有这么一段代码

 boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect);

org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode

这里的意思是:如果是select,那么useCache就是true,否则就是false

接着上面的说,第10行会尝试从二级缓存中获取数据,这里的调用链是这样的

从源码中,可以看到,二级缓存先于以及缓存,如果二级缓存中没有取到值,会充以及缓存中国取,一级缓存中没有取到,再从数据库查询数据

这个调用链这几个cache,还没有搞明白,具体的作用,后面学习之后,会更新;

二级缓存有一个点,需要明确:二级缓存只有在sqlSession执行commit的时候,才会更新;否则是不生效的

这个是commit的时候,二级缓存Put的调用链

5.总结

一级缓存是sqlSession级别的,二级缓存是mapper级别的,二级缓存的粒度更细,

一级缓存无法关闭,二级缓存可以开启或者关闭

一级缓存如果在分布式的场景下,可能会存在脏数据;

如果使用缓存,感觉还是Redis靠谱吧;

mybatis源码学习(三)-一级缓存二级缓存的更多相关文章

  1. mybatis源码学习:一级缓存和二级缓存分析

    目录 零.一级缓存和二级缓存的流程 一级缓存总结 二级缓存总结 一.缓存接口Cache及其实现类 二.cache标签解析源码 三.CacheKey缓存项的key 四.二级缓存TransactionCa ...

  2. mybatis源码学习:基于动态代理实现查询全过程

    前文传送门: mybatis源码学习:从SqlSessionFactory到代理对象的生成 mybatis源码学习:一级缓存和二级缓存分析 下面这条语句,将会调用代理对象的方法,并执行查询过程,我们一 ...

  3. mybatis源码学习:插件定义+执行流程责任链

    目录 一.自定义插件流程 二.测试插件 三.源码分析 1.inteceptor在Configuration中的注册 2.基于责任链的设计模式 3.基于动态代理的plugin 4.拦截方法的interc ...

  4. Mybatis源码学习第六天(核心流程分析)之Executor分析

    今Executor这个类,Mybatis虽然表面是SqlSession做的增删改查,其实底层统一调用的是Executor这个接口 在这里贴一下Mybatis查询体系结构图 Executor组件分析 E ...

  5. mybatis源码学习(一) 原生mybatis源码学习

    最近这一周,主要在学习mybatis相关的源码,所以记录一下吧,算是一点学习心得 个人觉得,mybatis的源码,大致可以分为两部分,一是原生的mybatis,二是和spring整合之后的mybati ...

  6. Mybatis源码解析(三) —— Mapper代理类的生成

    Mybatis源码解析(三) -- Mapper代理类的生成   在本系列第一篇文章已经讲述过在Mybatis-Spring项目中,是通过 MapperFactoryBean 的 getObject( ...

  7. 手把手带你阅读Mybatis源码(三)缓存篇

    前言 大家好,这一篇文章是MyBatis系列的最后一篇文章,前面两篇文章:手把手带你阅读Mybatis源码(一)构造篇 和 手把手带你阅读Mybatis源码(二)执行篇,主要说明了MyBatis是如何 ...

  8. Mybatis源码学习之整体架构(一)

    简述 关于ORM的定义,我们引用了一下百度百科给出的定义,总体来说ORM就是提供给开发人员API,方便操作关系型数据库的,封装了对数据库操作的过程,同时提供对象与数据之间的映射功能,解放了开发人员对访 ...

  9. Mybatis源码学习第八天(总结)

    源码学习到这里就要结束了; 来总结一下吧 Mybatis的总体架构 这次源码学习我们,学习了重点的模块,在这里我想说一句,源码的学习不是要所有的都学,一行一行的去学,这是错误的,我们只需要学习核心,专 ...

随机推荐

  1. 来玩一局CS吗?UE4射击游戏的独立服务器构建

    前言   根据UE4官方文档的介绍,UE4引擎在架构时就已经考虑到了多人游戏的情景,多人游戏基于客户端-服务器模式(CS模式).也就是说,会有一个服务器担当游戏状态的主控者,而连接的客户端将保持近似的 ...

  2. (Java) JWT-TokenUtils

    package com.vcgeek.hephaestus.utils; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; imp ...

  3. UVA12433 【Rent a Car】

    这题应该算是比较难的一道网络流的题,(但却在我校OJ考试上出现了),但是大家只要能理解此图的建边方式就行. 假设有5天的租车需求,虚拟出2*n+2 即 12个节点,0为源点,12为汇点. 1,源点到1 ...

  4. Python的闭包以及迭代器

    一,闭包 什么是闭包呢?闭包就是内层函数,对外层函数(非外层)的变量的引用,叫做闭包 def mz(): name = 'YJ' def xue(): print(name) #闭包 xue() mz ...

  5. CSPS模拟 71

    全程傻眼 T1 毛衣衬 meet_in_middle.. 不再使用二分查找,而是直接枚举对面状态,虽然底数爆炸但是指数减半,复杂度是对的. T2 猫儿嗔 逆序关系有支配关系? $DAG$树.. 把逆序 ...

  6. 8.3 NOIP CE反思

    lsc考完以后就CE了,然后滚回去吃*去了! 这次考试都比的一批,整个先是打了暴力然后对拍发现桶有可能炸内存,然后就打了一个hash-map然后......T1 T3全使用了它,结果: 没什么可说的了 ...

  7. 字符串模拟大数相加——Java实现

    本题是CVTE二面编程题,首先考虑返回值肯定是一个字符串(int会有溢出可能),并且两个字符串只含数字,不含”+“.”-“这种. 代码如下: public static String add(Stri ...

  8. 护网杯web

    首先进入网页后,观察到有sign up 点击sign up 进行注册 再点击sign in 进行登录 进入一个买辣条的界面,可以知道,5元可以买一包大辣条,多包大辣条可以换一包辣条之王,多包辣条之王可 ...

  9. .NET Core 对龙芯的支持情况和对 .NET Core 开发嵌入式的思考

    目录 .NET Core 对龙芯的支持情况和对 .NET Core 开发嵌入式的思考 一,遗憾的尝试 二,.NET Core在嵌入式下的几点不足 三,.NET Core 龙芯移植的进展和资料 .NET ...

  10. Resource Path Location Type Target runtime Apache Tomcat v6.0 is not defined(项目报错)已解决

    我换了开发工具后,导入的项目不是这里报错就是那里不错.不过,我喜欢.在tomcat里面部署项目后,定位到报错行时,总是提示我这句话:Description Resource Path Location ...