Mybatis源码解析之懒加载(一):配置和ResultLoaderMap

版权声明:感谢您的阅读,欢迎讨论并指出不足,可自由转载,但请注明出处和作者 https://blog.csdn.net/qq_39470742/article/details/88794755

Mybatis源码解析之核心类分析
Mybatis源码解析之初始化分析
Mybatis源码解析之执行流程解析
Mybatis源码解析之数据库连接和连接池
Mybatis源码解析之事务管理
Mybatis源码解析之缓存机制(一):一级缓存
Mybatis源码解析之缓存机制(二):二级缓存
Mybatis源码解析之插件机制
Mybatis源码解析之mapper接口的代理模式
Mybatis源码解析之DefaultResultSetHandler的handleResultSets方法解析
Mybatis源码解析之Spring集成mybatis-spring分析

在前面的文章里,我们已经提到,mybatis支持懒加载的方式去查询对象的属性。本篇文章,我们将针对懒加载的实现源码展开解析。

一. 懒加载的配置

1. setting节点

在全局配置的setting节点中,有3个配置属性与懒加载相关。

proxyFactory	指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。	CGLIB | JAVASSIST	JAVASSIST (MyBatis 3.3 以上)
lazyLoadingEnabled	延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。	true | false	false
aggressiveLazyLoading	当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载(参考 lazyLoadTriggerMethods)。	true | false	false (在 3.4.1 及之前的版本默认值为 true)

这些个配置对应configuration对象的lazyLoadingEnabled和aggressiveLazyLoading属性,解析代码在XMLConfigBuilder#settingsElement(properties)方法中的下面两条语句

configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
protected ProxyFactory proxyFactory = new JavassistProxyFactory();

2. resultMap节点

具体到某个类对象的某个属性是否需要开启,则可以通过resultMap中子节点的fetchType 属性(eager或lazy)来覆盖全局配置,如:

<collection property="list" column="id" fetchType="lazy" select="com.xxx.mapper.xxxMapper.selectById">

对象ResultMapping类对象的lazy属性。
具体的解析代码在XMLMapperBuilder#buildResultMappingFromContext(XNode, Class<?>, List<ResultFlag>的boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));语句。

二、ResultLoaderMap

ResultLoadMap用来表示需要懒加载的属性集,本质是一个HashMap:private final Map<String, LoadPair> loaderMap = new HashMap<String, LoadPair>();

1. ResultLoaderMap初始化

在DefaultSetHandler的getRowValue方法中,进行ResultLoadMap的初始化:final ResultLoaderMap lazyLoader = new ResultLoaderMap();

2. ResultLoadMap#addLoader(String, MetaObject, ResultLoader)

public void addLoader(String property, MetaObject metaResultObject, ResultLoader resultLoader) {
  String upperFirst = getUppercaseFirstProperty(property);
  if (!upperFirst.equalsIgnoreCase(property) && loaderMap.containsKey(upperFirst)) {
    throw new ExecutorException("Nested lazy loaded result property '" + property +
            "' for query id '" + resultLoader.mappedStatement.getId() +
            " already exists in the result map. The leftmost property of all lazy loaded properties must be unique within a result map.");
  }
  loaderMap.put(upperFirst, new LoadPair(property, metaResultObject, resultLoader));
}

封装成LoadPair后,加入loaderMap。
该方法在DefaultResultSetHandler的getNestedQueryMappingValue方法中被调用。

private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
     throws SQLException {
   final String nestedQueryId = propertyMapping.getNestedQueryId();
   final String property = propertyMapping.getProperty();
   final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
   final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
   final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, propertyMapping, nestedQueryParameterType, columnPrefix);
   Object value = null;
   if (nestedQueryParameterObject != null) {
     final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
     final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
     final Class<?> targetType = propertyMapping.getJavaType();
     //一级缓存中存在
     if (executor.isCached(nestedQuery, key)) {
       executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
       value = DEFERED;
     } else {
       final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
       if (propertyMapping.isLazy()) {
         lazyLoader.addLoader(property, metaResultObject, resultLoader);
         value = DEFERED;
       } else {
         value = resultLoader.loadResult();
       }
     }
   }
   return value;
 }

如果嵌套查询的值在一级缓存中存在,即使配置了懒加载,也不会执行后面介绍的懒加载机制,而是从缓存中获取,不过从缓存中缓存的值也是延迟加载的。
其它的getProperties、getPropertyNames、hasLoader、remove方法等也是对LoadPair的操作。

3. ResultLoadMap#load(String)

public boolean load(String property) throws SQLException {
  LoadPair pair = loaderMap.remove(property.toUpperCase(Locale.ENGLISH));
  if (pair != null) {
    pair.load();
    return true;
  }
  return false;
}

加载并且将其从loaderMap中移除。

扫描二维码关注公众号,回复: 5662893 查看本文章

4. ResultLoadMap#loadAll()

public void loadAll() throws SQLException {
  final Set<String> methodNameSet = loaderMap.keySet();
  String[] methodNames = methodNameSet.toArray(new String[methodNameSet.size()]);
  for (String methodName : methodNames) {
    load(methodName);
  }
}

遍历loaderMap调用load方法。

三、LoadPair

LoadPair是对于单个属性懒加载信息的封装,最重要的就是其load方法用来加载属性。

public void load(final Object userObject) throws SQLException {
   if (this.metaResultObject == null || this.resultLoader == null) {
     if (this.mappedParameter == null) {
       throw new ExecutorException("Property [" + this.property + "] cannot be loaded because "
               + "required parameter of mapped statement ["
               + this.mappedStatement + "] is not serializable.");
     }

     final Configuration config = this.getConfiguration();
     final MappedStatement ms = config.getMappedStatement(this.mappedStatement);
     if (ms == null) {
       throw new ExecutorException("Cannot lazy load property [" + this.property
               + "] of deserialized object [" + userObject.getClass()
               + "] because configuration does not contain statement ["
               + this.mappedStatement + "]");
     }

     this.metaResultObject = config.newMetaObject(userObject);
     this.resultLoader = new ResultLoader(config, new ClosedExecutor(), ms, this.mappedParameter,
             metaResultObject.getSetterType(this.property), null, null);
   }

   /* We are using a new executor because we may be (and likely are) on a new thread
    * and executors aren't thread safe. (Is this sufficient?)
    *
    * A better approach would be making executors thread safe. */
    * 新建resultLoader 保证线程安全
   if (this.serializationCheck == null) {
     final ResultLoader old = this.resultLoader;
     this.resultLoader = new ResultLoader(old.configuration, new ClosedExecutor(), old.mappedStatement,
             old.parameterObject, old.targetType, old.cacheKey, old.boundSql);
   }

   this.metaResultObject.setValue(property, this.resultLoader.loadResult());
 }

四、ResultLoader

LoadPair又将具体的加载逻辑委托给ResultLoader执行。

public Object loadResult() throws SQLException {
//获取查询结果
  List<Object> list = selectList();
  //结果值转化
  resultObject = resultExtractor.extractObjectFromList(list, targetType);
  return resultObject;
}

private <E> List<E> selectList() throws SQLException {
  Executor localExecutor = executor;
  //executor不是线程安全的,如果线程不同需要新建executor
  if (Thread.currentThread().getId() != this.creatorThreadId || localExecutor.isClosed()) {
    localExecutor = newExecutor();
  }
  try {
    return localExecutor.<E> query(mappedStatement, parameterObject, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, cacheKey, boundSql);
  } finally {
    if (localExecutor != executor) {
      localExecutor.close(false);
    }
  }
}

private Executor newExecutor() {
  final Environment environment = configuration.getEnvironment();
  if (environment == null) {
    throw new ExecutorException("ResultLoader could not load lazily.  Environment was not configured.");
  }
  final DataSource ds = environment.getDataSource();
  if (ds == null) {
    throw new ExecutorException("ResultLoader could not load lazily.  DataSource was not configured.");
  }
  final TransactionFactory transactionFactory = environment.getTransactionFactory();
  final Transaction tx = transactionFactory.newTransaction(ds, null, false);
  return configuration.newExecutor(tx, ExecutorType.SIMPLE);
}

在完成查询得到list后,需要进行类型转化,得到需要的类型值。具体逻辑委托给ResultExtractor#extractObjectFromList(List<Object>, Class<?>)。

public Object extractObjectFromList(List<Object> list, Class<?> targetType) {
  Object value = null;
  if (targetType != null && targetType.isAssignableFrom(list.getClass())) {
  //list或list的父类,直接赋值
    value = list;
  } else if (targetType != null && objectFactory.isCollection(targetType)) {
  //集合性质的类,新建后通过addAll加入
    value = objectFactory.create(targetType);
    MetaObject metaObject = configuration.newMetaObject(value);
    metaObject.addAll(list);
  } else if (targetType != null && targetType.isArray()) {
  //数组
    Class<?> arrayComponentType = targetType.getComponentType();
    Object array = Array.newInstance(arrayComponentType, list.size());
    if (arrayComponentType.isPrimitive()) {
      for (int i = 0; i < list.size(); i++) {
        Array.set(array, i, list.get(i));
      }
      value = array;
    } else {
      value = list.toArray((Object[])array);
    }
  } else {
    if (list != null && list.size() > 1) {
    //无法处理,抛出异常
      throw new ExecutorException("Statement returned more than one row, where no more than one was expected.");
    } else if (list != null && list.size() == 1) {
    //返回元素值
      value = list.get(0);
    }
  }
  return value;
}

猜你喜欢

转载自blog.csdn.net/qq_39470742/article/details/88794755
今日推荐