讨论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。所以读到了缓存。

猜你喜欢

转载自www.cnblogs.com/javammc/p/11324503.html