MyBatis缓存

109 阅读3分钟

一、执行器的构建

 XMLConfigBuilder
 private void parseConfiguration(XNode root) {
     settingsElement(settings);    
 }
 private void settingsElement(Properties props) {
     configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));    
 }
 DefaultSqlSessionFactory implements SqlSessionFactory
 @Override
 public SqlSession openSession() {
     return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); // DefaultExecutorType -->SIMPLE
 }
 ​
 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
     // execType == SIMPLE
     final Executor executor = configuration.newExecutor(tx, execType);
     // 返回DefaultSqlSession
     return new DefaultSqlSession(configuration, executor, autoCommit);
 }
 Configuration
 public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
     executor = new SimpleExecutor(this, transaction);
     // 默认值是true,所以默认构建的执行器就是CachingExecutor
     if (cacheEnabled) {
         executor = new CachingExecutor(executor);
     }
     executor = (Executor) interceptorChain.pluginAll(executor);
 }
 CachingExecutor
 private final Executor delegate;
 private final TransactionalCacheManager tcm = new TransactionalCacheManager();
 public CachingExecutor(Executor delegate) {
     // SimpleExecutor被委托给了CachingExecutor
     this.delegate = delegate;
     // SimpleExecutor被CachingExecutor封装
     delegate.setExecutorWrapper(this);
 }
 DefaultSqlSeesion
 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
     // CachingExecutor
     return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
 }
 CachingExecutor
 @Override
 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
     CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
     return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
 }
 ​
 @Override
 public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
     // delegate是被委托对象SimpleExecutor, createCacheKey是BaseExecutor中的
     return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);
 }
 ​
 @Override
 public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
       throws SQLException {
     // 这里要看Cache的构建、MappedStatement的构建
     // 如果在mapper.xml中配置了<cache/>,解析mapper.xml的时候会构建Cache,赋值到Configuration中的Map<String, Cache> caches和MappedStatement的Cache
     Cache cache = ms.getCache();
 }
 ​

二、Cache的构建

 XMLMapperBuilder
 // mapper.xml解析
 // <cache type="" blocking="" eviction="" flushInterval="" readOnly="" size=""/>
 // <cache-ref namespace=""/>
 private void configurationElement(XNode context) {
     cacheRefElement(context.evalNode("cache-ref"));
     cacheElement(context.evalNode("cache"));
 }
 ​
 private void cacheElement(XNode context) {
     if (context != null) {
         // 默认PERPETUAL
         String type = context.getStringAttribute("type", "PERPETUAL");
         Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
         // 默认LRU--我理解是清除缓存的机制
         String eviction = context.getStringAttribute("eviction", "LRU");
         Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
         Long flushInterval = context.getLongAttribute("flushInterval");
         Integer size = context.getIntAttribute("size");
         boolean readWrite = !context.getBooleanAttribute("readOnly", false);
         boolean blocking = context.getBooleanAttribute("blocking", false);
         Properties props = context.getChildrenAsProperties();
         builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
     }
 }
 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);
         currentCache = cache;
     return cache;
 }
 CacheBuilder
 public Cache build() {
     setDefaultImplementations();
     Cache cache = newBaseCacheInstance(implementation, id);
     setCacheProperties(cache);
     // issue #352, do not apply decorators to custom caches
     if (PerpetualCache.class.equals(cache.getClass())) {
         for (Class<? extends Cache> decorator : decorators) {
             cache = newCacheDecoratorInstance(decorator, cache);
             setCacheProperties(cache);
         }
         // 标准的装饰器
         cache = setStandardDecorators(cache);
     } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
         cache = new LoggingCache(cache);
     }
     return cache;
 }
 ​
 // cacheClass是decorator
 // 装饰器LruCache使用反射构造包含this.delegate = delegate;的对象,delegate当前是Perpetual
 private Cache newCacheDecoratorInstance(Class<? extends Cache> cacheClass, Cache base) {
     Constructor<? extends Cache> cacheConstructor = getCacheDecoratorConstructor(cacheClass);
     try {
         return cacheConstructor.newInstance(base);
     } catch (Exception e) {
         throw new CacheException("Could not instantiate cache decorator (" + cacheClass + "). Cause: " + e, e);
     }
 }
 ​
 private Cache setStandardDecorators(Cache cache) {
     try {
         MetaObject metaCache = SystemMetaObject.forObject(cache);
         if (size != null && metaCache.hasSetter("size")) {
             metaCache.setValue("size", size);
         }
         if (clearInterval != null) {
             cache = new ScheduledCache(cache);
             ((ScheduledCache) cache).setClearInterval(clearInterval);
         }
         if (readWrite) {
             cache = new SerializedCache(cache);
         }
             cache = new LoggingCache(cache);
             cache = new SynchronizedCache(cache);
         if (blocking) {
             cache = new BlockingCache(cache);
         }
         return cache;
     } catch (Exception e) {
         throw new CacheException("Error building standard cache decorators.  Cause: " + e, e);
     }
 }

没有额外添加配置的情况下,最终的Cache对象

image-20240312115857417

此时Configuration和MapperBuilderAssistant

 protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
 public void addCache(Cache cache) {
     caches.put(cache.getId(), cache);
 }
 ​
 private Cache currentCache;
 currentCache = cache;

三、MappedStatement构建

 XMLMapperBuilder
 private void configurationElement(XNode context) {
     // 解析mapper.xml中的CRUD标签,此时builderAssistant中的currentCache已经赋值
     buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
 }
 ​
 private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
     for (XNode context : list) {
         final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
         try {
             statementParser.parseStatementNode();
         } catch (IncompleteElementException e) {
             configuration.addIncompleteStatement(statementParser);
         }
     }
 }
 XMLStatementBuilder
 public void parseStatementNode() {
     boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
     boolean useCache = context.getBooleanAttribute("useCache", isSelect);
     builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
         fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
         resultSetTypeEnum, flushCache, useCache, resultOrdered,
         keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
 }
 <select id="selectUser" resultType="com.lazy.snail.domain.User" useCache="false" flushCache="false">
     select * from user
 </select>
 MapperBuilderAssistant
 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) {
     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))
         // currentCache被赋值给了ms
         .cache(currentCache);
 ​
     ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
     if (statementParameterMap != null) {
         statementBuilder.parameterMap(statementParameterMap);
     }
 ​
     MappedStatement statement = statementBuilder.build();
     configuration.addMappedStatement(statement);
     return statement;
 }

总结

  • 1.在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置
  • 2.在映射文件中设置标签
  • 3.二级缓存必须在SqlSession关闭或提交之后有效
  • 4.查询的数据所转换的实体类类型必须实现序列化的接口