一.二级缓存的介绍
1)二级缓存是用来解决一级缓存不能跨会话共享的问题的,范围是namespace级别的,可以被多个SqlSession共享(只要是同一个接口里面的相同方法,都可以共享),生命周期和应用同步
二.面试题
1.如果开启了二级缓存,二级缓存应该是工作在一级缓存之前,还是在一级缓存之后呢?二级缓存是在哪里维护的呢? 答:
作为一个作用范围更广的缓存,它肯定是在SqlSession的外层,否则不可能被多个SqlSession共享。而一级缓存是在SqlSession内部的,所以第一个问题,肯定是工作在一级缓存之前,也就是只有取不到二级缓存的情况下才到一个会话中去取一级缓存。
2.二级缓存放在哪个对象中维护呢?
要跨会话共享的话,SqlSession本身和它里面的BaseExecutor已经满足不了需求了,那我们应该在BaseExecutor之外创建一个对象。实际上MyBatis用了一个装饰器的类来维护,就是CachingExecutor。如果启用了二级缓存,MyBatis在创建Executor对象的时候会对Executor进行装饰,CachingExecutor对于查询请求,会判断二级缓存是否有缓存结果,如果有就直接返回,如果没有委派交给真正的查询器Executor实现类,比如SimpleExecutor来执行查询,再走到一级缓存的流程。最后会把结果缓存起来,并且返回给用户。
3.二级缓存的开启方式?
第一步:在mybatis-config.xml中配置了(可以不配置,默认是true):
只要没有显式地设置 cacheEnabled=false,都会用 CachingExecutor 装饰基本的执行器
第二步:在Mapper.xml 中配置标签:
Mapper.xml配置了之后,select()会被缓存。update()、delete()、会刷新缓存。
4.如果cacheEnabled=true,Mapper.xml没有配置标签,还有二级缓存吗?还会出现CachingExecutor包装对象吗?
只要cacheEnabled=true基本执行器就会被装饰。有没有配置,决定了在启动的时候会不会创建这个mapper的Cache对象,最终会影响到CachingExecutor query方法里面的判断:
if(cache!=null){
5.二级缓存的关闭?
我们可以在单个StatementID上显式关闭二级缓存(默认是true):
6.为什么事务不提交,二级缓存不存在
因为二级缓存使用TransactionalCacheManager(TCM)来管理,最后又调用了TransactionalCache的getObject()、putObject和commit()方法,TransactionalCache 里面又持有了真正的Cache对象,比如是经过层层装饰的PerpetualCache。在putObject的时候,只是添加到了entriesToAddOnCommit里面,只有它的commit()方法被调用的时候才会调用flushPendingEntries()真正写入缓存。它就是在DefaultSqlSession调用commit()的时候被调用的。
*
7.什么情况下有必要去开启二级缓存
1、因为所有的增删改都会刷新二级缓存,导致二级缓存失效,所以适合在查询为主的应用中使用,比如历史交易、历史订单的查询。否则缓存就失去了意义。
2、如果多个namespace中有针对于同一个表的操作,比如Blog表,如果在一个namespace中刷新了缓存,另一个namespace中没有刷新,就会出现读到脏数据的情况。所以,推荐在一个Mapper里面只操作单表的情况使用。