Mybatis提供了一级缓存和二级缓存,本文介绍如何去了解这两个缓存的概念,具体使用不做评论。
- 一级缓存:针对的是
SqlSession
,当SqlSession
没有关闭时,该会话中相同的操作会被缓存。 - 二级缓存:针对的是命名空间,需要手动声明
<cache/>
开启二级缓存,二级缓存是当SqlSession
关闭后,会将一级缓存的内容放置到二级缓存中。
一级缓存是默认开启的
我们先看配置文件中的一个标签:
<settings>
<setting name="localCacheScope" value="SESSION"/>
</settings>
在Mybatis配置文件中的<settings>
标签中有这样一个name
子标签localCacheScope
,它说明了一级缓存默认是针对Session
的:
子标签名称 | 描述 | 取值 | 默认 | |
---|---|---|---|---|
localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。 | SESSION / STATEMENT | SESSION |
实验
- 这个
setting
我们不配置,采用Mybatis默认的即可 - 我们通过
sqlSessionFactory
获得两个不同的sqlSession
- 通过
sqlSession
连续操作两次同样的操作,去判断其值是否相同
// 获得两个不同的Session
SqlSession sqlSession = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
DepartmentMapper departmentMapper = sqlSession.getMapper(DepartmentMapper.class);
DepartmentMapper departmentMapper2 = sqlSession2.getMapper(DepartmentMapper.class);
Department department = departmentMapper.findById("18ec781fbefd727923b0d35740b177ab");
Department department2 = departmentMapper.findById("18ec781fbefd727923b0d35740b177ab");
System.out.println("department == department2 : " + (department == department2));
Department department3 = departmentMapper2.findById("18ec781fbefd727923b0d35740b177ab");
Department department4 = departmentMapper2.findById("18ec781fbefd727923b0d35740b177ab");
System.out.println("department3 == department4 : " + (department3 == department4));
输出结果如下,如果我们使用LOG模式打印的话,也可以看到两个sqlSession
做的不同的findByID
操作仅仅发送给给了两次sql
,表示一级默认缓存是起作用的:
department == department2 : true
department3 == department4 : true
二级缓存的开启
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。要启用全局的二级缓存,很简单,只需要在对应的映射mapper
文件中声明标签<cache/>
。
但是请注意两个点:
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
- 二级缓存(命名空间的缓存)是由一级缓存提供的,这个提供动作是
sqlSession.close()
。
SqlSession sqlSession = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
DepartmentMapper departmentMapper = sqlSession.getMapper(DepartmentMapper.class);
DepartmentMapper departmentMapper2 = sqlSession2.getMapper(DepartmentMapper.class);
Department department = departmentMapper.findById("18ec781fbefd727923b0d35740b177ab");
Department department2 = departmentMapper.findById("18ec781fbefd727923b0d35740b177ab");
System.out.println("department == department2 : " + (department == department2));
sqlSession.close(); // 注意这里!!
Department department3 = departmentMapper2.findById("18ec781fbefd727923b0d35740b177ab");
Department department4 = departmentMapper2.findById("18ec781fbefd727923b0d35740b177ab");
System.out.println("department3 == department4 : " + (department3 == department4));
注意看我们添加的sqlSession.close();
它将sqlSession
的一级缓存推送到了二级缓存,那么如果我在命名空间DepartmentMapper.xml
中执行的操作,都会在<cache/>
中被保存下来,由于department
和department2
使用的是一级缓存,所以它们还是相等的。而department3
和department4
使用的是二级缓存得到的结果,所以它们并不相等,还记得我们说的第一个注意点吗,回去看看。二级缓存的效果就是,它也不会发送sql
语句,能直接命中结果。
输出结果如下:
department == department2 : true
department3 == department4 : false