討論Spring整合Mybatis時一級緩存失效得問題


問題

1、學習測試時發現了一級緩存並沒有生效,先看案例:
setting配置:

  <settings>
        <!-- (1) 提供可控制Mybatis框架運行行為的屬性信息 -->
        <setting name="lazyLoadingEnabled" value="false" />
        <setting name="logImpl" value="STDOUT_LOGGING" />
        <setting name="localCacheScope" value="SESSION"/>
    </settings>

測試代碼:

   @Test
    public void test1(){
        try(SqlSession sqlSession = sessionFactory.openSession();){
             GoodsMapper goodsMapper = sqlSession.getMapper(GoodsMapper.class);
             Goods goods = goodsMapper.getGoodsById(1,"電腦2");
              Goods goods2 = goodsMapper.getGoodsById(1,"電腦2");
        }
    }

    @Autowired
    private GoodsMapper goodsMapper;


    @Test
    public void test2(){
        Goods goods = goodsMapper.getGoodsById(1,"電腦2");
        Goods goods2 = goodsMapper.getGoodsById(1,"電腦2");
    }

問題分析

首先我們都知道一級緩存的作用范圍有兩種,一種是sqlSession,一種是STATEMENT。我們看test2方法,用的都是同一個mapper,按理說是應該只查詢一次的啊,難道同一個mapper的sqlSession不一樣。緊接着我試了test1方法,用的也都是同一個mapper,發現只查詢了一次數據庫。難道這兩種方法有不一樣的地方嗎?

答案就在MapperProxy類當中,我們打斷點執行test1方法,我們發現他的sqlSession類型是DefaultSqlSession

然后再執行test2方法,我們發現他的sqlSession類型是SqlSessionTemplate

他們的差別找到了,失效的原因多半就是SqlSessionTemplate造成的,SqlSessionTemplate類里有一個SqlSessionInterceptor,我們看他的invoke方法,代碼如下:

private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }

此時我們發現sqlSession是通過invoke方法里的getSqlSession方法重新賦值了,而我打斷點發現,同一個mapper調同樣的方法獲得的sqlSession是不一樣的,既然不一樣,那當然讀不到緩存了。

而DefaultSqlSession則是直接執行查詢方法,是同一個sqlSession。所以讀到了緩存。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM