本文主要是个人学习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. 一个普通类就能干趴你的springboot,你信吗?

    先声明本人并不是标题党,如果看了本篇文章并且认为没有得到任何收获,请您随便留言骂我,本人绝不还口,已经对springboot了如指掌大大神,求放过! 不BB了,直接上代码,请各位在自己的springb ...

  2. Docker安装方法整理

    目录 安装准备 在线安装 离线安装 Raspbian便捷脚本安装 卸载 安装准备: 卸载旧版本 较旧版本的Docker被称为docker或docker-engine.如果已安装,请卸载它们: sudo ...

  3. Java基础01-集合1、泛型

    集合.泛型 第一章:集合1 1. 什么是集合 定义:在Java中,集合是一种可以存储多个数据的容器. 代码: ArrayList<String> list = new ArrayList& ...

  4. Java编程思想——第14章 类型信息(一)

    运行时类型信息使得你可以在程序运行时发现和使用类型信息.Java是如何让我们在运行时识别对象和类的信息得呢? 主要有两种方式:1.传统RTTI,他假定我们在编译期间已经知道了所有类型:2.反射,它允许 ...

  5. scrapy爬取京东iPhone11评论(一)

    咨询行业中经常接触到文本类信息,无论是分词做词云图,还是整理编码分析用,都非常具有价值. 本文将记录使用scrapy框架爬取京东IPhone11评论的过程,由于一边学习一边实践,更新稍慢请见谅. 1. ...

  6. C/C.cpp

    我是怎么说服自己这题没有答案单调性的? 当轮数大于最优答案时,B的操作就成了捣乱,会使A多进行若干操作才能回复原状态 嗯,所以答案没有单调性. 就没去想A的操作可以把B的抵消啊.. 另外swap没有交 ...

  7. 测试工程师,选择python还是java?

    问:“你平时工作中,用java多还是用python多”? 答:“都还可以,根据具体的场景选择不同的语言”. 问:“比如说呢”? 答:“开发自己的测试平台,肯定会选择java:在centos服务器跑一些 ...

  8. 大数据之路week01--自学之集合_2(List)

    在学习过了Collection之后,接下来我们将去学习List, 先看API文档: List集合的特有功能:(没有列出Collection也有的功能) A:添加功能  add(int index, E ...

  9. git下载安装

    git是目前最流行的分布式版本控制系统,使用它可以很方便的对项目进行管理备份. 1.git下载 登录git官网https://git-scm.com/,点击downloads即可下载安装包 安装包如下 ...

  10. CSS尺寸样式属性

    尺寸样式属性介绍 属性 值 含义 height auto:自动,浏览器会自动计算高度length:使用px定义高度%:基于包含它的块级对象的百分比高度 设置元素高度 width 同上 设置元素的宽度 ...