MyBatis 之缓存机制核心解析
目录
1. 缓存
2. 一级缓存
2.1. 清空一级缓存
3. 二级缓存
4. 总结
前言
大家好,我是艺杯羹(๑•̀ㅂ•́)و✧
本文来讲解MyBatis中的缓存,旨在帮助读者更好的理解和掌握(๑•̀ㅂ•́)و✧个人主页:艺杯羹
系列专栏:MyBatis
1. 缓存
缓存是内存当中一块存储数据的区域,目的是提高查询效率
MyBatis会将查询结果存储在缓存当中,当下次执行相同的SQL时不访问数据库,而是直接从缓存中获取结果,从而减少服务器的压力
-
什么是缓存?
存在于内存中的一块数据 -
缓存有什么作用?
减少程序和数据库的交互,提高查询效率,降低服务器和数据库的压力 -
什么样的数据使用缓存?
经常查询但不常改变的,改变后对结果影响不大的数据 -
MyBatis 缓存分为哪几类?
一级缓存和二级缓存 -
如何判断两次 Sql 是相同的?
- 查询的 Sql 语句相同
- 传递的参数值相同
- 对结果集的要求相同
- 预编译的模板 Id 相同
2. 一级缓存
MyBatis一级缓存也叫本地缓存
SqlSession对象中包含一个Executor对象,Executor对象中包含一个PerpetualCache对象,在该对象存放一级缓存数据
MyBatis的一级缓存是默认开启的,不需要任何的配置
测试一级缓存
@Test
public void testCache1() throws IOException {InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();SqlSessionFactory factory = builder.build(is);SqlSession session = factory.openSession();// 使用同一个SqlSession查询UserMapper mapper1 = session.getMapper(UserMapper.class);UserMapper mapper2 = session.getMapper(UserMapper.class);User user1 = mapper1.findById(1);System.out.println(user1.hashCode());System.out.println("-------------------------------------------");User user2 = mapper2.findById(1);System.out.println(user2.hashCode());
}@Test
public void testCache2() throws IOException {InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();SqlSessionFactory factory = builder.build(is);SqlSession session1 = factory.openSession();SqlSession session2 = factory.openSession();// 使用不同的SqlSession查询UserMapper mapper1 = session1.getMapper(UserMapper.class);UserMapper mapper2 = session2.getMapper(UserMapper.class);User user1 = mapper1.findById(1);System.out.println(user1.hashCode());System.out.println("-------------------------------------------");User user2 = mapper2.findById(1);// 看是否是一个对象System.out.println(user2.hashCode());
}
2.1. 清空一级缓存
进行以下操作可以清空MyBatis一级缓存:
- SqlSession调用close(): 操作后SqlSession对象不可用,该对象的缓存数据也不可用
- SqlSession调用clearCache() / commit(): 操作会清空一级缓存数据
- SqlSession调用增删改方法: 操作会清空一级缓存数据,因为增删改后数据库发生改变,缓存数据将不准确
代码示范
@Test
public void testCache3() throws IOException {InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();SqlSessionFactory factory = builder.build(is);SqlSession session = factory.openSession();UserMapper mapper1 = session.getMapper(UserMapper.class);UserMapper mapper2 = session.getMapper(UserMapper.class);User user1 = mapper1.findById(1);System.out.println(user1.hashCode());// session.close();// session.clearCache();// session.commit();mapper1.delete(2);System.out.println("-------------------------------------------");User user2 = mapper2.findById(1);System.out.println(user2.hashCode());
}
3. 二级缓存
MyBatis二级缓存也叫全局缓存。数据存放在SqlSessionFactory中,只要是同一个工厂对象创建的SqlSession,在进行查询时都能共享数据。一般在项目中只有一个SqlSessionFactory对象,所以二级缓存的数据是全项目共享的
MyBatis一级缓存存放的是对象,二级缓存存放的是对象的数据
所以要求二级缓存存放的POJO必须是可序列化的,也就是要实现Serializable接口
MyBatis二级缓存默认不开启,手动开启后数据先存放在一级缓存中,只有一级缓存数据清空后,数据才会存到二级缓存中
注:SqlSession调用 clearCache() 无法将数据存到二级缓存中(直接清空一级缓存的当前数据)
开启二级缓存
1. POJO类实现Serializable接口
public class User implements Serializable {private int id;private String username;private String sex;private String address;
}
2. 在MyBatis配置文件添加如下设置:
<settings><setting name="cacheEnabled" value="true"/>
</settings>
由于cacheEnabled默认值是true,所以该设置可以省略
3. 在映射文件(UserMapper.xml)添加<cache />标签,该映射文件下的所有方法都支持二级缓存如果查询到的集合中对象过多,二级缓存只能缓存1024个对象引用。可以通过<cache />标签的size属性修改该数量
<cache size="2048"/>
4. 测试二级缓存
@Test
public void testCache4() throws IOException {InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();SqlSessionFactory factory = builder.build(is);SqlSession session1 = factory.openSession();SqlSession session2 = factory.openSession();UserMapper mapper1 = session1.getMapper(UserMapper.class);UserMapper mapper2 = session2.getMapper(UserMapper.class);User user1 = mapper1.findById(1);System.out.println(user1);System.out.println(user1.hashCode());// 让一级缓存失效session1.commit();System.out.println("-------------------------------------------");User user2 = mapper2.findById(1);System.out.println(user2);System.out.println(user2.hashCode());
}
4. 总结
对比项 | 一级缓存(本地缓存) | 二级缓存(全局缓存) |
---|---|---|
存储位置 | SqlSession 的 Executor 中的 PerpetualCache | SqlSessionFactory |
开启方式 | 默认开启,无需配置 | 默认不开启,需手动配置 |
共享范围 | 仅同一 SqlSession 内共享 | 同一 SqlSessionFactory 创建的 SqlSession 共享 |
存储内容 | 完整对象 | 对象数据(需 POJO 实现 Serializable 接口) |
生效关键条件 | 同一 SqlSession 执行相同 SQL | 一级缓存清空后,同一 SqlSessionFactory 下执行相同 SQL |
核心清空场景 | SqlSession 调用 close ()/clearCache ()/commit ()、执行增删改操作 | 对应增删改操作、配置失效等 |
到此,缓存就讲解完了,希望对大家有所帮助(๑•̀ㅂ•́)و✧