Mybatis二级缓存源码整理

  1. 添加配置
    mybatis-plus.configuration.cache-enabled=true
    
  2. 在mapper.xml文件中添加cache标签
    <cache size="10240" eviction="LRU"/>
    
  3. 同一个事务中二级缓存不生效,会使用一级缓存,因为事务未提交。
执行流程部分
  1. Configuration创建Executor对象
    public class Configuration{
        public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
            executorType = executorType == null ? defaultExecutorType : executorType;
            Executor executor = new SimpleExecutor(this, transaction);
            // 根据settings中配置的启用cache创建CachingExecutor包装类
            if (cacheEnabled) {
                executor = new CachingExecutor(executor);
            }
            return (Executor) interceptorChain.pluginAll(executor);
        }
    }
    
  2. CachingExecutor#query查询逻辑
    public class CachingExecutor implements Executor {
       public <E> List<E> query(
               MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler,
               CacheKey key, BoundSql boundSql) throws SQLException {
          //1. 从 MappedStatement中获取二级缓存对象,如果存在,则进入缓存查询逻辑
          Cache cache = ms.getCache();
          if (cache != null) {
             flushCacheIfRequired(ms);
             if (ms.isUseCache() && resultHandler == null) {
                ensureNoOutParams(ms, boundSql);
                List<E> list = (List<E>) tcm.getObject(cache, key);
                if (list == null) {
                   list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                   //2. 将数据保存到缓存中,注意这里先放入TransactionalCache对象中,等待事务提交时回调CachingExecutor#commit时,
                   // 调用TransactionalCache#commit -> 调用flushPendingEntries将缓存数据写入到cache中,
                   // 所以同一个事务中多次查询无法使用二级缓存中的数据
                   tcm.putObject(cache, key, list); // issue #578 and #116
                }
                return list;
             }
          }
          // 3. 非二级缓存逻辑
          return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
       }    
    }
    
  3. TransactionalCache#commit事务提交时回调逻辑
    public class TransactionalCache{
       // 事务体骄傲时回调函数 
       public void commit() {
          if (clearOnCommit) {
             delegate.clear();
          }
          flushPendingEntries();
          reset();
       }
       private void flushPendingEntries() {
          for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
             // 将缓存数据写入到cache对象中 
             delegate.putObject(entry.getKey(), entry.getValue());
          }
          for (Object entry : entriesMissedInCache) {
             if (!entriesToAddOnCommit.containsKey(entry)) {
                delegate.putObject(entry, null);
             }
          }
       }
    }
    

解析注册部分

XMLMapperBuilder解析MapperStatement并放入到Configuration中
  1. XMLMapperBuilder#configurationElement业务逻辑处理
    public class XMLMapperBuilder{
       private void configurationElement(XNode context) {
          try {
             String namespace = context.getStringAttribute("namespace");
             if (namespace == null || namespace.isEmpty()) {
                throw new BuilderException("Mapper's namespace cannot be empty");
             }
             builderAssistant.setCurrentNamespace(namespace);
             cacheRefElement(context.evalNode("cache-ref"));
             // 1. 解析XXXMapper.xml文件中的cache标签,并调用builderAssistant.useNewCache构建Cache实例
             cacheElement(context.evalNode("cache"));
             parameterMapElement(context.evalNodes("/mapper/parameterMap"));
             resultMapElements(context.evalNodes("/mapper/resultMap"));
             sqlElement(context.evalNodes("/mapper/sql"));
             // 2. 构建Statement对象
             buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
          } catch (Exception e) {
             throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
          }
       }    
       private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
          for (XNode context : list) {
             final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, 
                 builderAssistant, context, requiredDatabaseId);
              // 构建Statement对象
              statementParser.parseStatementNode();
         }
      }
    }
    
XMLMapperBuilder#cacheElement 展开细节
  1. MapperBuilderAssistant#useNewCache构建Cache实例并添加到Configuration中缓存
    public class MapperBuilderAssistant{
       public Cache useNewCache(
               Class<? extends Cache> typeClass, Class<? extends Cache> evictionClass, Long flushInterval,
               Integer size, boolean readWrite, boolean blocking, Properties props) {
          Cache cache = new CacheBuilder(currentNamespace).implementation(valueOrDefault(typeClass, PerpetualCache.class))
                  .addDecorator(valueOrDefault(evictionClass, LruCache.class)).clearInterval(flushInterval).size(size)
                  .readWrite(readWrite).blocking(blocking).properties(props).build();
          configuration.addCache(cache);
          // 将cache实例赋值给MapperBuilderAssistant对象的 currentCache 属性
          currentCache = cache;
          return cache;
       }    
    }
    
XMLStatementBuilder#parseStatementNode 展开细节
  1. XMLStatementBuilder#parseStatementNode调用builderAssistant.addMappedStatement实例化MapperStatement对象
    public class XMLStatementBuilder extends BaseBuilder {
       public void parseStatementNode() {
          // 省略部分代码 
          builderAssistant.addMappedStatement(
               id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap,
               parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered,
               keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets, dirtySelect);
       }
    }
    
  2. MapperBuilderAssistant#addMappedStatement
    public class MapperBuilderAssistant extends BaseBuilder {
       public MappedStatement addMappedStatement(
            String id, SqlSource sqlSource, StatementType statementType,
            SqlCommandType sqlCommandType, Integer fetchSize, Integer timeout, String parameterMap, Class<?> parameterType,
            String resultMap, Class<?> resultType, ResultSetType resultSetType, boolean flushCache, boolean useCache,
            boolean resultOrdered, KeyGenerator keyGenerator, String keyProperty, String keyColumn, String databaseId,
            LanguageDriver lang, String resultSets, boolean dirtySelect) {
          id = applyCurrentNamespace(id, false);
          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(flushCache).useCache(useCache)
                  //1. 将MapperBuilderAssistant对象的 currentCache 属性放入到cache字段中
                  .cache(currentCache)
                 .dirtySelect(dirtySelect);
          ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
          if (statementParameterMap != null) {
             statementBuilder.parameterMap(statementParameterMap);
          }
          MappedStatement statement = statementBuilder.build();
          configuration.addMappedStatement(statement);
          return statement;
       }    
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值