什么是缓存
缓存就是内存中的一个对象,用于对数据库查询结果的保存,用于减少与数据库的交互次数从而降低数据库的压力,进而提高响应速度。
什么是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 的内部都会有一个一级缓存对象。
在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的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)); }
输出:
可以看到,上面代码执行了三条相同的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 的代码了,就是一条简单的插入语句。
分别放开不同的更新语句,发现执行效果如下
输出结果:
如图所示,在两次查询语句中使用插入,会对一级缓存进行刷新,会导致一级缓存失效。
探究不同的 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); }
输出:
上面代码使用了不同的 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); }
输出结果
我们在两次查询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); }
输出:
我们在两次查询操作之间,使用了 sqlSession 的 clearCache() 方法清除了一级缓存,所以使用 clearCache 也会对一级缓存产生影响。