广告位联系
返回顶部
分享到

Mybatis的缓存详细介绍

java 来源:转载 作者:秩名 发布时间:2021-01-16 12:51:37 人浏览
摘要

Mybatis的缓存 mybatis是一个查询数据库的封装框架,主要是封装提供灵活的增删改sql,开发中,service层能够通过mybatis组件查询和修改数据库中表的数据;作为查询工具,mybatis有使用缓存,这里讲一下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默认是先查询二级缓存,没有,再查看一级缓存,都为空,最后查询数据库


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 : https://www.cnblogs.com/Narule/p/14284868.html
相关文章
  • SpringBoot自定义错误处理逻辑介绍

    SpringBoot自定义错误处理逻辑介绍
    1. 自定义错误页面 将自定义错误页面放在 templates 的 error 文件夹下,SpringBoot 精确匹配错误信息,使用 4xx.html 或者 5xx.html 页面可以打印错误
  • Java实现手写一个线程池的代码

    Java实现手写一个线程池的代码
    线程池技术想必大家都不陌生把,相信在平时的工作中没有少用,而且这也是面试频率非常高的一个知识点,那么大家知道它的实现原理和
  • Java实现断点续传功能的代码

    Java实现断点续传功能的代码
    题目实现:网络资源的断点续传功能。 二、解题思路 获取要下载的资源网址 显示网络资源的大小 上次读取到的字节位置以及未读取的字节
  • 你可知HashMap为什么是线程不安全的
    HashMap 的线程不安全 HashMap 的线程不安全主要体现在下面两个方面 在 jdk 1.7 中,当并发执行扩容操作时会造成环形链和数据丢失的情况 在
  • ArrayList的动态扩容机制的介绍

    ArrayList的动态扩容机制的介绍
    对于 ArrayList 的动态扩容机制想必大家都听说过,之前的文章中也谈到过,不过由于时间久远,早已忘却。 所以利用这篇文章做做笔记,加
  • JVM基础之字节码的增强技术介绍

    JVM基础之字节码的增强技术介绍
    字节码增强技术 在上文中,着重介绍了字节码的结构,这为我们了解字节码增强技术的实现打下了基础。字节码增强技术就是一类对现有字
  • Java中的字节码增强技术

    Java中的字节码增强技术
    1.字节码增强技术 字节码增强技术就是一类对现有字节码进行修改或者动态生成全新字节码文件的技术。 参考地址 2.常见技术 技术分类 类
  • Redis BloomFilter布隆过滤器原理与实现

    Redis BloomFilter布隆过滤器原理与实现
    Bloom Filter 概念 布隆过滤器(英语:Bloom Filter)是1970年由一个叫布隆的小伙子提出的。它实际上是一个很长的二进制向量和一系列随机映射
  • Java C++算法题解leetcode801使序列递增的最小交换次

    Java C++算法题解leetcode801使序列递增的最小交换次
    题目要求 思路:状态机DP 实现一:状态机 Java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution { public int minSwap(int[] nums1, int[] nums2) { int n
  • Mybatis结果集映射与生命周期介绍

    Mybatis结果集映射与生命周期介绍
    一、ResultMap结果集映射 1、设计思想 对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了 2、resultMap的应用场
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计