缓存就是内存中的数据,常常来自对数据库查询结果的保存。使用缓存,我们可以避免频繁的与数据库进行交互,进而提高响应速度MyBatis也提供了对缓存的支持,分为一级缓存和二级缓存,可以通过下图来理解:
①、一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
②、二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的
一级缓存
默认是开启的
①、我们使用同一个sqlSession,对User表根据相同id进行两次查询,查看他们发出sql语句的情况
@Test
public void firstLevelCacheTest() throws IOException {
// 1. 通过类加载器对配置文件进行加载,加载成了字节输入流,存到内存中 注意:配置文件并没有被解析
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2. (1)解析了配置文件,封装configuration对象 (2)创建了DefaultSqlSessionFactory工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
// 3.问题:openSession()执行逻辑是什么?
// 3. (1)创建事务对象 (2)创建了执行器对象cachingExecutor (3)创建了DefaultSqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4. 委派给Executor来执行,Executor执行时又会调用很多其他组件(参数设置、解析sql的获取,sql的执行、结果集的封装)
User user = sqlSession.selectOne("com.itheima.mapper.UserMapper.findByCondition", 1);
User user2 = sqlSession.selectOne("com.itheima.mapper.UserMapper.findByCondition", 1);
System.out.println(user == user2);
sqlSession.close();
}
查看控制台打印情况:
② 同样是对user表进行两次查询,只不过两次查询之间进行了一次update操作。
@Test
public void test3() throws IOException {
// 1. 通过类加载器对配置文件进行加载,加载成了字节输入流,存到内存中 注意:配置文件并没有被解析
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2. (1)解析了配置文件,封装configuration对象 (2)创建了DefaultSqlSessionFactory工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
// 3.问题:openSession()执行逻辑是什么?
// 3. (1)创建事务对象 (2)创建了执行器对象cachingExecutor (3)创建了DefaultSqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
// 4. 委派给Executor来执行,Executor执行时又会调用很多其他组件(参数设置、解析sql的获取,sql的执行、结果集的封装)
User user = sqlSession.selectOne("com.itheima.mapper.UserMapper.findByCondition", 1);
User user1 = new User();
user1.setId(1);
user1.setUsername("zimu");
sqlSession.update("com.itheima.mapper.UserMapper.updateUser",user1);
sqlSession.commit();
User user2 = sqlSession.selectOne("com.itheima.mapper.UserMapper.findByCondition", 1);
System.out.println(user == user2);
System.out.println(user);
System.out.println(user2);
System.out.println("MyBatis源码环境搭建成功....");
sqlSession.close();
}
查看控制台打印情况:
③、总结
1、第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从 数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。
2、 如果中间sqlSession去执行commit操作(执行插入、更新、删除),则会清空SqlSession中的 一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
3、 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直 接从缓存中获取用户信息
一级缓存原理探究与源码分析
问题1:一级缓存 底层数据结构到底是什么?
问题2:一级缓存的工作流程是怎样的?
一级缓存 底层数据结构到底是什么?
之前说不同SqlSession的一级缓存互不影响
,所以我从SqlSession这个类入手
可以看到,org.apache.ibatis.session.SqlSession
中有一个和缓存有关的方法——clearCache()
刷新缓存的方法,点进去,找到它的实现类DefaultSqlSession
@Override
public void clearCache() {
executor.clearLocalCache();
}
再次点进去executor.clearLocalCache()
,再次点进去并找到其实现类BaseExecutor
,
@Override
public void clearLocalCache() {
if (!closed) {
localCache.clear();
localOutputParamet