MyBatis 学习记录4 MyBatis的一级缓存
主题
分享记录一下MyBatis的一级缓存相关的学习.
Demo
public static void firstLevelCache() {
init("mybatis-config.xml");
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User u = mapper.selectByPrimaryKey(1);
System.out.println(u);
User u2 = mapper.selectByPrimaryKey(1);
System.out.println(u2);
session.close();
System.out.println("==============session2==============");
SqlSession session2 = sqlSessionFactory.openSession();
UserMapper mapper2 = session2.getMapper(UserMapper.class);
User u3 = mapper2.selectByPrimaryKey(1);
System.out.println(u3);
session2.close();
}
从1个Demo来学习.
上面这段代码首先通过session获取了mapper然后执行了2次同样的查询,查询用户ID为1的数据, 然后打印=======session2======,然后新开了一个session2. 然后获取了第二个mapper并执行了相同的查询.
输出结果:
2018-09-29 19:06:26,813 DEBUG [main] logging.LogFactory : Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
2018-09-29 19:06:26,961 DEBUG [main] pooled.PooledDataSource : PooledDataSource forcefully closed/removed all connections.
2018-09-29 19:06:26,962 DEBUG [main] pooled.PooledDataSource : PooledDataSource forcefully closed/removed all connections.
2018-09-29 19:06:26,962 DEBUG [main] pooled.PooledDataSource : PooledDataSource forcefully closed/removed all connections.
2018-09-29 19:06:26,962 DEBUG [main] pooled.PooledDataSource : PooledDataSource forcefully closed/removed all connections.
2018-09-29 19:06:27,102 DEBUG [main] jdbc.JdbcTransaction : Opening JDBC Connection
Sat Sep 29 19:06:27 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
2018-09-29 19:06:28,095 DEBUG [main] pooled.PooledDataSource : Created connection 1682463303.
2018-09-29 19:06:28,095 DEBUG [main] jdbc.JdbcTransaction : Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@64485a47]
2018-09-29 19:06:28,108 DEBUG [main] UserMapper.selectByPrimaryKey : ==> Preparing: select id, user_id, user_name, real_name, email, creator_uid, modifier_uid, created_at, updated_at, del from user where id = ?
2018-09-29 19:06:28,145 DEBUG [main] UserMapper.selectByPrimaryKey : ==> Parameters: 1(Integer)
2018-09-29 19:06:28,172 DEBUG [main] UserMapper.selectByPrimaryKey : <== Total: 1
User{id=1, userId=1, userName='test', realName='realName', email='jyzjyz12@163.com', creatorUid=1, modifierUid=1, createdAt=Mon Sep 24 10:10:43 CST 2018, updatedAt=Mon Sep 24 10:10:46 CST 2018, del=false}
User{id=1, userId=1, userName='test', realName='realName', email='jyzjyz12@163.com', creatorUid=1, modifierUid=1, createdAt=Mon Sep 24 10:10:43 CST 2018, updatedAt=Mon Sep 24 10:10:46 CST 2018, del=false}
2018-09-29 19:06:28,172 DEBUG [main] jdbc.JdbcTransaction : Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@64485a47]
2018-09-29 19:06:28,183 DEBUG [main] jdbc.JdbcTransaction : Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@64485a47]
2018-09-29 19:06:28,183 DEBUG [main] pooled.PooledDataSource : Returned connection 1682463303 to pool.
==============session2==============
2018-09-29 19:06:28,183 DEBUG [main] jdbc.JdbcTransaction : Opening JDBC Connection
2018-09-29 19:06:28,183 DEBUG [main] pooled.PooledDataSource : Checked out connection 1682463303 from pool.
2018-09-29 19:06:28,183 DEBUG [main] jdbc.JdbcTransaction : Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@64485a47]
2018-09-29 19:06:28,193 DEBUG [main] UserMapper.selectByPrimaryKey : ==> Preparing: select id, user_id, user_name, real_name, email, creator_uid, modifier_uid, created_at, updated_at, del from user where id = ?
2018-09-29 19:06:28,194 DEBUG [main] UserMapper.selectByPrimaryKey : ==> Parameters: 1(Integer)
2018-09-29 19:06:28,210 DEBUG [main] UserMapper.selectByPrimaryKey : <== Total: 1
User{id=1, userId=1, userName='test', realName='realName', email='jyzjyz12@163.com', creatorUid=1, modifierUid=1, createdAt=Mon Sep 24 10:10:43 CST 2018, updatedAt=Mon Sep 24 10:10:46 CST 2018, del=false}
2018-09-29 19:06:28,210 DEBUG [main] jdbc.JdbcTransaction : Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@64485a47]
2018-09-29 19:06:28,221 DEBUG [main] jdbc.JdbcTransaction : Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@64485a47]
2018-09-29 19:06:28,221 DEBUG [main] pooled.PooledDataSource : Returned connection 1682463303 to pool.
从输出中我们可以看到,红色部分是执行的SQL,蓝色部分是查询到用户信息并打印的数据...
总共执行了3次输出打印,但是SQL查询命令只运行了2次.其中第1个session的第1次查询和第2个session的第1次查询的时候会触发SQL的执行.而第1个session的第二次查询没有处罚SQL执行
从中我们可以得到结论:
一级缓存是发生在同一个SqlSession中的, 1个sqlSession 多次执行相同的SQL查询会有缓存
如何缓存
然后我们来学习下MyBatis是如何设计二级缓存的.
总体来说我觉得最核心的就是BaseExecutor这个类

看到BaseExecutor的成员域中有个wrapper字段,类型是Executor类,大家就应该能明白,Executor是一种装饰者的设计模式.这种模式在mybatis里用的真的挺多的.
我们不管是自定义Mapper还是使用SqlSession去执行SQL,最终都是委托Executor来执行的.


如上图,我们的selectByPrimaryKey方法会调用SqlSession的selectOne然后再转到selectList,最后委托给executor来执行.
而默认的情况下,我们在从SqlSessionFactory里拿到的SqlSession的时候new的是一个SimpleExecutor外层包裹着CachingExecutor.
其中CachingExecutor涉及二级缓存,而BaseExecutor(SimpleExecutor的父类)主要涉及一级缓存.
从之前的BaseExecutor的结构图中我们也可以发现有1个叫做localCache的字段.这个字段类型是PerpetualCache

PerpetualCache可以认为就是hashmap的简单封装.
@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;
}
当执行BaseExecutor的query方法的时候,如上代码所示,会先在L14那行,试着从localCache中获取,如果没有就L18,queryFromDatabase.否则就会返回localCache.getObject(key)的结果.代码还是比较清楚明了的
那么这里就涉及到一个问题.怎么样才算缓存命中? 或者换句话说CacheKey怎么计算? 因为相同的cacheKey会取到相同的缓存结果.
-1233329154:3360697231:test.mapper.UserMapper.selectByPrimaryKey:0:2147483647:select
id, user_id, user_name, real_name, email, creator_uid, modifier_uid, created_at,
updated_at, del
from user
where id = ?:1:development
上面这段字符串就是我debug中截取的一个CacheKey的字符串形式. CacheKey的equals和toString依赖的成员域差不多.从toString的结果上来看可能更直观一点.

首先需要说明一下,CacheKey有一个doUpdate方法,这个方法允许你加入一些需要计算缓存key的成分.也就是说加入的对象会影响key的结果.
然后我们来分析一下那个字符串,其中有很多个组成部分

-1233329154为CacheKey本身的hashcode
3360697231为doUpdate里添加过的对象的hashcode和
后面的各个部分是doUpdate中的添加过的对象的各自的toString.那么可以添加哪些东西呢?
从字符串中其实也可以看出来.
类.方法名 + 分页初始位置(默认为0) + 分页终止位置(默认为Integer.MAX_VALUE) + SQL + 调用Mapper方法的参数 + 环境名(configuration里配置的,默认是development)
从代码里也可以看出就是这么个逻辑: 下图为BaseExecutor实现Executor接口生成cacheKey的方法

另外cacheKey的equals只是比toString多了一个count对象的校验,也就是doUpdate添加过了几个对象的校验...其他部分基本是一致的.
所以如果2次查询CacheKey的hashcode和equals一致,那么就会使用之前缓存的结果.
小结
MyBatis一级缓存主要是在BaseExecutor中实现的,CacheKey涉及到很多组成部分(hashcode+各部分hashcode+类.方法名+分页界限+SQL+参数+环境等), 从直观上的感受来说.调用同一个Mapper方法,如果参数一致,那就会被取到缓存结果.
MyBatis 学习记录4 MyBatis的一级缓存的更多相关文章
- MyBatis 学习记录5 MyBatis的二级缓存
主题 之前学习了一下MyBatis的一级缓存,主要涉及到BaseExecutor这个类. 现在准备学习记录下MyBatis二级缓存. 配置二级缓存与初始化发生的事情 首先二级缓存默认是不开启的,需要自 ...
- myBatis学习(9):一级缓存和二级缓存
正如大多数持久层框架一样,MyBatis同样提供了一级缓存和二级缓存的支持 1. MyBatis一级缓存基于PerpetualCache的HashMap本地缓存,其存储作用域为 Session,默认情 ...
- mybatis学习记录二——mybatis开发dao的方法
4.1 SqlSession使用范围 4.1.1 SqlSessionFactoryBuilder 通过SqlSessionFactoryBuilder创建会话工厂SqlSession ...
- MyBatis学习总结(七)——Mybatis缓存(转载)
孤傲苍狼 只为成功找方法,不为失败找借口! MyBatis学习总结(七)--Mybatis缓存 一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的 ...
- 【转】MyBatis学习总结(七)——Mybatis缓存
[转]MyBatis学习总结(七)——Mybatis缓存 一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持 一级缓存: 基于PerpetualC ...
- 【转】MyBatis学习总结(一)——MyBatis快速入门
[转]MyBatis学习总结(一)——MyBatis快速入门 一.Mybatis介绍 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC ...
- 转:MyBatis学习总结(Mybatis总结精华文章)
http://www.cnblogs.com/xdp-gacl/tag/MyBatis%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93/ 当前标签: MyBatis学习总结 ...
- Mybatis学习(五)————— 延迟加载和缓存机制(一级二级缓存)
一.延迟加载 延迟加载就是懒加载,先去查询主表信息,如果用到从表的数据的话,再去查询从表的信息,也就是如果没用到从表的数据的话,就不查询从表的信息.所以这就是突出了懒这个特点.真是懒啊. Mybati ...
- Mybatis学习记录(七)----Mybatis查询缓存
1. 什么是查询缓存 mybatis提供查询缓存,用于减轻数据压力,提高数据库性能. mybaits提供一级缓存,和二级缓存. 一级缓存是SqlSession级别的缓存.在操作数据库时需要构造 sql ...
随机推荐
- eclipse如何通过git把项目上传到码云上
转载:原文链接:https://www.cnblogs.com/yixtx/p/8310311.html 1.eclipse安装git插件 具体我也做过,因为我下载的eclipse版本以及由git插件 ...
- 将 WPF、UWP 以及其他各种类型的旧样式的 csproj 文件迁移成新样式的 csproj 文件
写过 .NET Standard 类库或者 .NET Core 程序的你一定非常喜欢微软为他们新开发的项目文件(对于 C#,则是 csproj 文件).这种文件非常简洁,组织一个庞大的项目也只需要聊聊 ...
- C#/.NET 中的契约
将文档放到代码里面,文档才会及时地更新! 微软从 .NET Framework 4.0 开始,增加了 System.Diagnostics.Contracts 命名空间,用来把契约文档融入代码.然而后 ...
- nginx 获取请求头,URL参数
获取url参数 在 ngx_lua 中访问 Nginx 内置变量 ngx.var.arg_PARAMETER 即可获得GET参数PARAMETER的内容. 在 nginx配置中,通过$arg_PARA ...
- luarocks 安装
1. linux 安装 wget https://luarocks.org/releases/luarocks-2.4.1.tar.gz tar zxpf luarocks-2.4.1.tar.gz ...
- 系列文章--一步一步学Silverlight2
概述 由TerryLee编写的<Silverlight 2完美征程>一书,已经上市,在该系列文章的基础上补充了大量的内容,敬请关注.官方网站:http://www.dotneteye.cn ...
- cocos2dx 云彩特效
cocos2dx 云彩特效,就是一张云彩Png 图片从屏幕左边移动到右边,再从左边移动到右边 yuncai = static_cast<ImageView*>(Helper::seekWi ...
- [语法]C语言中二维数组做输入参数
C语言中二维数组做输入参数时, 可以同时指定各维长度, 可以只指定第二维的长度, 不可以只指定第一维的长度, 不可以各维长度都不指定. 一句话总结:要指定至少指定第二维,都不指定是不行的. 具体栗子如 ...
- zipkin之brave
brave是同步收集信息,及计算调用时间,但是异步发送日志信息给zipkin:所以很多时候你无法在第一时间获取日志数据可能需要等一会.另外在写一个demo的时候,因为最后睡了1秒,经常会发现丢了一些日 ...
- Centos下 yum方式安装LAMP
首先安装apache centos可以直接yum安装apache . 配置网易163 yum源 http://www.cnblogs.com/carbon3/p/5635403.html 一. ...