Mybatis一级缓存案例及原理分析

目录

1、案例

测试一级缓存什么时候使用

测试什么时候会清空缓存

案例总结

2、源码解析

缓存是什么?

那么他是什么时候被创建的呢?

那么这个CacheKy是怎么组成的呢?

那么缓存的值呢?最终是怎么存的缓存?

那么什么时候删除缓存呢?


1、案例

测试一级缓存什么时候使用

从下列的测试的代码中,我们很容易看出,在同一个sqlSession中,调用两次同样的查询,第二次是不会在建立数据库连接去进行查询的,而且输出的结果集是同一个对象,

        

从而可以判断出,Mybatis是默认开启一级缓存的,而缓存的范围是以sqlSession为单位

测试什么时候会清空缓存

观察下面的代码,大家可以看到,如果中间sqlSession去执行了commit操作(执行插入、更新、删除),则会清空SqlSession中的一级缓存,这样做的目的是为了让缓存中存储的是最新的信息,避免脏读。

还有一种手动清除缓存的办法,就是执行:sqlSession.clearCache();

案例总结

        ①第一次发起查询用户id的时候,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息后,将用户信息存储到缓存中。

        ②如果中间sqlSession去执行了commit操作(执行插入、更新、删除),则会清空SqlSession中的一级缓存,这样做的目的是为了让缓存中存储的是最新的信息,避免脏读。

           还有一种手动清除缓存的办法,就是执行:sqlSession.clearCache();

        ③第二次查询用户信息的时候,先去缓存找有没有,有,直接拿,没有,则去进行查询。

2、源码解析

        问题:缓存是什么?什么时候创建?什么时候清空?工作流程是什么?

        我们都知道手动清除缓存的代码:sqlSession.clearCache(); 从这段代码入手,可以很清楚看到缓存是什么,我先把这个类流程画出来。

缓存是什么?

        在点进PerpetualCache中clear方法后,会调用cache.clear();方法,观察cache,他其实就是类属性中的一个

        HashMap:private Map<Object, Object> cache = new HashMap<Object, Object>();

好了,第一问题:缓存是什么我们清楚了,就是一个HashMap集合

那么他是什么时候被创建的呢?

进入BaseExecutor,在执行query方法中,我们可以很明显的看到两段代码:        

CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);

        也就是说,每次在执行query方法的时候都会根据一定的规则生成一个缓存的key,然后在真正执行查询代码的时候传入这个key,根据这个cache key,去匹配缓存是否存在,如果不存在就创建,存在就直接过去结果值。

那么这个CacheKy是怎么组成的呢?

        进入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中还配置了<environments>这个标签,那么在这里他还会判断,如果设置了这个标签,就拿到这个标签属性的 default="development" 值,作为key的一部分

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<E>) localCache.getObject(key) : null;
if (list != null) {
  handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
  list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

那么什么时候删除缓存呢?

        在上面的案例中其实也说过了,这里在说下代码层面上的吧,从BaseExecutor这个类中,我们得知清除缓存的方法为:clearLocalCache();

        搜下这个类中什么时候会调用这个方法呢?很明显 曾删改 还有使用commit提交命令时,都会触发删除缓存的操作。

        至此,一级缓存所有案例分析、源码分析都结束了。

        感谢大家。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值