一 Mybatis两种缓存的解释
1. 一级缓存
在学习一级缓存之前,我们先了解一下SqlSession
SqlSession是对Connection的一层封装
如果要显式使用SqlSession,先使用输入流InputStream读取Mybatis的配置文件;再通过SqlSessionFactoryBuilder加载流,然后获取到SqlSessionFactory;通过SqlSessionFactory获取到SqlSession
String resource = "mybatis-config.xml";
// 使用Resources工具从类加载路径下加载指定文件
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
一级缓存的作用域有两种:session(默认)和statment,可通过设置local-cache-scope 的值来切换,默认为session,SqlSession之间互相隔离;二者的区别在于session会将缓存作用于同一个sqlSesson,而statment仅针对一次查询,所以,local-cache-scope: statment可以理解为关闭一级缓存
一级缓存命中的条件是:
- 同一个SqlSession
- 同一个Mapper中的同一个方法
- sql完全相同
这三个条件缺一不可
通常我们使用的MVC设计模式,分为三层架构,如果我们在Service调用Mapper,在不加事务的情况下每次调用都会create一个新的SqlSession,也就是说这种情况下缓存是不会生效的;如果加了事务,这种情况下就会复用创建的SqlSession,如果符合条件就会命中缓存
2. 二级缓存
二级缓存的作用域是一个命名空间,也就是一个mapper.xml中的范围,与SqlSession无关,只要是在一个命名空间内的相同的sql都可以命中缓存,可以被SqlSession共享
二级缓存命中的条件是:
- 同一个命名空间内
- 同一个Mapper中的同一个方法
- sql完全相同
二 两种缓存的使用方式
1. 一级使用方式
1.1 开启
默认开启,无需操作
1.2 刷新
- 在一个SqlSession中如果执行了CUD操作,就会刷新一级缓存
- 开启flushCache=“true”,在xml的statement标签中标注
1.3 关闭
全局关闭:local-cache-scope: statment
关闭单个:在statement标签中flushcache=true
2. 二级使用方式
2.1 开启
默认开启,但是需要在命名空间中加上下面的配置后才会生效
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
2.2 刷新
- 在同一命名空间中的CUD就会刷新二级缓存
- 达到了flushInterval超时时间就会刷新二级缓存
2.3 关闭
全局关闭:直接设置cache-enabled: true或者在命名空间中不要加标签
单个关闭:在statement标签中设置useCache=false
三 总结
Mybatis获取数据的顺序优先级:二级缓存>一级缓存>数据库
两种缓存都会造成脏读的问题,主要原因:
一级缓存SqlSession之间是互相隔离的,无法感知到别人的变化(刷新)
解决办法:减小SqlSession作用域或者关闭对应的一级缓存
二级缓存命名空间之间互相隔离,无法感知到别人的变化(刷新)
解决办法:引用别的命名空间,使他们在同一个作用域中,或者关闭对应的二级缓存
如果不加事务,每次查询之后就会立即commit,这也是不加事务一级缓存无法生效的原因,因为每次查询都是一个新的SqlSession
同时,如果事务的隔离级别是REQUIRES_NEW(开启新的事务),一级缓存也无法生效