主题

  分享记录一下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的一级缓存的更多相关文章

  1. MyBatis 学习记录5 MyBatis的二级缓存

    主题 之前学习了一下MyBatis的一级缓存,主要涉及到BaseExecutor这个类. 现在准备学习记录下MyBatis二级缓存. 配置二级缓存与初始化发生的事情 首先二级缓存默认是不开启的,需要自 ...

  2. myBatis学习(9):一级缓存和二级缓存

    正如大多数持久层框架一样,MyBatis同样提供了一级缓存和二级缓存的支持 1. MyBatis一级缓存基于PerpetualCache的HashMap本地缓存,其存储作用域为 Session,默认情 ...

  3. mybatis学习记录二——mybatis开发dao的方法

    4.1     SqlSession使用范围 4.1.1     SqlSessionFactoryBuilder 通过SqlSessionFactoryBuilder创建会话工厂SqlSession ...

  4. MyBatis学习总结(七)——Mybatis缓存(转载)

      孤傲苍狼 只为成功找方法,不为失败找借口! MyBatis学习总结(七)--Mybatis缓存 一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的 ...

  5. 【转】MyBatis学习总结(七)——Mybatis缓存

    [转]MyBatis学习总结(七)——Mybatis缓存 一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持 一级缓存: 基于PerpetualC ...

  6. 【转】MyBatis学习总结(一)——MyBatis快速入门

    [转]MyBatis学习总结(一)——MyBatis快速入门 一.Mybatis介绍 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC ...

  7. 转:MyBatis学习总结(Mybatis总结精华文章)

    http://www.cnblogs.com/xdp-gacl/tag/MyBatis%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93/ 当前标签: MyBatis学习总结   ...

  8. Mybatis学习(五)————— 延迟加载和缓存机制(一级二级缓存)

    一.延迟加载 延迟加载就是懒加载,先去查询主表信息,如果用到从表的数据的话,再去查询从表的信息,也就是如果没用到从表的数据的话,就不查询从表的信息.所以这就是突出了懒这个特点.真是懒啊. Mybati ...

  9. Mybatis学习记录(七)----Mybatis查询缓存

    1. 什么是查询缓存 mybatis提供查询缓存,用于减轻数据压力,提高数据库性能. mybaits提供一级缓存,和二级缓存. 一级缓存是SqlSession级别的缓存.在操作数据库时需要构造 sql ...

随机推荐

  1. HDU - 5324:Boring Class (CDQ分治&树状数组&最小字典序)

    题意:给定N个组合,每个组合有a和b,现在求最长序列,满足a不升,b不降. 思路:三位偏序,CDQ分治.   但是没想到怎么输出最小字典序,我好菜啊. 最小字典序: 我们倒序CDQ分治,ans[i]表 ...

  2. java获取服务器的ip和地址

    HttpServletRequest httpRequest=(HttpServletRequest)request; String strBackUrl = "http://" ...

  3. python(三):函数

    一.函数.名称空间与作用域 1.函数的构成 python有三种层次的抽象:(1)程序可分成多个模块:(2)每个模块包含多条语句:(3)每条语句对对象进行操作.函数大致处于第二层.函数有它的定义格式.参 ...

  4. python3 的字符串格式判断

    在python编程中,我们经常要面临将字符串进行转换的情况,那么字符串是否符合转换的要求呢?python中内置了字符串类的方法供我们使用进行字符串格式的判断. 1.isalnum() 所有字符都是数字 ...

  5. mix deps HEX_HTTP_CONCURRENCY=1 HEX_HTTP_TIMEOUT=120 timeout

    mix  deps.get  timeout 问题: If this happens consistently, adjust your concurrency and timeout setting ...

  6. eclipse marketplace网络连接失败的解决方法

    2015-12-04 01:12:33 本想在eclipse上安装一个插件,点进help-EclipseMarketplace却连接失败,错误如下: 在help-instal new software ...

  7. linux下踢出已登录用户

    通过xshell登录到linux,看到如下所示,有3个用户,但是前面两个不知在哪登录的了,那就踢出吧. 先确认一下自己是哪个 顺便注意一下“whoami”和“who am i”的不同 然后踢出前面两个 ...

  8. java代码---charAt()和toCharry()的用法

    总结:charAt()是返回字符型,把字符串拆分成某个字符,返回指定位置的字符.可以把字符串看成char型数组 package com.sads; ///输出一个大写英文字母的 public clas ...

  9. databaseDesgin-temple

    ylbtech-dbs:ylbtech-storebook- A, 返回顶部 1, 2, B,返回顶部 1, 2, C,返回顶部 作者:ylbtech出处:http://storebook.cnblo ...

  10. Hive中的用户自定义函数UDF

    Hive中的自定义函数允许用户扩展HiveQL,是一个非常强大的功能.Hive中具有多种类型的用户自定义函数.show functions命令可以列举出当前Hive会话中的所加载进来的函数,包括内置的 ...