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

主题

  之前学习了一下MyBatis的一级缓存,主要涉及到BaseExecutor这个类. 现在准备学习记录下MyBatis二级缓存.

配置二级缓存与初始化发生的事情

首先二级缓存默认是不开启的,需要自己配置开启.

如上图,需要在configuration里去开启.

其次在需要用到二级缓存的Mapper的配置里做一些操作,如下图,增加一个cache节点

至此就可以在UserMapper上开启二级缓存了.

当MaBatis初始化的时候,需要解析各种XML配置来生成SQLSessionFactory,解析Mapper的配置也是其中一环.而解析Mapper的配置中,解析Cache节点又是其中的一部分.

这个解析cache会做什么事情呢?

如上图所示,会往builderAssistant里去新建一个Cache对象(其中的参数是你之前XML里配置的).

Cache类的设计也是装饰着模式,层层嵌套.比如我们new出来的Cache中会包裹一个打印缓存命中信息的LoggingCache,我们指定的LruCache包裹着PerpetualCache(一级缓存也使用这个Cache)等等.

MapperBuilderAssistant是个Builder模式,解析Mapper的最后会调用assistant的addMappedStatement方法.

 1     public MappedStatement addMappedStatement(
 2             String id,
 3             SqlSource sqlSource,
 4             StatementType statementType,
 5             SqlCommandType sqlCommandType,
 6             Integer fetchSize,
 7             Integer timeout,
 8             String parameterMap,
 9             Class<?> parameterType,
10             String resultMap,
11             Class<?> resultType,
12             ResultSetType resultSetType,
13             boolean flushCache,
14             boolean useCache,
15             boolean resultOrdered,
16             KeyGenerator keyGenerator,
17             String keyProperty,
18             String keyColumn,
19             String databaseId,
20             LanguageDriver lang,
21             String resultSets) {
22
23         if (unresolvedCacheRef) {
24             throw new IncompleteElementException("Cache-ref not yet resolved");
25         }
26
27         id = applyCurrentNamespace(id, false);
28         boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
29
30         MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
31                 .resource(resource)
32                 .fetchSize(fetchSize)
33                 .timeout(timeout)
34                 .statementType(statementType)
35                 .keyGenerator(keyGenerator)
36                 .keyProperty(keyProperty)
37                 .keyColumn(keyColumn)
38                 .databaseId(databaseId)
39                 .lang(lang)
40                 .resultOrdered(resultOrdered)
41                 .resultSets(resultSets)
42                 .resultMaps(getStatementResultMaps(resultMap, resultType, id))
43                 .resultSetType(resultSetType)
44                 .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
45                 .useCache(valueOrDefault(useCache, isSelect))
46                 .cache(currentCache);
47
48         ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
49         if (statementParameterMap != null) {
50             statementBuilder.parameterMap(statementParameterMap);
51         }
52
53         MappedStatement statement = statementBuilder.build();
54         configuration.addMappedStatement(statement);
55         return statement;
56     }

把刚才new出来的cache设置到MappedStatement.Builder中,最后build出一个MappedStatement,并添加到Configuration里. 如下图

所以其实我们刚才在Mapper的XML里配置的cache,最后生成对应的java Cache对象,是放在MappedStatement里的,而MappedStatement是什么东西? 看了如下3个截图大家应该就明白了.就是你Mapper XML里配置的一个一个DML对应的节点,当然1个Mapper.XML里会有很多这样的节点,而他们是公用1个Cache的.

MappedStatement是全局唯一的对象,被放到了Configuration里,不同的Executor需要用到的时候都会从Configuration里去找SQL对应的MappedStatement.(其实是SqlSession调用Executor的query方法的时候会去根据类.方法名在configuration里查找mappedStatement并传入)

CachingExecutor

正如之前文章记录的那样,CachingExecutor是包裹在BaseExecutor外的一层Executor,使用的是装饰着模式. 二级缓存主要是在它里面实现的.

先来看一个Demo

2018-10-14 12:04:17,138 DEBUG [main] logging.LogFactory : Logging initialized using ‘class org.apache.ibatis.logging.slf4j.Slf4jImpl‘ adapter.
Logging initialized using ‘class org.apache.ibatis.logging.stdout.StdOutImpl‘ adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Cache Hit Ratio [test.mapper.UserMapper]: 0.0
Opening JDBC Connection
Sun Oct 14 12:04:17 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.
Created connection 25300561.
Setting autocommit to false on JDBC Connection [[email protected]]
==>  Preparing: select id, user_id, user_name, real_name, email, creator_uid, modifier_uid, created_at, updated_at, del from user where id = ?
==> Parameters: 1(Integer)
<==    Columns: id, user_id, user_name, real_name, email, creator_uid, modifier_uid, created_at, updated_at, del
<==        Row: 1, 1, test, realName, [email protected], 1, 1, 2018-09-24 10:10:43.0, 2018-09-24 10:10:46.0, 0
<==      Total: 1
User{id=1, userId=1, userName=‘test‘, realName=‘realName‘, email=‘[email protected]‘, creatorUid=1, modifierUid=1, createdAt=Mon Sep 24 10:10:43 CST 2018, updatedAt=Mon Sep 24 10:10:46 CST 2018, del=false}
Resetting autocommit to true on JDBC Connection [[email protected]]
Closing JDBC Connection [[email protected]]
Returned connection 25300561 to pool.
Cache Hit Ratio [test.mapper.UserMapper]: 0.5
User{id=1, userId=1, userName=‘test‘, realName=‘realName‘, email=‘[email protected]‘, creatorUid=1, modifierUid=1, createdAt=Mon Sep 24 10:10:43 CST 2018, updatedAt=Mon Sep 24 10:10:46 CST 2018, del=false}

如上图,从1个SQLSessionFactory里获取了2个SqlSession,分别调用了1次selectByPrimaryKey.

而红色部分输出看到 第一个sqlSession打印了1次SQL, 第二次调用selectByPrimaryKey的时候会打印蓝色的部分,输出缓存命中,概率为1/2,然后没有打印红色的查询语句,而是直接输出返回打印了之前缓存中的对象.

CachingExecutor的query方法如下图

与BaseExecutor的处理方法大致类似,只是先会从MapperedStatement里去寻找是否配置过缓存,有的话就从TransactionalCacheManager中通过Cache做为Key去找TransactionalCache,再从TransactionalCache中根据CacheKey查找结果, TransactionalCache也是Cache的一种实现,目的是为了支持事务回滚提交操作.

TransactionalCache中缓存对象的key为CacheKey,CacheKey之前在一级缓存中已经分享过,value为数据库查询结果,即一个ArrayList.

如果缓存中没有,那就委托内部wrapped的对象,也就是SImpleExecutor去调用query去数据库中找.

小结

总结一下二级缓存如何实现:

1.初始化阶段初始化Cache设置到MappedStatement里,MappedStatement设置到Configuration里.

2.执行查询的时候SqlSession去Configuration里找到MappedStatement传给CachingExecutor.

3.CachingExecutor获取MappedStatement再获取其中的Cache,根据Cache是否null,判断是否有开启二级缓存.

4.如果有开启,那Cache不为null,去TransactionalCacheManager中根据Cache为Key找到TransactionalCache,为null就委托SImpleExecutor继续查数据库.不为null,就在TransactionalCache根据CacheKey找到对应的Value并返回.

原文地址:https://www.cnblogs.com/abcwt112/p/9785816.html

时间: 2024-09-28 20:03:12

MyBatis 学习记录5 MyBatis的二级缓存的相关文章

Mybatis学习记录(四)--高级查询和缓存

这些都是连贯的学习笔记,所以有的地方因为之前都说过,所以也就没怎么写详细了,看不太明白的可以看看之前的笔记. 一.高级查询 高级查询主要是一对一查询,一对多查询,多对多查询 1.一对一查询 有用户和订单两个表,用户对订单是1对1查询.也就是订单中有一个外键是指向用户的. 先创建实体类: User.java public class User { private int id; private String username; private String password; private St

Mybatis学习记录(三)--Mybatis配置文件详解

关于mybatis的配置文件,主要有两个,一个是SqlMapperConfig.xml文件一个是mapper.xml文件,分别对这两个进行深入全面学习. 一.SqlMapperConfig.xml文件 1.标签概况 在SqlMapperConfig.xml中主要有以下标签,其中环境集合environments和spring整合后废除不用.objectFactory和plugins不经常使用. properties(属性) settings(全局配置参数) typeAliases(类型别名) ty

Mybatis学习记录(二)--Mybatis开发DAO方式

mybatis开发dao的方法通常用两种,一种是传统DAO的方法,一种是基于mapper代理的方法,下面学习这两种开发模式. 写dao之前应该要对SqlSession有一个更加细致的了解 一.mybatis的SqlSession使用范围 SqlSessionFactoryBuilder用于创建SqlSessionFacoty,SqlSessionFacoty一旦创建完成就不需要SqlSessionFactoryBuilder了,因为SqlSession是通过SqlSessionFactory生产

【MyBatis学习13】MyBatis中的二级缓存

1. 二级缓存的原理 前面介绍了,mybatis中的二级缓存是mapper级别的缓存,值得注意的是,不同的mapper都有一个二级缓存,也就是说,不同的mapper之间的二级缓存是互不影响的.为了更加清楚的描述二级缓存,先来看一个示意图: 从图中可以看出: sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到该UserMapper的二级缓存中. 如果SqlSession3去执行相同 mapper下sql,执行commit提交,则会清空该UserMapper下二级缓

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

孤傲苍狼 只为成功找方法,不为失败找借口! MyBatis学习总结(七)--Mybatis缓存 一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空. 2. 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,Hash

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

[转]MyBatis学习总结(七)——Mybatis缓存 一.MyBatis缓存介绍 正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空. 2. 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 M

mybatis学习笔记(14)-mybatis整合ehcache

mybatis学习笔记(14)-mybatis整合ehcache mybatis学习笔记14-mybatis整合ehcache 分布缓存 整合方法掌握 整合ehcache 加入ehcache的配置文件 ehcache是一个分布式缓存框架 分布缓存 我们系统为了提高系统并发,性能.一般对系统进行分布式部署(集群部署方式) 不使用分布缓存,缓存的数据在各各服务单独存储,不方便系统开发.所以要使用分布式缓存对缓存数据进行集中管理. mybatis无法实现分布式缓存,需要和其它分布式缓存框架进行整合.

MyBatis学习总结(一)——MyBatis快速入门(转载)

孤傲苍狼 只为成功找方法,不为失败找借口! MyBatis学习总结(一)--MyBatis快速入门 一.Mybatis介绍 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录. 二.mybatis快速入门 2.1.准备

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

[转]MyBatis学习总结(一)——MyBatis快速入门 一.Mybatis介绍 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录. 二.mybatis快速入门 2.1.准备开发环境 1.创建测试项目,普通jav