美文网首页
MyBatis原理(四)——SQL处理器StatementHan

MyBatis原理(四)——SQL处理器StatementHan

作者: Lnstark | 来源:发表于2021-12-05 23:32 被阅读0次

继续上一篇的内容...

3.4 结果集的处理

我们回到SimpleExecutor的doQuery方法,设置完参数那就剩执行查询了

return handler.query(stmt, resultHandler);

StatementHandler的query交给子类实现了,我们就看PreparedStatementHandler的实现:

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  // 转换为PreparedStatement
  PreparedStatement ps = (PreparedStatement) statement;
  // 直接执行
  ps.execute();
  // 用结果集处理器处理结果
  return resultSetHandler.handleResultSets(ps);
}

ResultSetHandler只有一个实现类DefaultResultSetHandler,结果集解析的精髓基本全在这个类里了。

在看解析过程之前我们先过一下整体的流程:

执行完SQL之后,读取ResultSet数据,并将每一行转换成相对应的对象。用户可在转换的过程当中可以通过ResultContext来控制是否要继续转换。转换后的对象都会暂存在ResultHandler中最后统一封装成list返回给调用方。

结果集处理.png

ResultSetHandler的handleResultSets是处理多结果集,里面有个循环遍历每个结果集来处理,但一般我们的查询都只有一个结果集,存储过程才会有多个结果集。我们看里面的处理单结果集方法handleResultSet:

// ResultSetWrapper是对JDBC的ResultSet的封装,便于取数据。
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
  try {
    if (parentMapping != null) {
      handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
    } else {
      // 如果结果处理器为空,就创建一个默认的,其实只是用来存储
      if (resultHandler == null) {
        DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
        // 处理结果集
        handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
        // 将结果放到multipleResults
        multipleResults.add(defaultResultHandler.getResultList());
      } else {
        handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
      }
    }
  } finally {
    // issue #228 (close resultsets)
    closeResultSet(rsw.getResultSet());
  }
}

handleRowValues:

public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
  // 如果是嵌套结果行
  if (resultMap.hasNestedResultMaps()) {
    // 嵌套结果集不支持分页
    ensureNoRowBounds();
    // 不推荐用自定义结果处理器,要用的话,要么设置safeResultHandlerEnabled为true,
    // 要么设置mappedStatement的resultOrdered为true
    checkResultHandler();
    handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
  } else {
    // 处理简单结果行
    handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
  }
}

我们先看简单结果行的处理handleRowValuesForSimpleResultMap:

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
  // 结果数据上下文
  DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
  // jdbc结果集
  ResultSet resultSet = rsw.getResultSet();
  // 如果设置了RowBounds,那么就跳到rowBounds.offset行。
  // 如果ResultSet是TYPE_FORWARD_ONLY类型的,那就直接定位到目标行,否则就一行行的跳
  skipRows(resultSet, rowBounds);
  
  // shouldProcessMoreRows: 
  //    如果resultContext.isStopped为true(可手动改)或者resultContext里数据数量达到rowBounds.limit的话
  //    那就返回false不继续处理
  while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
    // 如果ResultMap里存在鉴别器discriminator,那就先解析鉴别器,解析逻辑:
    //    取出ResultMap里的discriminator,然后用它里面的ResultMapping里的TypeHandler解析出结果
    //    如果解析的结果是个ResultMap的id,那就从configuration里取出这个id对应的ResultMap,继续解析
    //    循环直到结果不是ResultMap的id为止,然后返回ResultMap。
    ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
    // 获取行数据
    Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
    // 保存对象:如果parentMapping为null的话,直接将结果保存在resultHandler和resultContext里
    // 否则将结果保存在parentMapping里
    storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
  }
}

这里先解释一下ResultMapping,我们的<ResultMap></ResultMap>下面有<id/>、<result/>、<association/>、<constructor/>、<collection/>,这些都是ResultMapping。

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
  final ResultLoaderMap lazyLoader = new ResultLoaderMap();
  // 创建一个空的结果对象
  Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
  // 如果对象不为空且没找到结果处理器
  if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    // 用MetaObject包装
    final MetaObject metaObject = configuration.newMetaObject(rowValue);
    boolean foundValues = this.useConstructorMappings;
    // 看是否走自动装配:
    //  如果配了AutoMapping为true,或者嵌套ResultMap下AutoMappingBehavior配了full,
    //  或者非嵌套ResultMap下AutoMappingBehavior非none,那么走自动装配
    if (shouldApplyAutomaticMappings(resultMap, false)) {
      foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
    }
    // 非自动装配
    foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
    foundValues = lazyLoader.size() > 0 || foundValues;
    rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
  }
  return rowValue;
}

接着看自动装配逻辑,其实就是找到ResultSet里有的列,而ResultMap里没配的列,对这些列进行处理。

private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
  // 构造自动装配的mapping,mapping里面包含表的列名、字段属性、处理器(转化结果)和字段是否为基本类型
  // 不同的字段类型有不同的处理器,如String对应的StringTypeHandler,里面会调用ResultSet的getString方法,取出字段值。
  List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
  boolean foundValues = false;
  // 如果不为空
  if (!autoMapping.isEmpty()) {
    // 遍历
    for (UnMappedColumnAutoMapping mapping : autoMapping) {
      // 通过handler取出值
      final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
      if (value != null) {
        foundValues = true;
      }
      // 如果值不为空,或者callSettersOnNulls配置为true且字段不是基本类型的话,那就给对象设置属性值
      if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
        // gcode issue #377, call setter on nulls (value is not 'found')
        metaObject.setValue(mapping.property, value);
      }
    }
  }
  return foundValues;
}

// 构造自动装配的mapping
private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
  final String mapKey = resultMap.getId() + ":" + columnPrefix;
  // 先从缓存里取,如果没有的话再创建
  List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
  if (autoMapping == null) {
    autoMapping = new ArrayList<>();
    // 获取查询的结果集里存在的,而ResultMap里没配的列名
    final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
    // 遍历列名
    for (String columnName : unmappedColumnNames) {
      String propertyName = columnName;
      // 去除前缀
      if (columnPrefix != null && !columnPrefix.isEmpty()) {
        // When columnPrefix is specified,
        // ignore columns without the prefix.
        if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
          propertyName = columnName.substring(columnPrefix.length());
        } else {
          continue;
        }
      }
      // 将列名转为属性名,如果配了mapUnderscoreToCamelCase,那就从下划线转为驼峰
      final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
      // 如果获取到的属性名不为空且有setter方法
      if (property != null && metaObject.hasSetter(property)) {
        // 如果ResultMap里有该字段那么跳过,交给手动装配处理该字段
        if (resultMap.getMappedProperties().contains(property)) {
          continue;
        }
        // 否则去类型处理器注册器里找有没有对应类型的处理器,有的话也加上
        final Class<?> propertyType = metaObject.getSetterType(property);
        if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
          final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
          autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
        } else {
          // 否则就执行找不到匹配列的对应处理方式
          // AutoMappingUnknownColumnBehavior有三种配置:
          // NONE: 啥都不干; WARNING: 打印一下; FAILING: 抛出异常
          configuration.getAutoMappingUnknownColumnBehavior()
              .doAction(mappedStatement, columnName, property, propertyType);
        }
      } else {
        // 同上
        configuration.getAutoMappingUnknownColumnBehavior()
            .doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
      }
    }
    // 加到缓存里
    autoMappingsCache.put(mapKey, autoMapping);
  }
  return autoMapping;
}

然后看手动装配:

private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
    throws SQLException {
  // 获取ReusultMap里和ResultSet里都有的列名,且都被转为大写
  final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
  boolean foundValues = false;
  // 获取resultMap下的所有ResultMapping,遍历
  final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
  for (ResultMapping propertyMapping : propertyMappings) {
    // 列名加前缀
    String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
    // 如果设置了resultMap,那就忽略column配置,列名就取resultMap里的
    if (propertyMapping.getNestedResultMapId() != null) {
      // the user added a column attribute to a nested result map, ignore it
      column = null;
    }
    // 条件1:是复合列,像column="{id1=id, name=name}"
    // 条件2:列名不为空且mappedColumnNames里包含了该列名
    // 条件3:resultSet不为空。这个一般都为空,因为我们一般都是查询返回一个结果集的,返回多结果集才会选resultSet
    if (propertyMapping.isCompositeResult()
        || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
        || propertyMapping.getResultSet() != null) {
      // 获取字段值
      Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
      // issue #541 make property optional
      final String property = propertyMapping.getProperty();
      // 属性名为空则跳过
      if (property == null) {
        continue;
      } else if (value == DEFERRED) {
        // 延迟赋值
        foundValues = true;
        continue;
      }
      if (value != null) {
        foundValues = true;
      }
      // 设置属性
      if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
        // gcode issue #377, call setter on nulls (value is not 'found')
        metaObject.setValue(property, value);
      }
    }
  }
  return foundValues;
}

// 获取属性值
private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
    throws SQLException {
  // 如果配了select,就走嵌套查询逻辑
  if (propertyMapping.getNestedQueryId() != null) {
    return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
  } else if (propertyMapping.getResultSet() != null) {
    // 配了结果集的话
    addPendingChildRelation(rs, metaResultObject, propertyMapping);   // TODO is that OK?
    return DEFERRED;
  } else {
    // 否则直接通过TypeHandler取值,里面就是调ResultSet取值方法
    final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
    final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
    return typeHandler.getResult(rs, column);
  }
}

// 嵌套子查询
private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
    throws SQLException {
  // select id
  final String nestedQueryId = propertyMapping.getNestedQueryId();
  // 属性名
  final String property = propertyMapping.getProperty();
  // 根据select id拿到MappedStatement
  final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
  // 参数类型
  final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
  // 根据column和结果集里的值构造出参数对象,如果是复合参数,会构造出一个map
  final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, propertyMapping, nestedQueryParameterType, columnPrefix);
  Object value = null;
  // 如果配了select的话,column也必须要配,所以nestedQueryParameterObject不为空
  if (nestedQueryParameterObject != null) {
    // BoundSql里面包含SQL语句和参数对象
    final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
    // 获取缓存key,这里的executor是构造StatementHandler时传进来的,就是执行查询的executor
    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 = DEFERRED;
    } else {
      // 创建结果加载器
      final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
      // 是否支持懒加载:
      //  开了全局配置lazyLoadingEnabled,或者association或collection配了fetchType="lazy",那就支持懒加载
      //  全局配置aggressiveLazyLoading为true表示如果对具有懒加载特性的对象的任意调用会导致这个对象的完整加载,false表示每种属性按照需要加载。
      if (propertyMapping.isLazy()) {
        lazyLoader.addLoader(property, metaResultObject, resultLoader);
        value = DEFERRED;
      } else {
        value = resultLoader.loadResult();
      }
    }
  }
  return value;
}

我们具体看下延迟加载和懒加载。

延迟加载
延迟加载可以解决循环依赖,存在一级缓存的情况下会走这个,一般是子查询依赖父查询的时候执行这段逻辑,因为第一次执行父查询是没有一级缓存的。我们先回顾一下BaseExecutor的queryFromDatabase方法:

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  List<E> list;
  // 先在一级缓存里放个占位符
  localCache.putObject(key, EXECUTION_PLACEHOLDER);
  try {
    // 执行查询,里面会执行子查询
    list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
  } finally {
    // 执行完了删除占位符
    localCache.removeObject(key);
  }
  // 将结果放到一级缓存
  localCache.putObject(key, list);
  if (ms.getStatementType() == StatementType.CALLABLE) {
    localOutputParameterCache.putObject(key, parameter);
  }
  return list;
}

再看BaseExecutor的deferLoad方法:

public void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType) {
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  // 构造延迟加载器,这是BaseExecutor内部类,所以可以拿到一级缓存
  // 这里resultObject是子查询的一行(一个对象), property是对象的属性, 
  // key是父查询的结果的缓存key,localCache是一级缓存
  DeferredLoad deferredLoad = new DeferredLoad(resultObject, property, key, localCache, configuration, targetType);
  // 根据加载器内部的缓存key,判断localCache.get(key)不为空且不为EXECUTION_PLACEHOLDER(占位符)的话,
  // 就说明一级缓存里一级有了,那就直接拿。循环依赖的情况下localCache.get(key)为占位符,所以can not load
  if (deferredLoad.canLoad()) {
    // 根据key从一级缓存里取出缓存然后设置属性值
    deferredLoad.load();
  } else {
    // 放到队列里,等所有查询执行完了之后再从一级缓存里加载父查询的结果,复制给子查询的对应属性
    deferredLoads.add(new DeferredLoad(resultObject, property, key, localCache, configuration, targetType));
  }
}

丢到延迟加载队列之后,什么时候才会加载呢?我们再回想下BaseExecutor的query方法里,有个queryStack,表示当前的查询层级,父查询的话为1,第一个子查询为2,以此类推。当queryStack == 0说明所有的查询都执行完了,但是子查询可能还有一些延迟加载的,那就在此加载。逻辑简单不贴代码了。

参考Mybatis源码手记-从缓存体系看责任链派发模式与循环依赖企业级实践,这篇讲的比较详细。

懒加载
懒加载意思就是在bean调用getter等方法取值的时候才去执行子查询,参数配置如下:

参数 描述 默认
lazyLoadingEnabled 全局懒加载开关 false
aggressiveLazyLoading 任意方法触发加载 false
fetchType 加载方式 eager实时 lazy懒加载 eager

当调用setXXX方法手动设置属性之后,对应的属性懒加载将会被移除,不会覆盖手动设置的值。
当对象经过序列化和反序列化之后,默认不在支持懒加载。但如果在全局参数中设置了configurationFactory类,而且采用JAVA原生序列化是可以正常执行懒加载的。其原理是将懒加载所需参数以及配置一起进行序列化,反序列化后在通过configurationFactory获取configuration构建执行环境。
configurationFactory 是一个包含 getConfiguration 静态方法的类

public static class ConfigurationFactory {
    public static Configuration getConfiguration() {
        return configuration;
    }
}

原来就是通过对Bean的动态代理,重写所有属性的getXxx方法。在获取属性前先判断属性是否加载?然后加载之。

代理后的bean结构.png

在解析行数据的时候,如果遇到需要懒加载的属性,mybatis会通过javassist动态代理创建代理对象。回头再来看解析行数据方法getRowValue:

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
  // 创建ResultLoaderMap用于存储待懒加载的属性
  final ResultLoaderMap lazyLoader = new ResultLoaderMap();
  // 如果有ResultMapping是配了懒加载的话,就创建代理对象,否则就是原类型的对象
  // 代理对象通过JavassistProxyFactory的createProxy方法创建
  Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
  if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    final MetaObject metaObject = configuration.newMetaObject(rowValue);
    boolean foundValues = this.useConstructorMappings;
    if (shouldApplyAutomaticMappings(resultMap, false)) {
      foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
    }
    // 手动装配时传进去,在遇到需要懒加载的属性时,加到lazyLoader里
    // 具体就是在上面的getNestedQueryMappingValue方法里
    foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
    foundValues = lazyLoader.size() > 0 || foundValues;
    rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
  }
  return rowValue;
}

下面看JavassistProxyFactory创建代理对象的过程:

// JavassistProxyFactory的createProxy方法
@Override
public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
  return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
}

// EnhancedResultObjectProxyImpl的createProxy方法
public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
  final Class<?> type = target.getClass();
  // MethodHandler的实现类,里面包含ResultLoaderMap、Configuration等
  EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
  // 创建代理
  Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
  // 属性复制
  PropertyCopier.copyBeanProperties(type, target, enhanced);
  return enhanced;
}

static Object crateProxy(Class<?> type, MethodHandler callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
  // 利用javassist的ProxyFactory创建动态代理类
  ProxyFactory enhancer = new ProxyFactory();
  enhancer.setSuperclass(type);

  try {
    // 判断类型里是否存在writeReplace方法,如果不存在就会抛出NoSuchMethodException被下面捕获
    // 当writeReplace方法被实现后,序列化机制会先调用writeReplace方法将当前对象替换成另一个对象(该方法会返回替换后的对象)并将其写入流中
    type.getDeclaredMethod(WRITE_REPLACE_METHOD);
    // ObjectOutputStream will call writeReplace of objects returned by writeReplace
    if (LogHolder.log.isDebugEnabled()) {
      LogHolder.log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
    }
  } catch (NoSuchMethodException e) {
    // 如果实现了writeReplace方法,代理类就要实现WriteReplaceInterface接口,保证代理类里有writeReplace方法
    enhancer.setInterfaces(new Class[] { WriteReplaceInterface.class });
  } catch (SecurityException e) {
    // nothing to do here
  }

  Object enhanced;
  Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
  Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
  try {
    // 创建代理对象
    enhanced = enhancer.create(typesArray, valuesArray);
  } catch (Exception e) {
    throw new ExecutorException("Error creating lazy proxy.  Cause: " + e, e);
  }
  // 设置handler
  ((Proxy) enhanced).setHandler(callback);
  return enhanced;
}

下面是重点:EnhancedResultObjectProxyImpl的invoke方法的实现

public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
    final String methodName = method.getName();
    try {
      synchronized (lazyLoader) {
        // 如果方法名为writeReplace,那就说明正在执行序列化
        if (WRITE_REPLACE_METHOD.equals(methodName)) {
          // 创建一个用于序列化的原始类型对象(非代理对象)
          Object original;
          if (constructorArgTypes.isEmpty()) {
            original = objectFactory.create(type);
          } else {
            original = objectFactory.create(type, constructorArgTypes, constructorArgs);
          }
          // 赋值属性
          PropertyCopier.copyBeanProperties(type, enhanced, original);
          // 如果还有未加载的属性,那就创建一个JavassistSerialStateHolder来序列化
          if (lazyLoader.size() > 0) {
            return new JavassistSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
          } else {
            // 序列化original
            return original;
          }
        } else {
          // 如果ResultLoaderMap里还有属性未加载并且当前执行的方法不为finalize
          // (执行finalize方法时不进行懒加载,对象都要被回收了,还加载个锤子)
          if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
            // 如果aggressive-lazy-loading为true,那么执行任意方法(除finalize)都会懒加载所有
            // 如果aggressive-lazy-loading为false,那么lazyLoadTriggerMethods里包含了方法名的话也会懒加载所有
            if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
              lazyLoader.loadAll();
            } else if (PropertyNamer.isSetter(methodName)) {
              // 如果执行了setter方法,那么移除对应属性,该属性之后也不进行懒加载了
              final String property = PropertyNamer.methodToProperty(methodName);
              lazyLoader.remove(property);
            } else if (PropertyNamer.isGetter(methodName)) {
              // 如果执行了getter方法,并且ResultLoaderMap有该属性,那就懒加载该属性
              final String property = PropertyNamer.methodToProperty(methodName);
              if (lazyLoader.hasLoader(property)) {
                lazyLoader.load(property);
              }
            }
          }
        }
      }
      // 执行bean的原始方法
      return methodProxy.invoke(enhanced, args);
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
}

懒加载对象的序列化和反序列化
JavassistSerialStateHolder里保存了原始对象,未加载的属性,对象工厂,构造方法参数类型和构造方法参数,用于反序列化时还原出当前的代理对象。
JavassistSerialStateHolder继承了AbstractSerialStateHolder,AbstractSerialStateHolder实现了Externalizable。序列化时,writeExternal方法将上面5个字段依次写到ObjectOutputStream流,最后转成字节数组再保存。反序列化时,先用readExternal方法读出字节数组,readResolve方法(反序列化时会自动调用)将字节数组通过ObjectInputStream读出这5个字段,最后再用JavassistProxyFactory根据这5个字段创建还原出代理对象。

属性的加载
我们看ResultLoaderMap的load方法:

public boolean load(String property) throws SQLException {
  // 取出loaderMap里对应属性的LoadPair,执行load
  LoadPair pair = loaderMap.remove(property.toUpperCase(Locale.ENGLISH));
  if (pair != null) {
    pair.load();
    return true;
  }
  return false;
}

LoadPair的load方法:

public void load() throws SQLException {
  /* These field should not be null unless the loadpair was serialized.
   * Yet in that case this method should not be called. */
  if (this.metaResultObject == null) {
    throw new IllegalArgumentException("metaResultObject is null");
  }
  if (this.resultLoader == null) {
    throw new IllegalArgumentException("resultLoader is null");
  }

  this.load(null);
}

public void load(final Object userObject) throws SQLException {
  // metaResultObject和resultLoader都是transient类型的,序列化时不带上,
  // 所以如果他们是空的话,说明经过序列化了
  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.");
    }
    // 通过configurationFactory的getConfiguration静态方法取出Configuration
    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);
    // 创建ResultLoader
    // 这里传入一个ClosedExecutor,下面执行数据加载时,会判断Executor是否已关闭,是的话就创建一个新的执行器
    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);
  }
  // 执行数据加载,就是在resultLoader里创建一个SimpleExecutor进行查询,最后赋值给metaResultObject
  this.metaResultObject.setValue(property, this.resultLoader.loadResult());
}

嵌套结果集的处理
上面的简单结果集处理handleRowValuesForSimpleResultMap主要分3步:跳到目标行然后开始遍历,获取每行的值,保存值。而嵌套结果集稍微复杂一点,他可能要将关联查询的结果以一对多的形式保存的对象里,那如何将主表的那几条重复数据对应到一个主对象里呢?他这里是利用resultMap里的所有<id/>列(没有<id/>的话就所有列)列名和值来创建一个Cachekey,作为表示同一条主表数据的标识。遍历的时候递归解析行数据,将解析结果缓存在一个map里(Map<CacheKey, Object> nestedResultObjects),解析下一行的时候,先从缓存里获取,如果没有的话就创建新对象,如果有的话就拿出来,填充子属性。

解析流程.png

参考资料

B站——MyBatis源码解析大合集
源码阅读网

相关文章

网友评论

      本文标题:MyBatis原理(四)——SQL处理器StatementHan

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