原 荐 mybatis二级缓存了解下

栏目: 数据库 · 发布时间: 7年前

内容简介:某小伙伴问了我一个问题

背景

某小伙伴问了我一个问题

原 荐 mybatis二级缓存了解下

原 荐 mybatis二级缓存了解下

为啥查不到数据了呢???

分析

看到这种问题 常规思路就是是否数据隔离造成

但是看起来是正常的【毕竟只是查询】

小伙伴写的代码如下

@Override
public PUserVo getUserInfo(PuserQuery puserQuery) {
    PUserRelationQuery pUserRelationQuery = new PUserRelationQuery();
    pUserRelationQuery.setWxOpenId(puserQuery.getWxOpenId());
    pUserRelationQuery.setDeleteFlag(puserQuery.getDelleteFlag());
    pUserRelationQuery.setUserId(puserQuery.getUserId());
    pUserRelationQuery.setUserType(puserQuery.getUserType());
    pUserRelationQuery.setId(puserQuery.getId());
    PUserRelation pUserRelation = pUserRelationService.getUserRelation(pUserRelationQuery);
    logger.info("getUserInfo pUserRelation:" + JSON.toJSONString(pUserRelation));
    if (pUserRelation != null) {
        PUserVo pUserVo = orikaMapper.convert(pUserRelation, PUserVo.class);
        pUserVo.setImgSrc(pUserRelation.getImgSource());
        if (pUserRelation.getUserId() != null) {
            Puser puser = puserService.getById(pUserRelation.getUserId());
            pUserVo.setUserTel(puser.getUserTel());
            pUserVo.setStationId(puser.getStationId());
            pUserVo.setUserName(pUserRelation.getNickName());
        }
        return pUserVo;
    } else {
        return null;
    }
}

从上面代码来看确实查询到了数据才会返回!!!那么为何在db中却查询不到了呢???

为了了解该数据从何处获得的~【或者对应id的数据什么时间删除的 我们来翻一下binlog】

原 荐 mybatis二级缓存了解下

早在早上8:30该数据就被人删除了,但是为何在9:06该数据还能查询到呢???

到了这时 缓存两个字就到到了我的思绪中~

众所周知 mybatis分为一级缓存和二级缓存

其中一级缓存通过BaseExecutor完成并且无法取消

二级缓存通过CachingExecutor完成 通过全局配置cacheEnabled完成配置

常规博客都说明使用二级缓存需要设置cacheEnabled为true

<setting name="cacheEnabled" value="true"/>

实质上真的如此么???

来看到对应的默认值

configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));

哎呀~原来默认的配置中二级缓存就是生效的~看来真是尽信书不如无书……

那么为什么我们平时没有感觉到二级缓存生效呢???

二级缓存代码如下

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  executorType = executorType == null ? defaultExecutorType : executorType;
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Executor executor;
  if (ExecutorType.BATCH == executorType) {
    executor = new BatchExecutor(this, transaction);
  } else if (ExecutorType.REUSE == executorType) {
    executor = new ReuseExecutor(this, transaction);
  } else {
    executor = new SimpleExecutor(this, transaction);
  }
  if (cacheEnabled) {
    executor = new CachingExecutor(executor);
  }
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}

来看一下CachingExecutor

@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
    throws SQLException {
  Cache cache = ms.getCache();
  if (cache != null) {
    flushCacheIfRequired(ms);
    if (ms.isUseCache() && resultHandler == null) {
      ensureNoOutParams(ms, parameterObject, boundSql);
      @SuppressWarnings("unchecked")
      List<E> list = (List<E>) tcm.getObject(cache, key);
      if (list == null) {
        list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        tcm.putObject(cache, key, list); // issue #578 and #116
      }
      return list;
    }
  }
  return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

一定要cache不为空并且useCache为true并且resultHandler为空才会生效

官方文档如是说

缓存

MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。MyBatis 3 中的缓存实现的很多改进都已经实现了,使得它更加强大而且易于配置。

默认情况下是没有开启缓存的,除了局部的 session 缓存,可以增强变现而且处理循环 依赖也是必须的。要开启二级缓存,你需要在你的 SQL 映射文件中添加一行:

<cache/>

字面上看就是这样。这个简单语句的效果如下:

  • 映射语句文件中的所有 select 语句将会被缓存。
  • 映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。
  • 缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
  • 根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。
  • 缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。
  • 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

NOTE The cache will only apply to statements declared in the mapping file where the cache tag is located. If you are using the Java API in conjunction with the XML mapping files, then statements declared in the companion interface will not be cached by default. You will need to refer to the cache region using the @CacheNamespaceRef annotation.

也就是说当使用cache【当然cache-ref也可以】的时候可以创建cache 当使用cache-ref用来引用其他定义的缓存

boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
 
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
    .resource(resource)
    .fetchSize(fetchSize)
    .timeout(timeout)
    .statementType(statementType)
    .keyGenerator(keyGenerator)
    .keyProperty(keyProperty)
    .keyColumn(keyColumn)
    .databaseId(databaseId)
    .lang(lang)
    .resultOrdered(resultOrdered)
    .resulSets(resultSets)
    .resultMaps(getStatementResultMaps(resultMap, resultType, id))
    .resultSetType(resultSetType)
    .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
    .useCache(valueOrDefault(useCache, isSelect))
    .cache(currentCache);

而当命令类型为select将会默认加上useCache为true 这样就完成了默认cache的二级缓存。

我们来看一下这位同学的代码

原 荐 mybatis二级缓存了解下

破案

上述明显了 该同学加上了cache导致对应namespace下的二级缓存生效呢 二级缓存不会在每个sqlSession查询完成或者事务完成后清除 因此在db中操作的删除语句并不能影响到内存中的数据 导致某个请求一直可以查到“不存在”的数据

由此引发了一些问题~


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

算法图解

算法图解

[美] Aditya Bhargava / 袁国忠 / 人民邮电出版社 / 2017-3 / 49.00元

本书示例丰富,图文并茂,以让人容易理解的方式阐释了算法,旨在帮助程序员在日常项目中更好地发挥算法的能量。书中的前三章将帮助你打下基础,带你学习二分查找、大O表示法、两种基本的数据结构以及递归等。余下的篇幅将主要介绍应用广泛的算法,具体内容包括:面对具体问题时的解决技巧,比如,何时采用贪婪算法或动态规划;散列表的应用;图算法;K最近邻算法。一起来看看 《算法图解》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

在线进制转换器
在线进制转换器

各进制数互转换器

MD5 加密
MD5 加密

MD5 加密工具