美文网首页
mybatis 缓存控制

mybatis 缓存控制

作者: 全都是泡沫啦 | 来源:发表于2018-12-27 16:37 被阅读0次

    mybatis查询缓存分为一级缓存(相同会话SqlSession中Executor的PerpetualCache=localCache)和二级缓存(相同MappedStatement的id共用缓存)

    public enum LocalCacheScope {
      SESSION,STATEMENT
    }
    
    1. 在spring-mybatis的集成环境中在被注解@Transactional标识的方法中使用的是相同的SqlSession(前提:方法内都是同步执行,不使用线程或者线程池,因为TransactionSynchronizationManager使用的是ThreadLocal模式),代码如下:
    org.mybatis.spring.SqlSessionTemplate
    public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
          PersistenceExceptionTranslator exceptionTranslator) {
        this.sqlSessionFactory = sqlSessionFactory;
        this.executorType = executorType;
        this.exceptionTranslator = exceptionTranslator;
        this.sqlSessionProxy = (SqlSession) newProxyInstance(
            SqlSessionFactory.class.getClassLoader(),
            new Class[] { SqlSession.class },
            new SqlSessionInterceptor());
    }
    public <T> T selectOne(String statement, Object parameter) {
        return this.sqlSessionProxy.<T> selectOne(statement, parameter);
    }
    
    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);
            }
          }
        }
      }
    public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
        SqlSessionHolder holder = (SqlSessionHolder) 
    //private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");
    //private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal("Transaction synchronizations");
        TransactionSynchronizationManager.getResource(sessionFactory);
        SqlSession session = sessionHolder(executorType, holder);
        if (session != null) {
          return session;
        }
        session = sessionFactory.openSession(executorType);
        registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
        return session;
      }
    
    
    2. 缓存的配置参数汇总如下
    <configuration>
      <settings>
        <setting name="cacheEnabled" value="true"/> //全局控制二级缓存的开启
      </settings>
    </configuration>
    <mapper namespace="**.*DAO">
      <cache />            //创建mapper 二级缓存的对象,无法使用
      <select id="selectSystemDate" 
        flushCache="true"  //细粒度对mapper控制:是否刷新一级二级缓存
        useCache="true"    //细粒度对mapper控制:是否使用二级缓存
        resultType="date">
        select SYSDATE from dual
      </select>
    </mapper>
    
    2.1 mybatis-config.xml中cacheEnabled的属性配置:这个控制创建SqlSession中Executor是否会被包装为org.apache.ibatis.executor.CachingExecutor
      //org.apache.ibatis.session.Configuration#newExecutor
      public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null ? defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Executor executor;
        if (ExecutorType.BATCH == executorType) {
          executor = new BatchExecutor(this, transaction);
        } else if (ExecutorType.REUSE == executorType) {
          executor = new ReuseExecutor(this, transaction);
        } else {
          executor = new SimpleExecutor(this, transaction);
        }
        if (cacheEnabled) {
          executor = new CachingExecutor(executor);
        }
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
      }
    

    当使用CachingExecutor时二级缓存就已经开启,至于是否可以使用二级缓存就得判断flushCache useCache resultHandler 的参数情况了

      //org.apache.ibatis.executor.CachingExecutor#query
      public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
          throws SQLException {
        Cache cache = ms.getCache();
        if (cache != null) {
          flushCacheIfRequired(ms);
          if (ms.isUseCache() && resultHandler == null) {
            ensureNoOutParams(ms, parameterObject, boundSql);
            @SuppressWarnings("unchecked")
            List<E> list = (List<E>) tcm.getObject(cache, key);
            if (list == null) {
              list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
              tcm.putObject(cache, key, list); // issue #578 and #116
            }
            return list;
          }
        }
        return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      }
      private void flushCacheIfRequired(MappedStatement ms) {
        Cache cache = ms.getCache();
        if (cache != null && ms.isFlushCacheRequired()) {      
          tcm.clear(cache);
        }
      }
    
    2.2 mapper.xml中<cache />的属性:生成MappedStatement时配置cache
    //org.apache.ibatis.builder.MapperBuilderAssistant#addMappedStatement
    public MappedStatement addMappedStatement(.........) {
        .........
        MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
            .resource(resource)
            .fetchSize(fetchSize)
            .timeout(timeout)
            .statementType(statementType)
            .keyGenerator(keyGenerator)
            .keyProperty(keyProperty)
            .keyColumn(keyColumn)
            .databaseId(databaseId)
            .lang(lang)
            .resultOrdered(resultOrdered)
            .resultSets(resultSets)
            .resultMaps(getStatementResultMaps(resultMap, resultType, id))
            .resultSetType(resultSetType)
            .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
            .useCache(valueOrDefault(useCache, isSelect))
            .cache(currentCache);
        ......
        MappedStatement statement = statementBuilder.build();
        configuration.addMappedStatement(statement);
        return statement;
      }
    
    2.3 useCache属性: 控制MappedStatement的二级缓存的使用
    2.4 一级缓存默认是开启的状态,如果要关闭只能通过flushCache=true(此时相同MappedStatement的二级缓存也会失效)
    //org.apache.ibatis.executor.BaseExecutor#query
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
        if (closed) {
          throw new ExecutorException("Executor was closed.");
        }
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
          clearLocalCache();
        }
        List<E> list;
        try {
          queryStack++;
          list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
          if (list != null) {
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
          } else {
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
          }
        } finally {
          queryStack--;
        }
        if (queryStack == 0) {
          for (DeferredLoad deferredLoad : deferredLoads) {
            deferredLoad.load();
          }
          // issue #601
          deferredLoads.clear();
          if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
            // issue #482
            clearLocalCache();
          }
        }
        return list;
      }
    

    相关文章

      网友评论

          本文标题:mybatis 缓存控制

          本文链接:https://www.haomeiwen.com/subject/lsbrlqtx.html