mybatis缓存

加上flushCache=”true”后,再次运行结果如下

2.二级缓存

mybatis的二级缓存默认开启,但真正使用需要在mapper文件中添加相应的缓存配置

二级缓存存在于SqlSessionFactory生命周期中, 每个二级缓存对同一个mapper文件中的SELECT操作有效

#Configuration
protected boolean cacheEnabled = true;

// 执行器默认会传入到CachingExecutor进行一层包装
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);
}

#CacheExecutor
// delegate委托对象 为上述传入的一种具体的执行器
// cacheExecutor的具体查询更新操作都是通过委托对象来进行操作的
private final Executor delegate;
// 缓存管理器
private final TransactionalCacheManager tcm = new TransactionalCacheManager();

public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
    delegate.setExecutorWrapper(this);
}

// 具体分析下cacheExecutor的query方法
@Override
public <e> List<e> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
    throws SQLException {
    // &#x770B;&#x662F;&#x5426;&#x6709;&#x914D;&#x7F6E;&#x4E8C;&#x7EA7;&#x7F13;&#x5B58;
    Cache cache = ms.getCache();
    if (cache != null) {
        // &#x6839;&#x636E;cache != null && ms.isFlushCacheRequired() &#x5237;&#x65B0;&#x4E8C;&#x7EA7;&#x7F13;&#x5B58;
        // &#x82E5;select&#x6807;&#x7B7E;&#x4E2D;&#x6CA1;&#x6709;&#x914D;&#x7F6E; flushCache="true"&#x5219;&#x4E0D;&#x4F1A;&#x5237;&#x65B0;
        flushCacheIfRequired(ms);
        // &#x662F;&#x5426;&#x5C06;&#x67E5;&#x8BE2;&#x7ED3;&#x679C;&#x8FDB;&#x884C;&#x4E8C;&#x7EA7;&#x7F13;&#x5B58; select&#x6807;&#x7B7E;&#x9ED8;&#x8BA4;&#x662F;true &#x4E5F;&#x53EF;&#x7528;useCache="false"&#x914D;&#x7F6E;&#x4E0D;&#x8FDB;&#x884C;&#x7F13;&#x5B58;
        if (ms.isUseCache() && resultHandler == null) {
            // &#x5B58;&#x50A8;&#x8FC7;&#x7A0B;&#x76F8;&#x5173;&#x6821;&#x9A8C;
            ensureNoOutParams(ms, boundSql);
            @SuppressWarnings("unchecked")
            // &#x6839;&#x636E;cacheKey&#x4ECE;&#x7F13;&#x5B58;&#x4E2D;&#x53D6;&#x6570;&#x636E;
            List<e> list = (List<e>) tcm.getObject(cache, key);
            if (list == null) {
                // &#x53D6;&#x4E0D;&#x5230;&#x6570;&#x636E; &#x5219;&#x8FDB;&#x884C;&#x67E5;&#x8BE2;
                list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                // &#x5C06;&#x7ED3;&#x679C;&#x5B58;&#x8FDB;&#x4E8C;&#x7EA7;&#x7F13;&#x5B58;
                tcm.putObject(cache, key, list); // issue #578 and #116
            }
            return list;
        }
    }
    // &#x82E5;&#x6CA1;&#x6709;&#x914D;&#x7F6E;&#x4E8C;&#x7EA7;&#x7F13;&#x5B58; &#x5B9E;&#x9645;&#x4E0A;&#x5C31;&#x76F4;&#x63A5;&#x6267;&#x884C;BaseExecutor&#x4E2D;&#x7684;query&#x65B9;&#x6CD5;
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}</e></e></e></e>
// &#x4E0A;&#x8FF0;tcm.putObject(cache, key, list);
&#x5B9E;&#x9645;&#x4E0A;&#x8C03;&#x7528;&#x4E86;TransactionalCache&#x7684;putObject&#x65B9;&#x6CD5;,&#x5C06;&#x7ED3;&#x679C;&#x653E;&#x8FDB;entriesToAddOnCommit&#x8FD9;&#x4E2A;map&#x4E2D;,&#x5E76;&#x6CA1;&#x6709;&#x7F13;&#x5B58;&#x5230;delegate&#x4E2D;
&#x53EA;&#x6709;&#x8C03;&#x7528;sqlSession&#x7684;commit&#x6216;close&#x65B9;&#x6CD5; &#x624D;&#x4F1A;&#x5C06;&#x7ED3;&#x679C;&#x5B58;&#x8FDB;&#x7F13;&#x5B58;(&#x901A;&#x8FC7;flushPendingEntries&#x65B9;&#x6CD5;)
public class TransactionalCache implements Cache {

  private static final Log log = LogFactory.getLog(TransactionalCache.class);

  // &#x59D4;&#x6258;&#x5BF9;&#x8C61; &#x771F;&#x5B9E;&#x7684;&#x7F13;&#x5B58; &#x6700;&#x540E;&#x7ED3;&#x679C;&#x5C06;&#x5B58;&#x8FDB;&#x8FD9;&#x4E2A;&#x7F13;&#x5B58;
  private final Cache delegate;
  // &#x82E5;&#x4E3A;true&#xFF0C;&#x5219;commit&#x7684;&#x65F6;&#x5019;&#x4F1A;&#x5C06;delegate&#x6E05;&#x7A7A;
  private boolean clearOnCommit;
  // &#x5B58;&#x50A8;commit&#x65F6;&#x9700;&#x8981;&#x653E;&#x8FDB;&#x7F13;&#x5B58;&#x7684;&#x5BF9;&#x8C61;
  private final Map<object, object> entriesToAddOnCommit;
  // &#x5B58;&#x50A8;&#x8C03;&#x7528;getObject()&#x83B7;&#x53D6;&#x4E0D;&#x5230;&#x7684;key
  private final Set<object> entriesMissedInCache;

  public TransactionalCache(Cache delegate) {
    this.delegate = delegate;
    this.clearOnCommit = false;
    this.entriesToAddOnCommit = new HashMap<>();
    this.entriesMissedInCache = new HashSet<>();
  }

  @Override
  public String getId() {
    return delegate.getId();
  }

  @Override
  public int getSize() {
    return delegate.getSize();
  }

  @Override
  public Object getObject(Object key) {
    // issue #116
    Object object = delegate.getObject(key);
    if (object == null) {
      entriesMissedInCache.add(key);
    }
    // issue #146
    if (clearOnCommit) {
      return null;
    } else {
      return object;
    }
  }

  @Override
  public void putObject(Object key, Object object) {
    entriesToAddOnCommit.put(key, object);
  }

  @Override
  public Object removeObject(Object key) {
    return null;
  }

  @Override
  public void clear() {
    clearOnCommit = true;
    entriesToAddOnCommit.clear();
  }

  public void commit() {
    if (clearOnCommit) {
      delegate.clear();
    }
    flushPendingEntries();
    reset();
  }

  public void rollback() {
    unlockMissedEntries();
    reset();
  }

  private void reset() {
    clearOnCommit = false;
    entriesToAddOnCommit.clear();
    entriesMissedInCache.clear();
  }

  private void flushPendingEntries() {
    // &#x904D;&#x5386;entriesToAddOnCommit&#xFF0C;&#x5C06;&#x7ED3;&#x679C;&#x7F13;&#x5B58;
    for (Map.Entry<object, object> entry : entriesToAddOnCommit.entrySet()) {
      delegate.putObject(entry.getKey(), entry.getValue());
    }
    for (Object entry : entriesMissedInCache) {
      if (!entriesToAddOnCommit.containsKey(entry)) {
        delegate.putObject(entry, null);
      }
    }
  }

  private void unlockMissedEntries() {
    for (Object entry : entriesMissedInCache) {
      try {
        delegate.removeObject(entry);
      } catch (Exception e) {
        log.warn("Unexpected exception while notifiying a rollback to the cache adapter. "
            + "Consider upgrading your cache adapter to the latest version. Cause: " + e);
      }
    }
  }

}</object,></object></object,>

测试代码

  public void testCache2() {
    SqlSession sqlSession = null;
    try {
      // &#x5F00;&#x542F;&#x4E00;&#x4E2A;sqlSession
      sqlSession = sqlSessionFactory.openSession();
      StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
      Student student1 = studentMapper.getStudentById(1);
      sqlSession.close();
      log.info("&#x5F00;&#x542F;&#x65B0;&#x7684;session");
      sqlSession = sqlSessionFactory.openSession();
      studentMapper = sqlSession.getMapper(StudentMapper.class);
      Student student2 = studentMapper.getStudentById(1);
      Student student3 = studentMapper.getStudentById(1);
      Student student4 = studentMapper.getStudentById(1);
      log.info("student1 == student2, result:" + (student1 == student2));
      log.info("student1 == student3, result:" + (student1 == student3));
      log.info("student1 == student4, result:" + (student1 == student4));
    } finally {
      sqlSession.close();
    }
  }

结果输出

可以看到后三次的查询都命中了缓存,没有再去查找数据库。另外,查询出来的结果都不是同一个对象,事实上缓存的时候SerializedCache这个缓存 ,所有二级缓存的对象需要实现Serializable接口,后面存储的其实是对象的字节数组,这样反序列化出来的时候就不是同一个对象了。

若某个查询不想使用二级缓存,也可以加上flushCache=”true”这个配置,同一级缓存一样

#SerializedCache

@Override
public void putObject(Object key, Object object) {
    if (object == null || object instanceof Serializable) {
      delegate.putObject(key, serialize((Serializable) object));
    } else {
      throw new CacheException("SharedCache failed to make a copy of a non-serializable object: " + object);
    }
}

private byte[] serialize(Serializable value) {
    try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos)) {
      oos.writeObject(value);
      oos.flush();
      return bos.toByteArray();
    } catch (Exception e) {
      throw new CacheException("Error serializing object.  Cause: " + e, e);
    }
  }

最后

无论是一级缓存还是二级缓存再调用update()方法后都会clear清空缓存,使缓存失效。实际开发使用中mybatis缓存的坑还是很多的,需要谨慎小心。

Original: https://www.cnblogs.com/monianxd/p/16455915.html
Author: 默念x
Title: mybatis缓存

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/620920/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Mysql Date操作

    根据format字符串格式化date值。 下列修饰符可以被用在format字符串中: %W 星期名字(Sunday……Saturday) %D 有英语前缀的月份的日期(1s…

    数据库 2023年6月14日
    082
  • java企业官网源码 自适应响应式 freemarker 静态引擎 模块设计方案

    系统设计: 1.网站后台采用主流的 SSM 框架 jsp JSTL,网站后台采用freemaker静态化模版引擎生成html 2.因为是生成的html,所以访问速度快,轻便,对服务…

    数据库 2023年6月6日
    0290
  • Java编程作业

    1、编程题 设计一个用户类User,类中的变量有用户名、密码和记录用户数量的变量,定义3个构造方法:无参的、为用户名赋值的、为用户名和密码赋值的,还有获取和设置密码的方法和返回类信…

    数据库 2023年6月11日
    083
  • [Mysql]如何查看初次安装后的默认密码

    mysql初次安装时,会设置一个临时密码,不允许用空密码直接登录: ubuntu系统上这个密码的存放位置是 /etc/mysql/debian.cnf Original: http…

    数据库 2023年6月16日
    090
  • leetcode 437. Path Sum III 路径总和 III(中等)

    一、题目大意 给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始,也…

    数据库 2023年6月16日
    0102
  • LeetCode 344. 反转字符串

    编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 //输入一个字符串,输出它的倒序字符串 input: Hello output: olleH …

    数据库 2023年6月11日
    085
  • 设计模式——单例模式

    引言 今天来谈谈设计模式中的单例模式,温故知新,以免生疏。 软件设计领域的四位世界级大师Gang Of Four (GoF):Erich Gamma,Richard Helm,Ra…

    数据库 2023年6月16日
    0101
  • Linux 磁盘挂载

    具体操作是: 1.先对磁盘进行格式化; 挂载磁盘到需要的挂载点; 3.添加分区启动表 1、首先查看系统中磁盘信息 命令为:fdisk -l 2、格式化要挂载的磁盘 格式化命令为:m…

    数据库 2023年6月14日
    0153
  • 运行jar包使用外部依赖

    nohup java -Dloader.path=”lib/” -Dfile.encoding=utf-8 -jar test.jar > test….

    数据库 2023年6月9日
    061
  • 在浏览器中Django项目的静态文件打不开的一个原因

    2022-09-27 问题描述: 编写Django代码时,设置了一个”static”文件夹,在里面放置了一张图片。在”setting&#8221…

    数据库 2023年6月14日
    0102
  • Spark知识点总结

    Spark基础 Spark优势 优秀的数据模型与丰富计算抽象 Spark 借鉴了 MapReduce 思想发展而来,保留了其分布式并行计算的优点并改进了其明显的缺陷。 让中间数据存…

    数据库 2023年6月6日
    078
  • 适用于顺序磁盘访问的1分钟法则

    预备知识梳理 本文中设定 block size 与 page size 大小相等。 什么是 Block 文章的开始先解释一下,磁盘的数据读写是以扇区 (sector) 为单位的,而…

    数据库 2023年5月24日
    090
  • liquibase新增字段注释导致表格注释同时变更bug记录

    liquibase是一个用于数据库变更跟踪、版本管理和自动部署的开源工具。它的使用方式方法可以参考官方文档或者其他人的博客,这里不做过多介绍。 1. 问题复现 在使用过程中发现了一…

    数据库 2023年6月14日
    0113
  • hosts文件作用

    1、加快域名解析对于要经常访问的网站,我们可以通过在Hosts中配置域名和IP的映射关系,提高域名解析速度。由于有了映射关系,当我们输入域名计算机就能很快解析出IP,而不用请求网络…

    数据库 2023年6月11日
    065
  • flowable 从zip压缩包 部署流程定义

    /**部署流程定义(从zip压缩包) * @param name //部署名称 * @param zippath //zip文件路径 * @return 部署ID * @from …

    数据库 2023年6月6日
    090
  • loadrunner 无法保存许可信息

    1.CONFUGURATION—>loadrunner license—>New License页面,输入许可信息,提示:无法保存许可信息 2.解决方法,使用管理员角色…

    数据库 2023年6月14日
    0106
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球