一级缓存
MyBatis 是一个优秀的持久层框架,它通过提供 SQL 映射和对象关系映射功能简化了数据库操作。其中,MyBatis 的缓存机制是其重要的性能优化功能之一,分为一级缓存和二级缓存。一级缓存是默认开启的,且对开发者透明。
一级缓存是 MyBatis 中的本地缓存,作用范围是 SqlSession。在同一个 SqlSession 中执行的多次相同的查询操作,如果参数和 SQL 语句相同,MyBatis 会从缓存中直接返回查询结果,而不会重复访问数据库,从而提高了性能。
代码测试
测试一:同一 SqlSession 中两次相同查询
public class WzkicuCache01 {
public static void main(String[] args) throws IOException {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<WzkUser> wzkUser = userMapper.findAll();
System.out.println(wzkUser);
List<WzkUser> wzkUser2 = userMapper.findAll();
System.out.println(wzkUser2);
sqlSession.close();
}
}
运行结果
24/11/13 10:12:08 DEBUG UserMapper.findAll: ==> Preparing: select *,o.id oid from wzk_user u left join wzk_orders o on u.id=o.uid;
24/11/13 10:12:08 DEBUG UserMapper.findAll: ==> Parameters:
24/11/13 10:12:08 DEBUG UserMapper.findAll: <== Total: 3
两次查询中间没有执行 SQL,说明使用了 MyBatis 的一级缓存。
测试二:查询后执行更新操作
public class WzkicuCache02 {
public static void main(String[] args) throws IOException {
InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 第一次查询
List<WzkUser> wzkUser = userMapper.findAll();
System.out.println(wzkUser);
// 加入一次 UPDATE
WzkUser wzkUserUpdate = WzkUser
.builder()
.id(1)
.username("wzk-update")
.password("123-update")
.build();
wzkUserUpdate.setId(1);
userMapper.updateById(wzkUserUpdate);
sqlSession.commit();
// 再次查询
List<WzkUser> wzkUser2 = userMapper.findAll();
System.out.println(wzkUser2);
sqlSession.close();
}
}
运行结果
运行结果为:SQL 查询 → 更新操作 → SQL 查询。UPDATE 之后,MyBatis 会清空缓存。
工作原理
一级缓存存储在 SqlSession 的内部,它基于 Java 的 HashMap 结构实现,使用查询的 SQL 语句和参数作为键,查询结果作为值。
工作流程
-
查询前检查缓存:SqlSession 会先检查缓存中是否已经存在相应的数据。如果缓存命中,则直接返回缓存中的结果,不再执行 SQL 语句。
-
查询后更新缓存:如果缓存未命中,MyBatis 会执行 SQL 查询,获取结果,并将结果存储在一级缓存中。
-
缓存的失效:在某些情况下,一级缓存会失效。
一级缓存的特点
- 作用范围:仅限于当前的 SqlSession
- 线程安全:SqlSession 是线程不安全的,因此一级缓存仅在单个线程中有效
- 生命周期:一级缓存的生命周期与 SqlSession 一致,当 SqlSession 被关闭或销毁时,缓存会被清空
- 缓存粒度:一级缓存的粒度较细,限定在单个 SqlSession
一级缓存的生效条件
- 相同的 SQL 语句:查询的 SQL 和参数必须完全相同
- 相同的 SqlSession:必须在同一个 SqlSession 实例中进行查询操作
- 没有执行过更新操作:在执行 INSERT、UPDATE 或 DELETE 操作后,一级缓存会被清空
- 未显式清空缓存:如果手动调用了 clearCache() 方法,一级缓存会被清空
一级缓存的失效场景
- 不同的 SqlSession:如果查询发生在不同的 SqlSession 中,一级缓存会失效
- 执行更新操作:当执行了 INSERT、UPDATE 或 DELETE 语句后,MyBatis 会默认清空当前 SqlSession 的一级缓存
- 显式清空缓存:如果在代码中调用了 SqlSession 的 clearCache() 方法,会清空一级缓存
- 查询条件发生变化:如果查询的 SQL 或参数发生变化,一级缓存不会命中
原理探究
一级缓存到底是什么?一级缓存什么时候被创建?一级缓存的工作流程是什么样子的?
通过源码分析,可以发现:
- 在 BaseExecutor 中可以查看缓存相关的代码实现
- clearCache 方法和缓存相关
- Perpetualcache 本质上就是一个大的 Map<Object, Object>
- 创建缓存时会经过一系列的 UPDATE 方法,由 CacheKey 对象来执行
一级缓存是基于 HashMap 实现的本地缓存,默认开启,生命周期与 SqlSession 一致。