mybatis一級快取(session cache)引發的問題
阿新 • • 發佈:2019-01-29
問題回顧
最近專案功能單元測試中,出現了一個奇怪的bug。遠端除錯發現,程式進行了2次相同的查詢,返回了實體類(ClassA)的2個物件:classAInstance1和classAInstance2,當修改classAInstance1.property1時,竟然classAInstance2.property1也被改了!!! 很快發現classAInstance1和classAInstance2地址是相同的,它們是同一個記憶體物件!
原因分析
經除錯發現,mybatis返回的實體類的記憶體地址是相同的!於是猜測是mybatis快取的原因,於是進行了下面的測試驗證。
測試驗證
經過單元測試驗證,不開啟事務的情況下,多次相同的查詢,返回物件地址不相等, 程式碼略。
經過單元測試驗證,在一個事務內,多次相同的查詢,返回物件地址相等, 程式碼如下:
@Resource
private MybatisSessionCacheTestService mybatisSessionCacheTestService;
@Test
public void test_mybatis_sql_session_cache(){
Long id = 100L;
ClassA classAInstance1 = mybatisSessionCacheTestService.queryById(id);
ClassA classAInstance2 = mybatisSessionCacheTestService.queryById(id);
//assert mybatis cache is on
Assert.assertTrue(classAInstance1 == classAInstance2);
}
@Service
public class MybatisSessionCacheTestService {
@Resource
private ClassADAO classDAO;
@Transactional //spring 事務註解
public ClassA queryById(Long id){
return classADAO.queryById(id);
}
}
解決方案
- 1.把查詢提前到事務之前(之外),這樣只解決了個別問題,解決並不徹底。
- 2.在mybatis的mapper xml裡配置每次清空快取flushCache:
<select id="selectById" resultType="ClassA" flushCache="true">
...
</select>
附錄:mybatis快取介紹
一級快取
即session快取,作用域為 Session,當 Session flush 或 close 之後,該Session中的所有 Cache 就將清空,預設開啟。
注意 整合spring(使用mybatis-spring)時:
- 每次查詢spring會重新建立SqlSession,所以一級快取是不生效的。
- 而當開啟事務時,spring會使用同一個SqlSession做查詢,所以這個情況下一級快取是生效的
二級快取
即全域性快取,其作用域為 Mapper(Namespace),預設關閉。
其他參考:
- ,簡單介紹了mybatis一級和二級快取,獨立使用mybatis(SqlSession)、以及整合spring(mybatis-spring)的情況下進行了測試
- ,簡單介紹了mybatis一級和二級快取,獨立使用mybatis(SqlSession)進行了測試
附註jar包版本:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>