【MybBatis高级篇】MyBatis 缓存机制
简介
MyBatis
包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存。一级缓存和二级缓存。
- 默认情况下,只有一级缓存(
SqlSession
级别的缓存,也称为本地缓存)开启。 - 二级缓存需要手动开启和配置,他是基于
namespace
级别的缓存。 - 为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
一级缓存
1、一级缓存(local cache), 即本地缓存, 作用域默认为sqlSession
。当 Session flush 或 close 后, 该Session 中的所有 Cache 将被清空。
2、本地缓存不能被关闭, 但可以调用 clearCache()
来清空本地缓存, 或者改变缓存的作用域.
一级缓存演示
同一次会话期间只要查询过的数据都会保存在当前SqlSession
的一个Map中
pom
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.20</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
yml
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/ry?serverTimezone=GMT%2B8
driver-class-name: com.mysql.cj.jdbc.Driver
#配置分页插件
pagehelper:
helperDialect: mysql #设置数据库类型
reasonable: true #开启合理化:页码<=0 查询第一页,页码>=总页数查询最后一页
supportMethodsArguments: true #支持通过 Mapper 接口参数来传递分页参数
Mapper类
public interface SysConfigMapper {
@Select("select * from sys_conifg")
List<Map> selectList();
}
启动类
@MapperScan(basePackages = "cn.zysheep.mapper")
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
test类
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@Slf4j
public class SysConfigMapperTest {
@Autowired
SysConfigMapper sysConfigMapper;
@Autowired
SqlSessionFactory sqlSessionFactory;
@Test
public void testSelectList() {
List<Map> maps1 = sysConfigMapper.selectList();
List<Map> maps2 = sysConfigMapper.selectList();
log.info("maps1: {}:", maps1);
log.info("maps2: {}:", maps2);
}
}
查询了两次,并没有走一级缓存,此时非常疑惑,不应该吖,mybatis不是默认开启一级缓存了吗?
原因: 因为使用sysConfigMapper代理对象查询是属于namespace级别,也就是二级缓存才能生效。后面使用二级缓存在解决该问题,那如何才能使用一级缓存呢,我们可以注入SqlSessionFactory 对象调用openSession()方法从而得到SqlSession手动执行sql语句
修改测试类代码
@Test
public void testExecuteSqlSession() {
SqlSession sqlSession = sqlSessionFactory.openSession();
SysConfigMapper mapper = sqlSession.getMapper(SysConfigMapper.class);
List<Map> maps1 = mapper.selectList();
List<Map> maps2 = mapper.selectList();
log.info("maps1: {}:", maps1);
log.info("maps2: {}:", maps2);
}
测试一级缓存失效: 得到两个不同的SqlSession ,执行对应的查询语句,未命中索引。需要使用二级缓存可以解决这个问题
@Test
public void testExecuteSqlSession() {
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SysConfigMapper mapper1 = sqlSession1.getMapper(SysConfigMapper.class);
List<Map> maps1 = mapper1.selectList();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SysConfigMapper mapper2 = sqlSession2.getMapper(SysConfigMapper.class);
List<Map> maps2 = mapper2.selectList();
log.info("maps1: {}:", maps1);
log.info("maps2: {}:", maps2);
}
二级缓存
1、二级缓存(second level cache),全局作用域缓存
2、二级缓存默认不开启,需要手动配置
3、二级缓存在 SqlSession 关闭或提交之后才会生效
4、MyBatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口
使用步骤
原始SSM XML方式
1、全局配置文件mybatis-config.xml
中开启二级缓存
<setting name="cacheEnabled" value="true"/>
2、需要使用二级缓存的映射文件****Mapper.xml
处使用cache配置缓存
<cache />
3、注意:POJO需要实现Serializable接口
SpringBoot集成MyBatis注解方式
1、yml中开启二级缓存
mybatis:
configuration:
cache-enabled: true
2、Mapper层接口使用@CacheNamespace
注解
@CacheNamespace(blocking = true)
public interface SysConfigMapper {
@Select("select * from sys_config")
List<Map> selectList();
}
基本上就是这样。这个简单语句的效果如下:
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
缓存相关属性
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
1、 eviction=“FIFO”:缓存回收策略。默认的是 LRU。
- LRU – 最近最少使用的:移除最长时间不被使用的对象。
- FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
- SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
- WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
2、flushInterval:刷新间隔,单位毫秒。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
3、size:引用数目,正整数,代表缓存最多可以存储多少个对象,太大容易导致内存溢出
4、readOnly:只读,true/false
- true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
- false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。
二级缓存演示
1、和一级缓存失效代码一样,只需要开启二级缓存,并且关闭 SqlSession
@Test
public void testSecondLevelCache() {
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SysConfigMapper mapper1 = sqlSession1.getMapper(SysConfigMapper.class);
List<Map> maps1 = mapper1.selectList();
// 关闭sqlSession1
sqlSession1.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SysConfigMapper mapper2 = sqlSession2.getMapper(SysConfigMapper.class);
List<Map> maps2 = mapper2.selectList();
log.info("maps1: {}:", maps1);
log.info("maps2: {}:", maps2);
// 关闭sqlSession2
sqlSession2.close();
}
2、测试SpringBoot整合MyBatis基于接口SysConfigMapper注入的方式
@Test
public void testSysConfigMapper() {
List<Map> maps1 = sysConfigMapper.selectList();
List<Map> maps2 = sysConfigMapper.selectList();
log.info("maps1: {}:", maps1);
log.info("maps2: {}:", maps2);
}