1. 程式人生 > >spring事物隔離級別和mybatis快取的問題

spring事物隔離級別和mybatis快取的問題

今天使用@Transactional遇到了問題,通過一些實驗,總結下自己的想法。如有不當歡迎指正~

專案使用spring boot + mybatis + mysql,先上程式碼:

    @Override
    @Transactional
    public void test(){
        UDeptDO dept = uDeptDao.get("0cf58a5d6ae811e89feafa163eb3e537");
        // 列印舊值
        log.info("deptname={}", dept.getDeptname());
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
        }
        dept.setDeptname("test");
        update(dept);
        dept = uDeptDao.get("0cf58a5d6ae811e89feafa163eb3e537");
        // 列印新值
        log.info("deptname={}", dept.getDeptname());
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
        }
    }
    
    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void test1(){  
        UDeptDO dept = uDeptDao.get("0cf58a5d6ae811e89feafa163eb3e537");
        // 列印舊值
        log.info("deptname={}", dept.getDeptname());
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
        }
        // 列印舊值,事物未提交
        dept = uDeptDao.get("0cf58a5d6ae811e89feafa163eb3e537");
        log.info("deptname={}", dept.getDeptname());
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
        }
        // 列印新值,事物已提交,READ_COMMITTED生效
        dept = uDeptDao.get("0cf58a5d6ae811e89feafa163eb3e537");
        log.info("deptname={}", dept.getDeptname());
    }

以上是一個service實現類的兩個測試方法,方法test使用Spring事物的預設隔離級別(也就是由底層資料庫提供的隔離級別,mysql預設是REPEATABLE_READ),方法test1使用READ_COMMITTED。同時呼叫test和test1,預期test1方法第三次查詢能獲取到修改值,但是並沒有成功(此處糾結了好久,還以為自己理解錯了。。。)。分析日誌發現test1後兩次查詢未列印SQL語句,估計是快取的原因。給mapper檔案的查詢語句加上useCache="false" flushCache="true"這兩個配置,問題解決。


從這個例子可以看到,READ_COMMITTED可以避免dirty reads,即test1第二次查詢不會查到test的修改,只有test方法事物提交後,test1在第三次查詢才能看到。這裡test1方法後兩次的查詢行為,其實就是我們常說的non-repeatable reads。如果使用REPEATABLE_READ級別,能使test1的三次查詢結果保持一致。