• Mybatis二级缓存源码整理


    1. 添加配置
      mybatis-plus.configuration.cache-enabled=true
      
      • 1
    2. 在mapper.xml文件中添加cache标签
      <cache size="10240" eviction="LRU"/>
      
      • 1
    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);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    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);
         }    
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
    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);
               }
            }
         }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21

    解析注册部分

    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();
           }
        }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
    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;
         }    
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
    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);
         }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    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;
         }    
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
  • 相关阅读:
    Flutter混合栈管理
    CSP 2022 提高组&普及组总结
    网络配置(一)
    06 数列极限的概念
    FFmpeg开发笔记(三十)解析H.264码流中的SPS帧和PPS帧
    Python二级 每周练习题27
    [PAT练级笔记] 22 Basic Level 1022 D进制的A+B
    【RocketMQ 十】RocketMQ工作原理之消息生产及存储
    基于jsp+mysql+Spring+mybatis+VUE的SpringBoot电影院会员积分管理系统
    Redis server启动后会做哪些操作?
  • 原文地址:https://blog.csdn.net/yichengjie_c/article/details/134330672