目录
MyBatis 缓存是 MyBatis 中的一个重要特性,用于提高数据库查询的性能。MyBatis 提供了一级缓存和二级缓存两种类型的缓存机制。
一级缓存:一级缓存(本地缓存)是 MyBatis 中默认的缓存机制,它是 SqlSession 级别的缓存。当使用 SqlSession 执行一次查询时,查询到的结果集会存储在 SqlSession 的缓存中。当使用 SqlSession 再次执行相同的查询时,它会首先从缓存中获取结果,而不会再次查询数据库。一级缓存是基于事务的,当 SqlSession flush(插入、更新、修改) 或 close 后,该 SqlSession 中的所有 Cache 将被清空
二级缓存:二级缓存是 Mapper 级别的缓存,也称为 namespace 级别。这意味着无论通过哪个 SqlSession 执行相同的 Mapper 接口操作,都可以访问到相同的二级缓存数据,即跨 SqlSession 共享同一 Mapper 接口的数据。二级缓存需要手动配置,它可以实现更大范围的数据共享和更高效的查询性能。二级缓存也可以配置为使用不同的缓存实现,如 Ehcache、Redis 等
MyBatis 缓存的主要作用是减少对数据库的访问次数,提高查询性能。然而,需要注意的是,缓存也会占用内存空间,如果缓存的数据量过大,可能会导致内存溢出的问题。因此,在使用 MyBatis 缓存时,需要根据具体的应用场景和数据量进行合理的配置和调优。
一级缓存 (local cache),即本地缓存,作用域默认为 SqlSession 。本地缓存不能被关闭,当 SqlSession flush(插入、更新、修改) 或 close 后,该 SqlSession 中的所有 Cache 将被清空。此外,也可以调用 sqlSession.clearCache() 来手动清空本地缓存。
在 MyBatis 配置文件中可以通过设置 localCacheScope 参数来改变一级缓存的作用域,它有以下两种设置:
- @Test
- public void selectById1() {
- EmployeeMapper mapper = sqlSessionOne.getMapper(EmployeeMapper.class);
-
- // 第一次查询
- System.out.println("第一次查询");
- Employee employee1 = mapper.selectEmployeeById(1);
- System.out.println(employee1);
-
- // 第二次查询
- System.out.println("第二次查询");
- Employee employee2 = mapper.selectEmployeeById(1);
- System.out.println(employee2);
-
- // 手动清除本地缓存
- System.out.println("清除本地缓存");
- sqlSessionOne.clearCache();
-
- // 第三次查询
- System.out.println("第三次查询");
- Employee employee3 = mapper.selectEmployeeById(1);
- System.out.println(employee3);
-
- }
第一次查询,从数据库中查询数据并将查询结果保存在本地缓存中
第二次查询,直接获取本地缓存的查询结果
清空本地缓存
第三次查询,从数据库中查询数据
MyBatis 的二级缓存是一个更为全局的缓存机制,其作用范围是 namespace 级别,可以被多个 SqlSession 共享。它的生命周期与应用程序同步,主要用于解决一级缓存不能跨会话共享的问题。
以下是二级缓存的一些详细介绍:
在 MyBatis 配置文件进行设置
- <settings>
- <setting name="cacheEnabled" value="true"/>
- settings>
在 SQL 映射文件中设置
在
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
注:在 Mapper 接口使用 @CacheNamespace 注解也可以开启二级缓存
实体类实现 java.io.Serializable 接口
- @Test
- public void selectAll() {
- // 不同 sqlSession 对同一 mapper 接口进行操作
- SqlSession sqlSession1 = sqlSessionFactory.openSession();
- EmployeeMapper mapper1 = sqlSession1.getMapper(EmployeeMapper.class);
- SqlSession sqlSession2 = sqlSessionFactory.openSession();
- EmployeeMapper mapper2 = sqlSession2.getMapper(EmployeeMapper.class);
-
- // 通过 sqlSession1 进行第一次查询
- List
employees1 = mapper1.selectAllEmployee(); - for (Employee employee : employees1) {
- System.out.println(employee);
- }
-
- // 当 sqlsession 提交、关闭时,二级缓存会有数据
- //sqlSession1.commit();
- sqlSession1.clearCache();
- sqlSession1.close();
-
- // 通过 sqlSession2 进行第二次查询
- List
employees2 = mapper2.selectAllEmployee(); - for (Employee employee : employees2) {
- System.out.println(employee);
- }
- }
通过 sqlSession1 进行第一次查询,二级缓存没有数据
清空 sqlSession1 的一级缓存
sqlSession1 关闭(二级缓存有查询结果,二级缓存的数据是从一级缓存中来的吗???)
通过 sqlSession2 进行第二次查询,从二级缓存中获得查询结果,二级缓存命中率 0.5
补充:
禁用二级缓存
可以使用 useCache 属性(这个属性只有 select 有)来禁用二级缓存,默认为 true,即使用二级缓存,当然也可以通过 @Options(useCache=true) 来设置。
清空一级和二级缓存
flushCache 属性是用来清空一级缓存和二级缓存的。select 中默认为 flushCache=false,insert、delete、update 中默认为 flushCache=true,因为执行数据的增删改 SQL 语句后,数据库与缓存数据可能已经不一致,此时缓存已经不能用了,如果不清空缓存则可能出现脏读的情况。也可以通过 @Options(flushCache=options.FlushCachePolicy.TRUE) 进行设置。
也可以通过实现 cache 接口自定义二级缓存
尚硅谷文档