MyBatis缓存有两种:一级缓存和二级缓存。
一级缓存(local cache),即本地缓存,作用域默认为session。当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
本地缓存不能被关闭,可以调用clearCache()来清空本地缓存,或者改变缓存的作用域。
不能被关闭的原因如下(没看大懂):
By default local cache data is used for the whole session duration. This cache is needed to resolve circular references and to speed up repeated nested queries, so it can never be completely disabled but you can configure the local cache to be used just for the duration of an statement execution by setting localCacheScope=STATEMENT.
在SELECT语句中配置刷新缓存,如下:
<select id="aaa" parameterType="string" resultType="integer" flushCache="true">
在mybatis3.1之后,可以配置本地缓存的作用域。在mybatis.xml中配置,如下:
<setting name="localCacheScope" value="SESSION"/> 作用域为SESSION,这位默认值。
<setting name="localCacheScope" value=" STATEMENT "/> 作用域为STATEMENT,即作用域为SQL。
二级缓存(second level cache),作用域默认为Mapper(Namespace)。
二级缓存可以被配置为打开或者关闭,默认为关闭,在mybatis.xml中配置。
<setting name="cacheEnabled" value="true"/>
当全局的二级缓存(setting中配置)设置为关闭时可以在Mapper XML中配置单个mapper的二级缓存为打开,配置如下:
<cache />
当全局的二级缓存(setting中配置)设置为打开时,mapper中这个配置无效,即mapper中配置为关闭该mapper的二级缓存也是打开。
如果想对某条SQL单独对待,可以在SELECE语句中配置useCache,配置如下:
<select id="aaa" parameterType="string" resultType="integer" useCache="false">
当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。
当执行一条查询SQL时,
流程为: 从二级缓存中进行查询 -> [如果缓存中没有,委托给 BaseExecutor] -> 进入一级缓存中查询 -> [如果也没有] -> 则执行 JDBC 查询。
例子:
Java代码
@Transactional
public void test() {
System.out.println(depotService.get(252L).getName());
DepotDto dto = new DepotDto();
dto.setId(252l);
dto.setName("test");
depotService.update(dto);
System.out.println(depotService.get(252L).getName());
}
输出为:
张江站
test
上面的代码test1和test2是一个事务,当执行test1时:
查询站点,保存值到一级缓存(test1),保存值到二级缓存(mapper)。
更新站点的name,刷新一级缓存(test1), 刷新二级缓存(mapper)。
再查询站点,先看二级缓存,没有;再看一级缓存,没有,最后从数据库中查询。所以得出的是数据库中的最新值。
但当将 depotService.update 方法事务属性配置为REQUIRES_NEW时,相当于有事务test1和事务test2。
输出为:
张江站
张江站
当执行test1时:
查询站点,保存值到一级缓存(test1),保存值到二级缓存(mapper)。
更新站点的name,刷新一级缓存(test2), 刷新二级缓存(mapper)。此时在test2内部,刷新的是test2的一级缓存,所以
test1的一级缓存还在。
再查询站点,先看二级缓存(mapper),没有;再看一级缓存(test1)(此时在test1事务内部,查找的是test1的一级缓存),
有,则返回值。所以得出的不是数据库中的最新值。
如果这时将mapper的get语句配置成这样。
Xml代码
<select id="get" parameterType="java.lang.Long" resultMap="depotResultMap" flushCache="true">
输出为:
张江站
test
在mybatis3.1之后,。可以在mybatis.xml中配置得到一样的效果。
最后,强烈推荐阅读http://www.iteye.com/topic/1112327这篇文章。
另外,个人觉得由于二级缓存作用域是namespace,namespace一般对应一个表,当两个表关联查询时,不知道将SQL放入哪个namespace中。所以配置二级缓存开启的SQL最好是和基础表关联(不常更变的数据)。两个业务表关联的SQL,最好不配置二级缓存开启。