详解Mybatis的缓存

Mybatis的缓存

mybatis是一个查询数据库的封装框架,主要是封装提供灵活的增删改sql,开发中,service层能够通过mybatis组件查询和修改数据库中表的数据;作为查询工具,mybatis有使用缓存,这里讲一下mybatis的缓存相关源码。

缓存

在计算机里面,任何信息都有源头,缓存一般指源头信息读取后,放在内存或者其他读取较快的地方,下次读取相同信息不去源头查询而是直接从内存(或者能快速存取的硬件)读取。这样可以减少硬件使用,提高读取速度。

mybatis也是这样,查询数据库的数据之后,mybatis可以把查询结果缓存到内存,下次查询如果查询语句相同,并且查询相关的表的数据没被修改过,就可以直接返回缓存中的结果,而不用去查询数据库的语句,有效节省了时间。

简单看一下mybatis一级缓存和二级缓存相关源码,学习使用

一级缓存

通过查看源码可知,一级缓存是绑定sqSsession中的,所以每次查询sqlSession不同就失效,相同的sqlSession可以使用一级缓存。

mybatis默认sqlsession:org.apache.ibatis.session.defaults.DefaultSqlSession

构造方法中传入executor(查询执行对象)

 public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
  this.configuration = configuration;
  this.executor = executor;
  this.dirty = false;
  this.autoCommit = autoCommit;
 }

executor中携带一级缓存成员:

 protected BaseExecutor(Configuration configuration, Transaction transaction) {
  this.transaction = transaction;
  this.deferredLoads = new ConcurrentLinkedQueue<>();
  this.localCache = new PerpetualCache("LocalCache"); //默认一级缓存
  this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
  this.closed = false;
  this.configuration = configuration;
  this.wrapper = this;
 }

查询使用一级缓存逻辑

org.apache.ibatis.executor.BaseExecutor.query()

 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());

  List<E> list;
  try {
   queryStack++;
   	//localCache 一级缓存
   list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
    //先从一级缓存中获取,key是通过sql语句生成
   if (list != null) {
    handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
   } else {
    // 如果缓存中没有 才从数据库查询
    list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
   }
  } finally {
   queryStack--;
  }
  return list;
 }

 //从数据库读取数据
 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  List<E> list;
  localCache.putObject(key, EXECUTION_PLACEHOLDER);
  try {
   list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
  } finally {
   localCache.removeObject(key);//将一级缓存清除
  }
  localCache.putObject(key, list);//返回查询结果之前,先放入一级缓存 刷新
  if (ms.getStatementType() == StatementType.CALLABLE) {
   localOutputParameterCache.putObject(key, parameter);
  }
  return list;
 }

二级缓存

二级缓存mapper中的,默认是开启的,但需要在映射文件mapper.xml中添加<cache/>标签

<mapper namespace="userMapper">
	<cache/><!-- 添加cache标签表示此mapper使用二级缓存 -->
</mapper>

配置false可以关闭二级缓存

二级缓存的解析

org.apache.ibatis.builder.xml.XMLMapperBuilder

 private void configurationElement(XNode context) {
  try {
   //...
   cacheElement(context.evalNode("cache")); //解析cache标签
  } catch (Exception e) {
   throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
  }
 }

 private void cacheElement(XNode context) {
  if (context != null) { // if hava cache tag 如果有cache标签才执行下面的逻辑
   String type = context.getStringAttribute("type", "PERPETUAL");
   Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
   String eviction = context.getStringAttribute("eviction", "LRU");
   Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
   Long flushInterval = context.getLongAttribute("flushInterval");
   Integer size = context.getIntAttribute("size");
   boolean readWrite = !context.getBooleanAttribute("readOnly", false);
   boolean blocking = context.getBooleanAttribute("blocking", false);
   Properties props = context.getChildrenAsProperties();
   builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);//建立二级缓存
  }
 }

org.apache.ibatis.builder.MapperBuilderAssistant.useNewCache():

 public Cache useNewCache(Class<? extends Cache> typeClass,
   Class<? extends Cache> evictionClass,
   Long flushInterval,
   Integer size,
   boolean readWrite,
   boolean blocking,
   Properties props) {
  Cache cache = new CacheBuilder(currentNamespace)
    .implementation(valueOrDefault(typeClass, PerpetualCache.class))
    .addDecorator(valueOrDefault(evictionClass, LruCache.class))
    .clearInterval(flushInterval)
    .size(size)
    .readWrite(readWrite)
    .blocking(blocking)
    .properties(props)
    .build();
  configuration.addCache(cache);//二级缓存赋值,如果cache标签为空,不会执行此方法,currentCache为空
  currentCache = cache;
  return cache;
 }

在映射文件mapper中如果没有cache标签,不会执行上面的useNewCache方法,cache为null,就不会使用二级缓存(相当于失效)。

查询使用二级缓存逻辑

org.apache.ibatis.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) {//如果二级缓存对象不为空 尝试在二级缓存中获取(没有cache标签此对象就是空)
   flushCacheIfRequired(ms);
   if (ms.isUseCache() && resultHandler == null) {
    ensureNoOutParams(ms, boundSql);
    @SuppressWarnings("unchecked")
    List<E> list = (List<E>) tcm.getObject(cache, key); //从二级缓存中获取数据
    if (list == null) {
     list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); //如果为空,使用delegate查询(BaseExecutor)
     tcm.putObject(cache, key, list); // 查询结果保存到二级缓存
    }
    return list;
   }
  }
  return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
 }

二级缓存和一级缓存不用想,数据库的数据被修改是要清空缓存的,不然数据有误,至于怎么清空,是另一套逻辑了,mapper中的cache标签可以配置一些参数,比如缓存定期清空。

一级二级缓存先后顺序

mybatis默认是先查询二级缓存,没有,再查看一级缓存,都为空,最后查询数据库

以上就是详解Mybatis的缓存的详细内容,更多关于Mybatis的缓存的资料请关注我们其它相关文章!

(0)

相关推荐

  • SpringBoot下Mybatis的缓存的实现步骤

    说起 mybatis,作为 Java 程序员应该是无人不知,它是常用的数据库访问框架.与 Spring 和 Struts 组成了 Java Web 开发的三剑客--- SSM.当然随着 Spring Boot 的发展,现在越来越多的企业采用的是 SpringBoot + mybatis 的模式开发,我们公司也不例外.而 mybatis 对于我也仅仅停留在会用而已,没想过怎么去了解它,更不知道它的缓存机制了,直到那个生死难忘的 BUG.故事的背景比较长,但并不是啰嗦,只是让读者知道这个 BUG 触

  • MyBatis缓存实现原理及代码实例解析

    一.一级缓存(本地缓存) sqlSession级别的缓存.一级缓存是一直开启的:SqlSession级别的一个Map与数据库同一次会话期间查询到的数据会放在本地缓存中.以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库: 一级缓存失效情况(没有使用到当前一级缓存的情况,效果就是,还需要再向数据库发出查询): 1.sqlSession不同 2.sqlSession相同,查询条件不同.(当前一级缓存中还没有这个数据) 3.sqlSession相同,两次查询之间执行了增删改操作(这次增删

  • Mybatis如何通过注解开启使用二级缓存

    这篇文章主要介绍了Mybatis基于注解开启使用二级缓存,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 本文主要是补充一下Mybatis中基于注解的二级缓存的开启使用方法. 1.在Mybatis的配置文件中开启二级缓存 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.or

  • Mybatis如何实现延迟加载及缓存

    一.延迟加载 1.在mybatis.xml配置文件中,开启延迟加载 <settings> <!--开启延迟加载--> <setting name="lazyLoadingEnabled" value="true"></setting> <setting name="aggressiveLazyLoading" value="false"></setting>

  • MyBatis开启二级缓存实现过程解析

    MyBatis的一级缓存是sqlSession作用域的,默认开启,执行DML(insert, update, delete)操作后自动删除. 下面介绍一下如何开启MyBatis的二级缓存,作用域为Mapper: 1.修改config.xml配置文件: <settings> <!-- 开启二级缓存 --> <setting name="cacheEnabled" value="true"/> </settings> 这里

  • 解决spring结合mybatis时一级缓存失效的问题

    之前了解到mybatis的一级缓存是默认开启的,作用域是sqlSession,是基 HashMap的本地缓存.不同的SqlSession之间的缓存数据区域互不影响. 当进行select.update.delete操作后并且commit事物到数据库之后,sqlSession中的Cache自动被清空 <setting name="localCacheScope" value="SESSION"/> 结论 spring结合mybatis后,一级缓存作用: 在未

  • Mybatis-plus基于redis实现二级缓存过程解析

    1. mybatis-plus开启二级缓存 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver jdbc-url: jdbc:mysql://192.168.222.155:3306/sys?serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true&

  • mybatis plus使用redis作为二级缓存的方法

    建议缓存放到 service 层,你可以自定义自己的 BaseServiceImpl 重写注解父类方法,继承自己的实现.为了方便,这里我们将缓存放到mapper层.mybatis-plus整合redis作为二级缓存与mybatis整合redis略有不同. 1. mybatis-plus开启二级缓存 mybatis-plus.configuration.cache-enabled=true 2. 定义RedisTemplate的bean交给spring管理,这里为了能将对象直接存取到redis中,

  • SpringBoot + Mybatis-plus实战之Mybatis-plus的一级缓存、二级缓存

    前言 现在的JAVA行业,貌似已经是SpringBoot + SpringCloud 的天下了,早期的SSH,SSM框架已经老去,与SpringBoot相结合的JPA框架虽然省去了很多的增删改查sql,但是比较笨拙,在面对一些复杂多变的逻辑时常常力不从心,而相对应的Mybatis由于其高度的灵活性受到广大JAVA攻城狮的欢迎.之前整合过了springboot+mybatis,前几天看到一个面试的问一个问题,Mybatis的一级缓存,二级缓存.我想这个应该也是一个重点吧,所以今天决定来详细解读一下

  • Mybatis 缓存原理及失效情况解析

    这篇文章主要介绍了Mybatis 缓存原理及失效情况解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.什么是缓存[Cache] 存在内存中的临时数据. 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题. 2.为什么要使用缓存 减少和数据库的交互次数,减少系统开销,提高系统效率. 3.什么样的数据需要使用缓存 经常查询且不易改变的数据

  • MyBatis整合Redis实现二级缓存的示例代码

    MyBatis框架提供了二级缓存接口,我们只需要实现它再开启配置就可以使用了. 特别注意,我们要解决缓存穿透.缓存穿透和缓存雪崩的问题,同时也要保证缓存性能. 具体实现说明,直接看代码注释吧! 1.开启配置 SpringBoot配置 mybatis: configuration: cache-enabled: true 2.Redis配置以及服务接口 RedisConfig.java package com.leven.mybatis.api.config; import com.fasterx

随机推荐