今天寫代碼的時候碰到了一個情況 簡單的的寫了一個小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的緩存介紹,講的很詳細了。