mybatis一級快取(session cache)引發的問題

flysharkym發表於2016-05-14

問題回顧

最近專案功能單元測試中,出現了一個奇怪的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),預設關閉。

其他參考:

附註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>

相關文章