今天写代码的时候碰到了一个情况 简单的的写了一个小demo
@Test
@Transactional
public void test(){
Role role = new Role();
role.setRolename("xxxx");
int insert = roleMapper.insert(role);
System.out.println(insert);
Role roleBase = roleMapper.selectByPrimaryKey(role.getId());
System.out.println(roleBase);
change(roleBase.getId(),"aaaa");
change(roleBase.getId(),"bbbb");
System.out.println(JSONUtils.toJSONString(roleBase));
}
private void change(Integer roleId, String s){
Role role = roleMapper.selectByPrimaryKey(roleId);
System.out.println(role);
role.setRolename(s);
}
对插入数据库的一条数据做了一个change操作,但是并没有传入roleBase这个对象。
输出:
通过观察输出,可以看出通过 selectByPrimaryKey 这个方法查出的对象是同一个对象,而且roleBase的值被改变了。
第一反应就是用了缓存。
之后通过修改mybatis的配置
<settings>
<setting name="cacheEnabled" value="false"/>
</settings>
不启用缓存,但是查出来的数据依然是一样的,还是同一个对象。
之后通过对selectByPrimaryKey打断点,跟踪执行的流程
这里将数据缓存进了localCache!!!
在网上找了一些cache相关的文章
local cache和cache
mybatis提供了两种cache类型:local cache和cache
-
local cache,也就是所谓的局部缓存。由以下参数控制:
-
localCacheScope
,见相关文档
-
-
cache,也就是所谓的二级缓存。由以下参数控制:
要特别注意的是,mybatis的local cache是无法关闭的。
那么local cache干了什么?在默认配置情况下,mybatis会将同一session内的查询结果都放在local cache中,这样可以提高性能,避免每次都hit到数据库。
那么cache干了什么呢?和local cache相对的,cache是跨session的,也就是说这个session中缓存的结果,在另外一个session中也能够用到。
问题分析
前面已经讲到了在同一session中的查询会将结果缓存,那么这个和我们一开始提到的问题有什么关系呢?聪明的你一定已经想到了,这个问题和启用了事务有关。
实际上mybatis在和spring集成后,会自动将session绑定到事务上,那么就会产生前面提到的问题。
解决办法
有以下几种解决办法:
-
在mybatis配置文件中
localCacheScope=STATEMENT
。 -
在mapper配置文件中,给select设置
flushCache=true
。需要注意的是,这样会将local cache和cache都清空掉。 -
不用事务
缓存参考:https://segmentfault.com/a/1190000008207977
测试了1,3两个方法可以结局localcache的问题。2应该也ok
学习可以学习美团点评对Mybatis的缓存介绍,讲的很详细了。