本文主要介绍在SpringBoot项目中如何使用Mybatis的一级、二级缓存,为了演示方便,本文的数据库采用H2内存数据库,数据库连接池默认使用SpringBoot2.X自带的hikariCP。

正确的使用Mybatis缓存可以有效减少多余的数据库查询操作,节约IO。

接下来我们从实践出发,看一看mybatis的一级,二级缓存如何使用,相关代码请查阅:https://github.com/zhengxl5566/springboot-demo.git

1、概念介绍

  • 什么是一级缓存

    在日常开发过程中,经常会有相同的sql执行多次查询的情况,mybatis提供了一级缓存来优化这些查询,避免多次请求数据库。

    一级缓存在mybatis中默认是开启的并且是session级别,它的作用域为一次sqlSession会话。
  • 什么是二级缓存

    相对于一级缓存,二级缓存的作用域更广泛,它不止局限于一个sqlSession,可以在多个sqlSession之间共享,事实上,它的作用域是namespace。

    mybatis的二级缓存默认也是开启的,但由于他的作用域是namespace,所以还需要在mapper.xml中开启才能生效
  • 缓存的优先级

    通过mybatis发起的查询,作用顺序为:二级缓存->一级缓存->数据库 ,其中任何一个环节查到不为空的数据,都将直接返回结果
  • 缓存失效

    当在一个缓存作用域中发生了update、insert、delete 动作后,将会触发缓存失效,下一次查询将命中数据库,从而保证不会查到脏数据。

2、代码演示

一级缓存

默认情况下,mybatis开启并使用了一级缓存。

单元测试用例:

    /**
* 开启事务,测试一级缓存效果
* 缓存命中顺序:二级缓存---> 一级缓存---> 数据库
**/
@Test
@Transactional(rollbackFor = Throwable.class)
public void testFistCache(){
// 第一次查询,缓存到一级缓存
userMapper.selectById(1);
// 第二次查询,直接读取一级缓存
userMapper.selectById(1); }

执行结果:

可以看到,虽然进行了两次查询,但最终只请求了一次数据库,第二次查询命中了一级缓存,直接返回了数据。

这里有两点需要说明一下:

1、为什么开启事务

由于使用了数据库连接池,默认每次查询完之后自动commite,这就导致两次查询使用的不是同一个sqlSessioin,根据一级缓存的原理,它将永远不会生效。

当我们开启了事务,两次查询都在同一个sqlSession中,从而让第二次查询命中了一级缓存。读者可以自行关闭事务验证此结论。

2、两种一级缓存模式

一级缓存的作用域有两种:session(默认)和statment,可通过设置local-cache-scope 的值来切换,默认为session。

二者的区别在于session会将缓存作用于同一个sqlSesson,而statment仅针对一次查询,所以,local-cache-scope: statment可以理解为关闭一级缓存。

二级缓存

默认情况下,mybatis打开了二级缓存,但它并未生效,因为二级缓存的作用域是namespace,所以还需要在Mapper.xml文件中配置一下才能使二级缓存生效

  • 单表二级缓存

    下面对userMapper.xml配置一下,让其二级缓存生效,只需加入cache标签即可
<cache></cache>

单元测试用例:

    /**
* 测试二级缓存效果
* 需要*Mapper.xml开启二级缓存
**/
@Test
public void testSecondCache(){
userMapper.selectById(1);
userMapper.selectById(1);
}

执行结果:



这里可以看到,第二次查询直接命中了缓存,日志还打印了该缓存的命中率。读者可以自行关闭二级缓存查看效果,通过注掉对应mapper.xml的cache标签,或者 cache-enabled: false 均可

  • 多表联查二级缓存

    接下来演示多表联查的二级缓存,user表left join user_order表 on user.id = user_order.user_id

    我们考虑这样一种情况,该联查执行两次,第二次联查前更新user_order表,如果只使用cache配置,将会查不到更新的user_orderxi,因为两个mapper.xml的作用域不同,要想合到一个作用域,就需要用到cache-ref

    userOrderMapper.xml
<cache></cache>

userMapper.xml

<cache-ref namespace="com.zhengxl.mybatiscache.mapper.UserOrderMapper"/>

单元测试用例:

    /**
* 测试多表联查的二级缓存效果
* 需要*Mapper.xml设定引用空间
**/
@Test
public void testJoin(){
System.out.println(userMapper.selectJoin());
System.out.println(userMapper.selectJoin());
UserOrder userOrder = new UserOrder();
userOrder.setGoodName("myGoods");
userOrder.setUserId(1);
userOrderMapper.saveOrder(userOrder);
System.out.println(userMapper.selectJoin()); }

执行结果:

首先查询了两次user表,第二次命中二级缓存,然后更新user_order表,使缓存失效,第三次查询时命中数据库。

综上,mybatis的单机缓存就介绍完了,读者可以自行下载样例工程验证。

总结:

mybatis默认的session级别一级缓存,由于springboot中默认使用了hikariCP,所以基本没用,需要开启事务才有用。但一级缓存作用域仅限同一sqlSession内,无法感知到其他sqlSession的增删改,所以极易产生脏数据

二级缓存可通过cache-ref让多个mapper.xml共享同一namespace,从而实现缓存共享,但多表联查时配置略微繁琐。

所以生产环境建议将一级缓存设置为statment级别(即关闭一级缓存),如果有必要,可以开启二级缓存

注意:如果应用是分布式部署,由于二级缓存存储在本地,必然导致查询出脏数据,所以,分布式部署的应用不建议开启。

参考资料:


欢迎扫码关注我的个人公众号,获取最新文章↓

SpringBoot+Mybatis一级缓存和二级缓存详解的更多相关文章

  1. springboot+mybatis 用redis作二级缓存

    1.加入相关依赖包: <?xml version="1.0" encoding="UTF-8"?> <project xmlns=" ...

  2. MyBatis 一级缓存、二级缓存全详解(一)

    目录 MyBatis 一级缓存.二级缓存全详解(一) 什么是缓存 什么是MyBatis中的缓存 MyBatis 中的一级缓存 初探一级缓存 探究一级缓存是如何失效的 一级缓存原理探究 还有其他要补充的 ...

  3. Mybatis一级缓存、二级缓存详讲

    Mybatis 一级缓存.二级缓存 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 查询缓存 首先,我们先看一下这个标题“查询缓存”,那就说明跟增.删.改是没有任何关联的,只有在查询 ...

  4. Mybatis 一级缓存和二级缓存原理区别 (图文详解)

    Java面试经常问到Mybatis一级缓存和二级缓存,今天就给大家重点详解Mybatis一级缓存和二级缓存原理与区别@mikechen Mybatis缓存 缓存就是内存中的数据,常常来自对数据库查询结 ...

  5. mybatis 详解(九)------ 一级缓存、二级缓存

    上一章节,我们讲解了通过mybatis的懒加载来提高查询效率,那么除了懒加载,还有什么方法能提高查询效率呢?这就是我们本章讲的缓存. mybatis 为我们提供了一级缓存和二级缓存,可以通过下图来理解 ...

  6. mybatis基础系列(四)——关联查询、延迟加载、一级缓存与二级缓存

    关本文是Mybatis基础系列的第四篇文章,点击下面链接可以查看前面的文章: mybatis基础系列(三)——动态sql mybatis基础系列(二)——基础语法.别名.输入映射.输出映射 mybat ...

  7. mybatis一级缓存和二级缓存(一)

    一级缓存: 就是Session级别的缓存.一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中. 如果短时间内这个session(一定要同一个session)又做了同一个操作,那么h ...

  8. Mybatis进阶使用-一级缓存与二级缓存

    简介 缓存是一般的ORM 框架都会提供的功能,目的就是提升查询的效率和减少数据库的压力.跟Hibernate 一样,MyBatis 也有一级缓存和二级缓存,并且预留了集成第三方缓存的接口. 一级缓存 ...

  9. [原创]关于mybatis中一级缓存和二级缓存的简单介绍

    关于mybatis中一级缓存和二级缓存的简单介绍 mybatis的一级缓存: MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候 ...

随机推荐

  1. pytorch入门2.2构建回归模型初体验(开始训练)

    pytorch入门2.x构建回归模型系列: pytorch入门2.0构建回归模型初体验(数据生成) pytorch入门2.1构建回归模型初体验(模型构建) pytorch入门2.2构建回归模型初体验( ...

  2. 8、react 高阶组件

    1.高阶组件:封装 高阶组件使用得是react得一种模式,增强现有组件得功能 一个高阶组件就是一个函数,这个函数接收得是组件类作为参数得,并且返回得是一个新组件,再返回得新组件中有输入参数组件不具备得 ...

  3. 【LGR-072】回首过去

    题目   点这里看题目. 分析   可以发现,符合条件的分数约分后,其分母必须为\(2^m5^k\).因此,原分数一定可以表示为: \[\frac{XY}{2^m5^kX} \]   其中\((10, ...

  4. 2019-02-02 Python学习——生成器杨辉三角,迭代器与可迭代对象的区别

    练习 杨辉三角定义如下: 1 / \ 1 1 / \ / \ 1 2 1 / \ / \ / \ 1 3 3 1 / \ / \ / \ / \ 1 4 6 4 1 / \ / \ / \ / \ / ...

  5. (七)ExtentReports测试报告的使用

    原文链接:https://www.jianshu.com/p/4cd9e92d5edf 1.简介 ExtentReports用于生成测试报告,其不光漂亮而且使用简单,并可以定制相应的样式. 2.使用: ...

  6. os.remove() 删除文件

    概述 os.remove() 方法用于删除指定路径的文件.如果指定的路径是一个目录,将抛出OSError. 在Unix, Windows中有效 语法 remove()方法语法格式如下: os.remo ...

  7. TCP和UDP的Socket编程实验

    Linux Socket 函数库是从 Berkeley 大学开发的 BSD UNIX 系统中移植过来的.BSD Socket 接口是在众多 Unix 系统中被广泛支持的 TCP/IP 通信接口,Lin ...

  8. laravel向视图传递变量

    向视图中传递变量 我们在开发web应用当中,通常都不是为了写静态页面而生的,我们需要跟数据打交道,那么这个时候,问题就来了,在一个MVC的框架中,怎么将数据传给视图呢?比如我们要在 ArticleCo ...

  9. 鼠标悬停,使用css切换图片

    鼠标悬停,使用css切换图片 当鼠标悬停在li上面切换另一张图片,只需添加下述css样式即可

  10. Java多线程与并发基础

    CS-LogN思维导图:记录专业基础 面试题 开源地址:https://github.com/FISHers6/CS-LogN 多线程与并发基础 实现多线程 面试题1:有几种实现线程的方法,分别是什么 ...