目录
1、案例
测试一级缓存什么时候使用
测试什么时候会清空缓存
案例总结
2、源码解析
缓存是什么?
那么他是什么时候被创建的呢?
那么这个CacheKy是怎么组成的呢?
那么缓存的值呢?最终是怎么存的缓存?
那么什么时候删除缓存呢?
从下列的测试的代码中,我们很容易看出,在同一个sqlSession中,调用两次同样的查询,第二次是不会在建立数据库连接去进行查询的,而且输出的结果集是同一个对象,
从而可以判断出,Mybatis是默认开启一级缓存的,而缓存的范围是以sqlSession为单位
观察下面的代码,大家可以看到,如果中间sqlSession去执行了commit操作(执行插入、更新、删除),则会清空SqlSession中的一级缓存,这样做的目的是为了让缓存中存储的是最新的信息,避免脏读。
还有一种手动清除缓存的办法,就是执行:sqlSession.clearCache();
①第一次发起查询用户id的时候,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息后,将用户信息存储到缓存中。
②如果中间sqlSession去执行了commit操作(执行插入、更新、删除),则会清空SqlSession中的一级缓存,这样做的目的是为了让缓存中存储的是最新的信息,避免脏读。
还有一种手动清除缓存的办法,就是执行:sqlSession.clearCache();
③第二次查询用户信息的时候,先去缓存找有没有,有,直接拿,没有,则去进行查询。
问题:缓存是什么?什么时候创建?什么时候清空?工作流程是什么?
我们都知道手动清除缓存的代码:sqlSession.clearCache(); 从这段代码入手,可以很清楚看到缓存是什么,我先把这个类流程画出来。
在点进PerpetualCache中clear方法后,会调用cache.clear();方法,观察cache,他其实就是类属性中的一个
HashMap:private Map
好了,第一问题:缓存是什么我们清楚了,就是一个HashMap集合
进入BaseExecutor,在执行query方法中,我们可以很明显的看到两段代码:
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
也就是说,每次在执行query方法的时候都会根据一定的规则生成一个缓存的key,然后在真正执行查询代码的时候传入这个key,根据这个cache key,去匹配缓存是否存在,如果不存在就创建,存在就直接过去结果值。
进入createCacheKey()可以看到下面的代码片段
从代码中我们可以看出来,mybatis中的一级缓存的key是由四个部分组成的:
statementid:唯一标识,一般是 mapper全路径 + “.” + 方法名
params:传入的参数值
boundSql:sql语句本身
rowBounds:分页对象
CacheKey cacheKey = new CacheKey();
//statementid sql语句的所在位置包名+类名+方法名
cacheKey.update(ms.getId());
//rowBounds offset 默认是0
cacheKey.update(rowBounds.getOffset());
//rowBounds limit默认是Integer.MAXVALUE
cacheKey.update(rowBounds.getLimit());
//boundSql 具体的SQL语句
cacheKey.update(boundSql.getSql());
for (ParameterMapping parameterMapping : parameterMappings) {
....
....
//params sql中的参数
cacheKey.update(value);
}
当然,如果我们在sqlMapConfig中还配置了
if (configuration.getEnvironment() != null) {
// issue #176
cacheKey.update(configuration.getEnvironment().getId());
}
这个就比较好回答了,在获取到key后,SQL的执行器会开始真正的执行查询,查询的方法中会带入这个cache key,如果找到了就直接返回数据,如果没找到,证明这次查询并没有缓存就会真正的去进行数据库查询,然后把结果进行缓存
也就是map = put(生成的cache key,结果集)
代码片段如下:
// 判断list是否为null,如果有一级缓存直接获取值,如果没有进行数据库查询
list = resultHandler == null ? (List) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
在上面的案例中其实也说过了,这里在说下代码层面上的吧,从BaseExecutor这个类中,我们得知清除缓存的方法为:clearLocalCache();
搜下这个类中什么时候会调用这个方法呢?很明显 曾删改 还有使用commit提交命令时,都会触发删除缓存的操作。
至此,一级缓存所有案例分析、源码分析都结束了。
感谢大家。