在 MyBatis 中,缓存机制是为了提高数据库操作的性能,避免重复查询相同的数据。MyBatis 提供了两种缓存机制:一级缓存和二级缓存。
1. 一级缓存(Local Cache)
定义:
- 一级缓存是 MyBatis 的默认缓存,它在
SqlSession
级别起作用,生命周期与SqlSession
相同。也就是说,同一个SqlSession
对象在多次执行相同的查询时,如果参数相同,第一次查询的结果会被缓存起来,后续的相同查询会直接从缓存中获取数据,而不再发送查询请求到数据库。
特点:
- 默认启用:一级缓存默认是开启的,不需要做额外的配置。
- 作用范围是
SqlSession
:同一个SqlSession
内有效,查询的数据在SqlSession
关闭时缓存也会失效。 - 根据查询参数缓存:如果查询的 SQL 和参数相同,则从缓存中读取结果;否则重新查询数据库。
- 自动管理:MyBatis 自动管理一级缓存,不需要手动设置或操作。
何时清除缓存:
- 每当执行
INSERT
、UPDATE
或DELETE
操作时,一级缓存会被清除。因为此时数据发生了变化,缓存内容可能已经不准确,必须重新查询数据库。 - 调用
SqlSession.clearCache()
方法手动清空缓存。 - 当
SqlSession
关闭后,一级缓存也随之失效。
工作原理:
- 执行查询时,MyBatis 首先会去检查当前
SqlSession
中是否有相同的查询请求已经缓存过。 - 如果有,则直接返回缓存的结果;如果没有,则从数据库中查询,并将结果存入缓存,以便后续相同查询使用。
示例:
// 第一次查询
User user1 = sqlSession.selectOne("com.example.mapper.UserMapper.selectUserById", 1);
// 第二次查询,使用相同的SqlSession和参数,走缓存
User user2 = sqlSession.selectOne("com.example.mapper.UserMapper.selectUserById", 1);
System.out.println(user1 == user2); // true,表示两次查询结果是同一个对象(走了缓存)
2. 二级缓存(Global Cache)
定义:
- 二级缓存作用范围是
Mapper
(映射器)级别,多个SqlSession
可以共享相同的缓存数据。也就是说,同一个Mapper
对象的查询结果可以跨SqlSession
复用。
特点:
- 默认关闭:二级缓存默认是关闭的,需要手动配置开启。
- 作用范围是 Mapper:缓存是基于
Mapper
文件或Mapper
接口的,二级缓存存储在namespace
级别。 - 缓存持久化:二级缓存中的数据可以被多个
SqlSession
共享,但每个Mapper
都有自己的缓存区域,互不影响。 - 基于对象序列化:缓存数据是通过对象序列化存储的,所以需要实体类实现
Serializable
接口。
何时失效:
- 与一级缓存相似,
INSERT
、UPDATE
、DELETE
操作会清除对应namespace
下的缓存。 - 在同一
Mapper
下的查询结果被缓存,不同Mapper
之间不共享缓存。
开启二级缓存:
全局配置:在
mybatis-config.xml
中开启全局二级缓存支持。<settings> <setting name="cacheEnabled" value="true"/> </settings>
Mapper 级别配置:在每个
Mapper.xml
文件中开启二级缓存。<cache/>
实体类实现
Serializable
:缓存的对象必须是可序列化的,因此要让实体类实现Serializable
接口。
示例:
配置全局开启二级缓存:
<settings> <setting name="cacheEnabled" value="true"/> </settings>
在
UserMapper.xml
中开启缓存:<mapper namespace="com.example.mapper.UserMapper"> <cache/> <select id="selectUserById" parameterType="int" resultType="User"> SELECT * FROM user WHERE id = #{id} </select> </mapper>
实体类
User
:public class User implements Serializable { private int id; private String name; private int age; // Getters and Setters }
工作原理:
- 当第一个
SqlSession
执行查询操作时,查询结果会被缓存到二级缓存中。 - 当第二个
SqlSession
执行相同查询操作时,会直接从二级缓存中读取数据,而不是再次查询数据库。
示例代码:
// 第一次查询,走数据库
SqlSession sqlSession1 = sqlSessionFactory.openSession();
User user1 = sqlSession1.selectOne("com.example.mapper.UserMapper.selectUserById", 1);
sqlSession1.commit(); // 提交事务后,查询结果会被放入二级缓存
sqlSession1.close();
// 第二次查询,走二级缓存
SqlSession sqlSession2 = sqlSessionFactory.openSession();
User user2 = sqlSession2.selectOne("com.example.mapper.UserMapper.selectUserById", 1);
sqlSession2.close();
System.out.println(user1 == user2); // true,表示两次查询结果是同一个对象(走了二级缓存)
3. 一级缓存 vs 二级缓存
缓存类型 | 作用范围 | 生命周期 | 共享范围 | 默认状态 |
---|---|---|---|---|
一级缓存 | SqlSession | SqlSession 生命周期 | 当前 SqlSession 内 | 默认开启 |
二级缓存 | Mapper | SqlSession 结束后持久化 | 跨 SqlSession 共享 | 默认关闭 |
4. 缓存清理条件
- 一级缓存:每当执行
INSERT
、UPDATE
、DELETE
操作,或手动调用clearCache()
,缓存会被清空。 - 二级缓存:每当执行
INSERT
、UPDATE
、DELETE
操作,相关 Mapper 下的缓存会被清空。
5. 缓存机制的使用场景
- 一级缓存:适用于在同一个
SqlSession
中多次查询相同数据的场景,避免重复查询数据库。 - 二级缓存:适用于跨多个
SqlSession
查询相同数据,尤其是在高并发场景下,可以显著减少数据库压力,提升性能。
总结
- 一级缓存:默认开启,作用在
SqlSession
范围内,每个SqlSession
独立管理缓存,生命周期短。 - 二级缓存:默认关闭,需要手动开启,作用在
Mapper
范围内,多个SqlSession
共享缓存,缓存内容可以跨会话。