Mybatis的一级、二级缓存
这也是一个面试大概率会问到的一个问题,一般简历上面都有写熟悉SQL优化什么的,不知道怎么优化的可以看下面这篇文章。而Mybaits作为大多数公司用的持久层框架,一级二级缓存概念也是经常被问到的,下面就结合概念和实战来写一下什么是Mybaits的一级、二级缓存!
1、什么是缓存?
什么是缓存:所谓的缓存,就是将程序或系统经常要调用的对象存在内存中,以便其使用时可以快速调用,不必再去创建新的重复的实例。
为什么使用缓存:使用缓存可以减少和数据库的交互次数,提高执行效率,并且可以保护数据库不被高并发的请求拖死。
什么样的数据能使用缓存,什么样的数据不能使用:
适用于缓存的数据:
- 经常查询并且不经常被改变的数据。
- 数据的正确与否对最终结果影响不大的数据。
不适用于缓存的数据:
- 经常改变的数据。
- 数据的正确与否对最终结果影响很大的。
2、Mybatis的一级缓存
Mybatis的一级缓存是默认开启的,其实指的是Mybaits中SqlSession对象的缓存。当我们执行查询之后,查询的结果会同时存入到 SqlSession为我们提供的一块区域中。该区域的结构是一个Map。当我们再次查询同样的数据,Mybatis会先去SqlSession中查询是否存在,存在的话直接返回,而不会再去数据库查询。这里要注意一个点,因为一级缓存是依赖于SqlSession对象的,当SqlSession对象消失时,Mybaits的一级缓存也就消失了。
当Mybaits与Spring整合的时候,不带Spring事务的方法内,每次请求数据库,都会新建一个SqlSession,这时候是使用不到一级缓存的,代码示例如下:
public void test1(){ MailConf mail1 = mailConfService.findByMailCode("11"); System.out.println(mail1); MailConf mail2 = mailConfService.findByMailCode("11"); System.out.println(mail2); System.out.println(mail1 == mail2); }
![21a8f00fd3cf5a2ac4b045a6a82046e1.png](https://i-blog.csdnimg.cn/blog_migrate/1597bdb655dbe368e74db11e8ce37113.jpeg)
image
如上图,可以看到,每次请求都新建了一个Sqlsession,返回的对象也不是同一个。
在带Spring事务的方法内,每次请求数据库,只新建一个SqlSession,这时候是可以使用到一级缓存的,代码示例如下:
@Transactional(rollbackFor = Exception.class)public void test1(){ MailConf mail1 = mailConfService.findByMailCode("11"); System.out.println(mail1); MailConf mail2 = mailConfService.findByMailCode("11"); System.out.println(mail2); System.out.println(mail1 == mail2); }
![be0c624843b335d25850adfb6ba57f00.png](https://i-blog.csdnimg.cn/blog_migrate/f8f4e77622555b1f2d3b4d3e2ed6b519.jpeg)
如上图,这样就用到了一级缓存。
除了事务问题,还有一些问题也会影响到一级缓存,比如调用了Sqlsession的修改、添加、删除、commit()、close()等方法时,一级缓存也会被清空,这里就不一一示范了,底下示例就先删除一条记录,然后看下结果:
@Transactional(rollbackFor = Exception.class) public void test1() { MailConf mail1 = mailConfService.findByMailCode("11"); System.out.println(mail1); // 删除 mailConfService.deleteConf(2); MailConf mail2 = mailConfService.findByMailCode("11"); System.out.println(mail2); System.out.println(mail1 == mail2); }
![2b98bc7adea8d091a5f7f6d7590fcf46.png](https://i-blog.csdnimg.cn/blog_migrate/011659b88b132b1eafc82549dd0f966a.jpeg)
��同样的代码,不同的执行结果,这时候也是没有用到一级缓存。(这里有个小问题,如果把删除放在两次查询的最前面或者最后面,会不会用到缓存,可以自己测试一下。答案我先说下:只要不是放在两次查询的中间,都会使用到一级缓存。)
3、Mybatis的二级缓存
Mybatis的二级缓存是默认关闭的,它指的是Mybatis中SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享其二级缓存。这里注意:一个SqlSessionFactory对象可能创建多个SqlSession。
SpringBoot项目中二级缓存使用步骤:
- 1、让当前的映射文件支持二级缓存(在xxx.xml中配置)
- 2、让当前的操作支持二级缓存(在select标签中配置)
SqlSessionFactory和SqlSession关系图如下:
![02619848c16c82fae2cc842e831469d2.png](https://i-blog.csdnimg.cn/blog_migrate/5b6e7c7fa4804f6aedb606a3d3872806.jpeg)
代码示例如下:
![6607d7ad00639894e2c9218b7297826e.png](https://i-blog.csdnimg.cn/blog_migrate/17252a6b4d30e2b201725e180642ca83.jpeg)
@AutowiredSqlSessionFactory sqlSessionFactory; public void test1() { SqlSession sqlSession1 = sqlSessionFactory.openSession(); MailConfMapper mapper1 = sqlSession1.getMapper(MailConfMapper.class); MailConf mailConf1 = mapper1.findByMailCode("11"); System.out.println(mailConf1); // 关闭一级缓存 sqlSession1.close(); SqlSession sqlSession2 = sqlSessionFactory.openSession(); MailConfMapper mapper2 = sqlSession2.getMapper(MailConfMapper.class); MailConf mailConf2 = mapper2.findByMailCode("11"); System.out.println(mailConf2); // 关闭一级缓存 sqlSession2.close(); System.out.println(mailConf1 == mailConf2); }
![f893dbc7002dbe775c52649348c5e31e.png](https://i-blog.csdnimg.cn/blog_migrate/d396403644d70a54a03e3bacf2258a5e.jpeg)
上面代码,创建了两个SqlSession,并且关闭了SqlSession的一级缓存,然后结果图可以看到,只是查询了一次数据库。第二次返回的是缓存中的数据,但是看红色框那里,返回的对象竟然不是同一个,这是怎么回事呢?原因是这样的:SqlSession对象中存放的是返回数据的对象,而SqlSessionFactory对象中存放的是数据,不是对象,所以上面结果图看到的是返回数据是同一个,而对象却不是同一个。
总结
一级缓存与二级缓存的不同之处:
- 1、Mybatis的一级缓存默认开启,而二级缓存默认关闭。
- 2、Mybatis的一级缓存指的是Mybaits中SqlSession对象的缓存,而二级缓存指的是SqlSessionFactory对象的缓存。一个SqlSessionFactory对象包括多个SqlSession对象。
- 3、SqlSession对象中存放的是返回数据的对象,而SqlSessionFactory对象中存放的是数据,不是对象。
- 4、Mybatis和Spring整合的时候,一级缓存与事务有关,而二级缓存与事务无关。