MyBatis一级缓存详解(一)

简介: 缓存就是内存中的一个对象,用于对数据库查询结果的保存,用于减少与数据库的交互次数从而降低数据库的压力,进而提高响应速度。

什么是缓存


缓存就是内存中的一个对象,用于对数据库查询结果的保存,用于减少与数据库的交互次数从而降低数据库的压力,进而提高响应速度。

什么是MyBatis中的缓存


MyBatis 中的缓存就是说 MyBatis 在执行一次SQL查询或者SQL更新之后,这条SQL语句并不会消失,而是被MyBatis 缓存起来,当再次执行相同SQL语句的时候,就会直接从缓存中进行提取,而不是再次执行SQL命令。


MyBatis中的缓存分为一级缓存和二级缓存,一级缓存又被称为 SqlSession 级别的缓存,二级缓存又被称为表级缓存。


SqlSession是什么?SqlSession 是SqlSessionFactory会话工厂创建出来的一个会话的对象,这个SqlSession对象用于执行具体的SQL语句并返回给用户请求的结果。


SqlSession级别的缓存是什么意思?SqlSession级别的缓存表示的就是每当执行一条SQL语句后,默认就会把该SQL语句缓存起来,也被称为会话缓存


MyBatis 中的一级缓存


一级缓存是 SqlSession级别 的缓存。在操作数据库时需要构造 sqlSession 对象,在对象中有一个(内存区域)数据结构(HashMap)用于存储缓存数据。不同的 sqlSession 之间的缓存数据区域(HashMap)是互相不影响的。用一张图来表示一下一级缓存,其中每一个 SqlSession 的内部都会有一个一级缓存对象。

54.jpg

在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的SQL,MyBatis 提供了一级缓存的方案优化这部分场景,如果是相同的SQL语句,会优先命中一级缓存,避免直接对数据库进行查询,提高性能。


初探一级缓存


我们继续使用 MyBatis基础搭建以及配置详解中的例子(https://mp.weixin.qq.com/s/Ys03zaTSaOakdGU4RlLJ1A)进行 一级缓存的探究。


在对应的 resources 根目录下加上日志的输出信息  log4j.properties

##define an appender named console
log4j.appender.console=org.apache.log4j.ConsoleAppender
#The Target value is System.out or System.err
log4j.appender.console.Target=System.out
#set the layout type of the apperder
log4j.appender.console.layout=org.apache.log4j.PatternLayout
#set the layout format pattern
log4j.appender.console.layout.ConversionPattern=[%-5p] %m%n
##define a logger
log4j.rootLogger=debug,console

模拟思路既然每个 SqlSession 都会有自己的一个缓存,那么我们用同一个 SqlSession 是不是就能感受到一级缓存的存在呢?调用多次 getMapper 方法,生成对应的SQL语句,判断每次SQL语句是从缓存中取还是对数据库进行操作,下面的例子来证明一下

@Test
public void test(){
  DeptDao deptDao = sqlSession.getMapper(DeptDao.class);
  Dept dept = deptDao.findByDeptNo(1);
  System.out.println(dept);
  DeptDao deptDao2 = sqlSession.getMapper(DeptDao.class);
  Dept dept2 = deptDao2.findByDeptNo(1);
  System.out.println(dept2);
  System.out.println(deptDao2.findByDeptNo(1));
}

输出:

55.jpg


可以看到,上面代码执行了三条相同的SQL语句,但是只有一条SQL语句进行了输出,其他两条SQL语句都是从缓存中查询的,所以它们生成了相同的 Dept 对象。

探究一级缓存是如何失效的


上面的一级缓存初探让我们感受到了 MyBatis 中一级缓存的存在,那么现在你或许就会有疑问了,那么什么时候缓存失效呢?这个问题也就是我们接下来需要详细讨论的议题之一。


探究更新对一级缓存失效的影响


上面的代码执行了三次相同的查询操作,返回了相同的结果,那么,如果我在第一条和第二条SQL语句之前插入更新的SQL语句,是否会对一级缓存产生影响呢?代码如下:

@Test
public void testCacheLose(){
  DeptDao deptDao = sqlSession.getMapper(DeptDao.class);
  Dept dept = deptDao.findByDeptNo(1);
  System.out.println(dept);
  // 在两次查询之间使用 更新 操作,是否会对一级缓存产生影响
  deptDao.insertDept(new Dept(7,"tengxun","shenzhen"));
  //        deptDao.updateDept(new Dept(1,"zhongke","sjz"));
  //        deptDao.deleteByDeptNo(7);
  DeptDao deptDao2 = sqlSession.getMapper(DeptDao.class);
  Dept dept2 = deptDao2.findByDeptNo(1);
  System.out.println(dept2);
}

为了演示效果,就不贴出 insertDept 的代码了,就是一条简单的插入语句。


分别放开不同的更新语句,发现执行效果如下


输出结果:

56.jpg


如图所示,在两次查询语句中使用插入,会对一级缓存进行刷新,会导致一级缓存失效。


探究不同的 SqlSession 对一级缓存的影响


如果你看到这里了,那么你应该知道一级缓存就是 SqlSession 级别的缓存,而同一个 SqlSession 会有相同的一级缓存,那么使用不同的 SqlSession 是不是会对一级缓存产生影响呢?显而易见是的,那么下面就来演示并且证明一下

private SqlSessionFactory factory; // 把factory设置为全局变量
@Test
public void testCacheLoseWithSqlSession(){
  DeptDao deptDao = sqlSession.getMapper(DeptDao.class);
  Dept dept = deptDao.findByDeptNo(1);
  System.out.println(dept);
  SqlSession sqlSession2 = factory.openSession();
  DeptDao deptDao2 = sqlSession2.getMapper(DeptDao.class);
  Dept dept2 = deptDao2.findByDeptNo(1);
  System.out.println(dept2);
}

输出:

57.jpg

上面代码使用了不同的 SqlSession 对同一个SQL语句执行了相同的查询操作,却对数据库执行了两次相同的查询操作,生成了不同的 dept 对象,由此可见,不同的 SqlSession 是肯定会对一级缓存产生影响的。


同一个 SqlSession 使用不同的查询操作


使用不同的查询条件是否会对一级缓存产生影响呢?可能在你心里已经有这个答案了,再来看一下代码吧

@Test
public void testWithDifferentParam(){
  DeptDao deptDao = sqlSession.getMapper(DeptDao.class);
  Dept dept = deptDao.findByDeptNo(1);
  System.out.println(dept);
  DeptDao deptDao2 = sqlSession.getMapper(DeptDao.class);
  Dept dept2 = deptDao2.findByDeptNo(5);
  System.out.println(dept2);
}

输出结果

58.jpg

我们在两次查询SQL分别使用了不同的查询条件,查询出来的数据不一致,那就肯定会对一级缓存产生影响了。


手动清理缓存对一级缓存的影响


我们在两次查询的SQL语句之间使用 clearCache 是否会对一级缓存产生影响呢?下面例子证实了这一点

@Test
public void testClearCache(){
  DeptDao deptDao = sqlSession.getMapper(DeptDao.class);
  Dept dept = deptDao.findByDeptNo(1);
  System.out.println(dept);
  //在两次相同的SQL语句之间使用查询操作,对一级缓存的影响。
  sqlSession.clearCache();
  DeptDao deptDao2 = sqlSession.getMapper(DeptDao.class);
  Dept dept2 = deptDao2.findByDeptNo(1);
  System.out.println(dept2);
}

输出:

59.jpg

我们在两次查询操作之间,使用了 sqlSession 的 clearCache() 方法清除了一级缓存,所以使用 clearCache 也会对一级缓存产生影响。

相关文章
|
8月前
|
缓存 Java 数据库连接
Mybatis缓存相关面试题有多卷
使用 MyBatis 缓存机制需要注意以下几点: 对于频繁更新和变动的数据,不适合使用缓存。 对于数据的一致性要求比较高的场景,不适合使用缓存。 如果配置了二级缓存,需要确保缓存的数据不会影响到其他业务模块的数据。 在使用缓存时,需要注意缓存的命中率和缓存的过期策略,避免缓存过期导致查询性能下降。
125 0
|
4月前
|
缓存 Java 数据库连接
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
文章介绍了MyBatis的缓存机制,包括一级缓存和二级缓存的配置和使用,以及如何整合第三方缓存EHCache。详细解释了一级缓存的生命周期、二级缓存的开启条件和配置属性,以及如何通过ehcache.xml配置文件和logback.xml日志配置文件来实现EHCache的整合。
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
|
1月前
|
缓存 Java 数据库连接
MyBatis缓存机制
MyBatis提供两级缓存机制:一级缓存(Local Cache)默认开启,作用范围为SqlSession,重复查询时直接从缓存读取;二级缓存(Second Level Cache)需手动开启,作用于Mapper级别,支持跨SqlSession共享数据,减少数据库访问,提升性能。
32 1
|
1月前
|
缓存 Java 数据库连接
深入探讨:Spring与MyBatis中的连接池与缓存机制
Spring 与 MyBatis 提供了强大的连接池和缓存机制,通过合理配置和使用这些机制,可以显著提升应用的性能和可扩展性。连接池通过复用数据库连接减少了连接创建和销毁的开销,而 MyBatis 的一级缓存和二级缓存则通过缓存查询结果减少了数据库访问次数。在实际应用中,结合具体的业务需求和系统架构,优化连接池和缓存的配置,是提升系统性能的重要手段。
60 4
|
2月前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
2月前
|
SQL 缓存 Java
MyBatis如何关闭一级缓存(分注解和xml两种方式)
MyBatis如何关闭一级缓存(分注解和xml两种方式)
86 5
|
7月前
|
SQL 缓存 Java
MYBATIS缓存
MYBATIS缓存
|
3月前
|
缓存 Java 数据库连接
使用MyBatis缓存的简单案例
MyBatis 是一种流行的持久层框架,支持自定义 SQL 执行、映射及复杂查询。本文介绍了如何在 Spring Boot 项目中集成 MyBatis 并实现一级和二级缓存,以提高查询性能,减少数据库访问。通过具体的电商系统案例,详细讲解了项目搭建、缓存配置、实体类创建、Mapper 编写、Service 层实现及缓存测试等步骤。
|
6月前
|
SQL 缓存 Java
【面试官】Mybatis缓存有什么问题吗?
面试官:你说下对MyBatis的理解?面试官:那SqlSession知道吧?面试官:Mybatis的缓存有哪几种?面试官:那Mybatis缓存有什么问题吗?面试官:Mybatis分页插件是怎么
【面试官】Mybatis缓存有什么问题吗?
|
6月前
|
缓存 算法 Java
关于MyBatis的缓存详解
MyBatis 的缓存机制非常灵活,可以通过简单的配置来满足不同的性能需求。合理地使用缓存可以显著提高应用程序的性能,尤其是在处理大量数据库查询时。然而,开发者需要注意缓存的一致性和并发问题,特别是在使用可读写缓存时。