MyBatis 提供了两种缓存机制,分别是 一级缓存 和 二级缓存。它们可以显著提高数据库操作的性能,通过减少数据库的访问次数,但它们的工作原理、作用范围以及使用方式有所不同。
一、一级缓存
1. 概述
一级缓存是 SqlSession 级别的缓存,也叫做 本地缓存。它默认开启,并且是 MyBatis 的默认缓存机制。在一次数据库会话(SqlSession
)中,MyBatis 会将查询到的结果缓存到一级缓存中。如果相同的 SQL 被多次执行(在同一个 SqlSession
中),MyBatis 会从缓存中读取数据,而不去数据库中查询,这样可以减少数据库的访问。
2. 特点
- 作用范围:一级缓存的作用范围仅限于一个
SqlSession
实例,也就是说,每个SqlSession
都有独立的一级缓存。 - 缓存失效:在同一个
SqlSession
中,只要没有提交、回滚或关闭SqlSession
,缓存的数据都是有效的。执行commit()
或rollback()
后,一级缓存会被清空,缓存的内容会失效。 - 缓存内容:一级缓存存储的是
SqlSession
中执行的查询结果。例如,对于一个查询语句,第一次查询会执行数据库操作,将结果存入缓存,之后相同的查询会直接从缓存中返回,而无需再次查询数据库。
3. 示例
// 获取 SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();// 第一次查询,将会访问数据库
User user1 = sqlSession.selectOne("namespace.selectUser", 1);// 第二次查询(相同 SQL 和参数),此时数据会从一级缓存中获取
User user2 = sqlSession.selectOne("namespace.selectUser", 1);System.out.println(user1 == user2); // true,因为是同一个 SqlSession 对象
4. 清空一级缓存
- 调用
sqlSession.clearCache()
手动清空缓存。 - 调用
commit()
或rollback()
,这些操作也会清空一级缓存。
二、二级缓存
1. 概述
二级缓存是 SqlSessionFactory 级别的缓存,也叫做 全局缓存。它在 MyBatis 的多个 SqlSession
之间共享缓存数据。换句话说,二级缓存的数据是跨 SqlSession
存在的,可以共享缓存内容。
2. 特点
- 作用范围:二级缓存是跨
SqlSession
的,所有使用同一个SqlSessionFactory
创建的SqlSession
都可以共享这个缓存。也就是说,二级缓存是全局缓存,能够跨会话存储查询结果。 - 缓存失效:二级缓存会在
SqlSessionFactory
被销毁时失效。如果SqlSessionFactory
被关闭,二级缓存中的数据会被清空。 - 配置与使用:二级缓存需要在 MyBatis 配置文件中显式启用,并且每个映射器(Mapper)都可以单独配置是否启用二级缓存。
3. 如何启用二级缓存
- 在 MyBatis 配置文件中启用二级缓存:
<configuration><settings><setting name="cacheEnabled" value="true"/></settings> </configuration>
- 在 Mapper 文件中启用二级缓存: 每个 Mapper 都可以单独配置是否启用二级缓存,通常在 Mapper XML 中进行配置。
<mapper namespace="com.example.mapper.UserMapper"><!-- 启用二级缓存 --><cache /><select id="selectUser" resultType="User">SELECT * FROM users WHERE id = #{id}</select> </mapper>
- 二级缓存的存储方式: 二级缓存通常使用内存存储查询结果,但它也可以通过自定义缓存实现,支持持久化缓存。默认情况下,MyBatis 使用
PerpetualCache
类作为二级缓存实现,也可以通过配置缓存实现类来替换。
4. 二级缓存的工作原理
- 查询数据:当一个查询被执行时,MyBatis 会先检查二级缓存中是否已有该数据。如果没有,MyBatis 会执行 SQL 查询,将结果存入缓存,并返回查询结果。
- 使用缓存:在随后的查询中,如果该数据存在于二级缓存中,MyBatis 会直接返回缓存中的数据,避免再次查询数据库。
- 缓存更新:当执行更新、删除等操作时,相关缓存会被清除或更新。这是为了确保缓存中的数据始终是最新的。
5. 清除二级缓存
- 手动清空缓存:通过调用
sqlSession.clearCache()
来清空一级缓存,但二级缓存依赖于SqlSessionFactory
,不会自动清空。 - 执行增删改操作时清空:对于增、删、改等操作,MyBatis 会自动清空相关的二级缓存,确保缓存中的数据是一致的。
6. 示例
// 获取 SqlSession
SqlSession sqlSession1 = sqlSessionFactory.openSession();
User user1 = sqlSession1.selectOne("namespace.selectUser", 1);
sqlSession1.commit(); // 提交会话,一级缓存清空// 获取另一个 SqlSession,查询将使用二级缓存
SqlSession sqlSession2 = sqlSessionFactory.openSession();
User user2 = sqlSession2.selectOne("namespace.selectUser", 1);
System.out.println(user1 == user2); // true,数据来自二级缓存
7. 二级缓存与事务的关系
- 在执行增、删、改等写操作时,相关的二级缓存会被清空。这样可以确保修改后的数据不会因缓存而导致不一致。
- 由于 MyBatis 的事务机制是基于
SqlSession
的,所以在同一事务中的不同SqlSession
共享的二级缓存会在事务提交时清空。
8. 二级缓存的生命周期
- 二级缓存会在
SqlSessionFactory
被销毁时失效,通常在应用程序停止时,MyBatis 会销毁SqlSessionFactory
实例,二级缓存也会清空。
三、总结
- 一级缓存 是
SqlSession
局部的缓存,每个SqlSession
都有自己的缓存,默认启用。它会在同一个会话内缓存查询结果,避免重复查询数据库。 - 二级缓存 是跨
SqlSession
的全局缓存,多个SqlSession
可以共享缓存的内容,适用于跨会话的缓存。二级缓存需要在 MyBatis 配置文件和 Mapper 文件中显式启用。 - 缓存清空机制:一级缓存会在事务提交、回滚或手动清空时清空,而二级缓存则会在修改数据时自动清空相关缓存,保持数据一致性。