MyBatis 源码分析-05 - MyBatis ResultSet 处理流程分析

image-20210325161354612

限于篇幅, 本文仅解释resultSetHandler.handleResultSets()这一过程, 其他内容请参考其他文章.

Version :

  • MyBatis : 3.5.5

  • 项目地址 :MyBatis-source-learn

  • PS: 若文章字体偏大或者偏小,建议通过 ctrl键+鼠标滑轮 进行修改,以提升阅读效果.(带来不便,请谅解!)

流程:

DefaultResultSetHandler.handleResultSets();
 1. ResultSetWrapper rsw = getFirstResultSet(stmt);// 通过 Statement 获取resultSet,并包装
 2. handleResultSet(rsw, resultMap, multipleResults, null); // 操作statement , 并关闭
   1.handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);// 处理行数据 ,封装结果对象resultObject
	 1.handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
	   1.Object rowValue = getRowValue(rsw, discriminatedResultMap, null);// 获取行数据
			// 1. 实例化 resultObject, 根据class 创建 对应的类  (重要!!!)
			1. Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
			//2.  处理unMapped字段即自动注入.  将 JavaBean的属性 与 列属性 进行映射,并赋值  (重要!!!)
			2. applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
					// 1. 将列 与JavaBean属性 相绑定(columnName, property, typeHandler, propertyType) 
					1. autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
					// 2.赋值.从列中取值,利用反射机制注入JavaBean对应的字段中即:metaObject.originObject(即rowValue中
					2.   metaObject.setValue(mapping.property, value);
             // 3. 处理 mapped 字段. 将JavaBean属性与列属性 相互映射, 并赋值(过程与2 -- 大致相同,不再赘述)
            3.applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix)
       2.storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);//将rowValue加入到list中

测试代码:

ps :其他代码请自己去MyBatis-source-learn查看

测试类:

  • CreateSqlSession.java
public class CreateSqlSession {

    public static void main(String[] args) {
//         1. 连接数据库,配置基本参数
        DataSource dataSource = getDataSource();
        TransactionFactory transactionFactory = new JdbcTransactionFactory();
        Environment environment = new Environment("development",  transactionFactory, dataSource);
         Configuration configuration = new Configuration(environment);
//         2.  处理XXXMapper, 生成SqlSource(即处理xml 以及注解生成的SQL语句 与PreparedStatement绑定)
        configuration.addMapper(BlogMapper.class);
        // 3.  获取SqlSession
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
        SqlSession sqlSession = sqlSessionFactory.openSession();
//         4. 获取mapper,执行方法
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        for (int i = 0; i < 10; i++) {
            Blog blog = mapper.selectBlog(1);
            System.out.println("i: "+blog);
        }
    }

**PS: MyBatis 默认查询 顺序是 二级缓存 (CachedExecutor.cache) -> 一级缓存(BaseExecutor.localCache) -> 数据库. **

人话描述总流程:

1. 获取 resultSet 即 ResultSetWrapper rsw = getFirstResultSet(stmt);
2. 处理resultSet , nestedResultSet 即  handleResultSet(rsw, resultMap, multipleResults, null);
     2.1  略过, 标签处理 ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);// 处理<>resultMap.Discriminate 接口
      2.2 Object rowValue = getRowValue(rsw, discriminatedResultMap, null);// 获取行数据
		2.2.1 实例化 ResultObjectcreateResultObject(rsw, resultMap, lazyLoader, columnPrefix)
        2.2.2 赋值 ResultObject 
        	1. unMapped 属性赋值 applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix)
             1.1将列与JavaBean属性进行绑定 createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
			 1.2 获取列数据值 即 mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
			 1.3 利用反射将值注入 即  metaObject.setValue(mapping.property, value);
            2. Mapped 属性赋值 applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) 
      2.3  将rowValue 加入到list 中 即storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet); 
3. 返回 list

源码分析:

  • PreparedStatementHandler.java
@Override
 public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
   PreparedStatement ps = (PreparedStatement) statement;
   ps.execute();//与数据库交互, 执行操作
   return resultSetHandler.handleResultSets(ps);// 处理结果集 ,将结果集与 resultmap映射,返回list对象
 }

方法执行流程堆栈信息:

说明: 下面我会给出整体流程 的代码, 会忽略部分无关重要的代码,可部分内容重要度不高,若不想看,可直接跳转到getRowValue(args)这个方法中,建议跳过, 若想看,建议速读.

image-20210324171350080

整体流程 :

说明: 下面我会给出整体流程 的代码, 会忽略部分无关重要的代码,可部分内容重要度不高,若不想看,可直接跳转到getRowValue(args)这个方法中,建议跳过, 若想看,建议速读.

  • DefaultResultSetHandler.java
handleResultSets(stmt)
@Override
 public List<Object> handleResultSets(Statement stmt) throws SQLException {
   ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
   // 查询方法的返回值 list.
   final List<Object> multipleResults = new ArrayList<>();

   int resultSetCount = 0;
   ResultSetWrapper rsw = getFirstResultSet(stmt);
   // 按照行依次处理 resultset ,
   List<ResultMap> resultMaps = mappedStatement.getResultMaps();//resultMaps 在构建mappedStatement(SqlSource) 时生成, 通过addMappedStatement() 加入到resultmaps中
   int resultMapCount = resultMaps.size();
   validateResultMapsCount(rsw, resultMapCount); //处理没有结果集的查询结果
   while (rsw != null && resultMapCount > resultSetCount) {
     ResultMap resultMap = resultMaps.get(resultSetCount);
     handleResultSet(rsw, resultMap, multipleResults, null);// 将resultset 与 resultmap相绑定
     rsw = getNextResultSet(stmt);
     cleanUpAfterHandlingResultSet();
     resultSetCount++;
   }
 // 处理嵌套resultset 即 nestedResultMap
   String[] resultSets = mappedStatement.getResultSets();
   if (resultSets != null) {
     while (rsw != null && resultSetCount < resultSets.length) {
       ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
       if (parentMapping != null) {
         String nestedResultMapId = parentMapping.getNestedResultMapId();
         ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
         handleResultSet(rsw, resultMap, null, parentMapping);
       }
       rsw = getNextResultSet(stmt);
       cleanUpAfterHandlingResultSet();
       resultSetCount++;
     }
   }
     
  • DefaultResultSetHandler.java
handleResultSet(args)

DefaultResultSetHandler.java

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.add(defaultResultHandler.getResultList());
       } else {
         handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
       }
     }
   } finally {
     // issue #228 (close resultsets)
     closeResultSet(rsw.getResultSet());
   }
 }

处理行数据

PS: 这一部分内容我会放较大部分比重, 这部分主要做了两件事,

1. **实例化resultObject , 即rowValue**
2.  **赋值 将DB中的列与JavaBean的字段想绑定,并赋值**
3. 在赋值过程中 注意 MetaObject
getRowValue(args)
  • DefaultResultSetHandler.java
 //
 // GET VALUE FROM ROW FOR SIMPLE RESULT MAP
 // 
// 实例化resultObject ,并且赋值 根据 resultSet 赋值
 private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
   final ResultLoaderMap lazyLoader = new ResultLoaderMap();
   Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);//1.创建 resultObject, 实例化但未初始化
   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;//处理unmapped 
     }// 处理mapped property
     foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
     foundValues = lazyLoader.size() > 0 || foundValues;
     rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
   }
   return rowValue;
 }
实例化

时刻回忆总流程 Go!

实例化调用堆栈信息:

image-20210324235122014

PS: 只会给出关键源码

createResultObject(args)
  • DefaultResultSetHandler.java
// 实例化 and 构造映射
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
  this.useConstructorMappings = false; // reset previous mapping result
  final List<Class<?>> constructorArgTypes = new ArrayList<>();//构造器方法 参数类型集合
  final List<Object> constructorArgs = new ArrayList<>(); // 构造器方法 参数集合
  Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
  if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
    for (ResultMapping propertyMapping : propertyMappings) {
      // issue gcode #109 && issue #149
      if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
        resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
        break;
      }
    }
  }
  this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
  return resultObject;
}
// 创建结果对象, 实例化 未初始化
private Object  createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
    throws SQLException {
  final Class<?> resultType = resultMap.getType();// 结果的返回类型 xxxclass eg:Blog selectBlog()中resultType = Blog
  final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);//反射创建
  final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();// xml文件配置的<Constructor>
  if (hasTypeHandlerForResultObject(rsw, resultType)) {// 处理基本数据类型
    return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
  } else if (!constructorMappings.isEmpty()) {// 构造方法不为空
    return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
  } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {// 判断返回值类型是接口?返回值(类)是否有构造器
    return objectFactory.create(resultType);// 利用ObjectFactory 反射的方式创建对应的class对象
  } else if (shouldApplyAutomaticMappings(resultMap, false)) {// 是否需要自动映射
    return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
  }
  throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
create(args)
  • DefaultObjectFactory.java
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
  Class<?> classToCreate = resolveInterface(type);//1 处理集合接口
  // we know types are assignable
  return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);// 2. 实例化
}
instantiateClass(args)
  • DefaultObjectFactory.java

  • 映射构造器方法 并 实例化类对象.

//类实例化 1. 无参构造 没有条件创造条件去执行 2. 有参构造   
  private  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    try {
      Constructor<T> constructor;//1. 无参构造
      if (constructorArgTypes == null || constructorArgs == null) {
        constructor = type.getDeclaredConstructor();
        try {
          return constructor.newInstance();// 1.1 尝试调用默认的构造方法
        } catch (IllegalAccessException e) {
          if (Reflector.canControlMemberAccessible()) {
            constructor.setAccessible(true); // 1.2 访问权限被拒 ,然后强制跳过访问权限生成实例, // TODO: 2021/3/13 应该抛异常, 不应该这么 处理
            return constructor.newInstance();
          } else {
            throw e;
          }
        }
      }//2. 处理带参构造器
      constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[0]));
      try {
        return constructor.newInstance(constructorArgs.toArray(new Object[0]));
      } catch (IllegalAccessException e) {
        if (Reflector.canControlMemberAccessible()) {
          constructor.setAccessible(true);
          return constructor.newInstance(constructorArgs.toArray(new Object[0]));
        } else {
          throw e;
        }
      }
    } catch (Exception e) {
      String argTypes = Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList)
          .stream().map(Class::getSimpleName).collect(Collectors.joining(","));
      String argValues = Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList)
          .stream().map(String::valueOf).collect(Collectors.joining(","));
      throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
    }
  }
赋值

PS : 以AutoMapping赋值过程为例

赋值-堆栈调用过程

image-20210324153347301

总方法:

applyAutomaticMappings(args)

  • DefaultResultSetHandler.java
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
   // 1. 获取 未映射的字段  以及与其对应的 JavaBean 属性 绑定的 列表
    List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
    boolean foundValues = false;
    if (!autoMapping.isEmpty()) {//2. 获取 列数据 值 ,即 value  =1 (id =1 )
      for (UnMappedColumnAutoMapping mapping : autoMapping) {//2.1 利用resultset(rs) 获取字段值rs.getString(name); //这里是将方法接口化,根据typeHandler 的类型来执行对应的方法getNullableResult()
        final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
        if (value != null) {
          foundValues = true;
        } // 3. 通过反射的方式获取set方法,对metaObject中的originObject(rowValue 即Blog) 字段进行赋值
        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;
  }
创建列与 JavaBean属性的映射

堆栈信息:

image-20210325001020017

即 :

List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
  • DefaultResultSetHandler.java
// 将未配对的 列名 与 JavaBean 的属性 ,type 进行 配对,并将结果缓存处理
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) { //1.缓存中没有则创建
    autoMapping = new ArrayList<>();
    final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
    for (String columnName : unmappedColumnNames) {
      String propertyName = columnName; // // 1.1 获取未配对的列名称, 并对列信息进行处理
      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;
        }
      }//2  将列信息 与 JavaBean 字段相绑定, 不赋值
      final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
      if (property != null && metaObject.hasSetter(property)) {// row =blog metaObject = blog meta info
        if (resultMap.getMappedProperties().contains(property)) {
          continue;//2.1 判断 当前列是否已经映射过, 若是,则跳过
        } //2.2  获取 与列 对应的 Java Bean 的属性名, 以及参数类型
        final Class<?> propertyType = metaObject.getSetterType(property);// 返回property 的类型
        if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
          final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
          autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));//isPrimitive() 判断数据是否是原始数据类型
        } else {
          configuration.getAutoMappingUnknownColumnBehavior()
              .doAction(mappedStatement, columnName, property, propertyType);
        }
      } else {
        configuration.getAutoMappingUnknownColumnBehavior()
            .doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
      }
    }//3. 加入缓存
    autoMappingsCache.put(mapKey, autoMapping);
  }
  return autoMapping;
}
获取列数据值
for (UnMappedColumnAutoMapping mapping : autoMapping) {//2.1 利用resultset(rs) 获取字段值rs.getString(name); //这里是将方法接口化,根据typeHandler 的类型来执行对应的方法getNullableResult()
       final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);

特殊说明:关于typeHandler接口的说明, 以StringTypeHandler 为例

typeHandler利用模板方法(父类定义过程,子类进行扩展)的设计模式, 也就是说:

typeHandler.getResult()-> BaseTypeHandler.getResult() -> XXXTypeHandler.getNullableResult() 的方式进行调用

image-20210325001546881

属性赋值
  1. 调用堆栈信息

image-20210325002114014

PS: 利用代理的方式获取JavaBean属性对应的setter方法, 然后实现赋值这一过程

特殊说明: metaObject.originObject =rowValue

setBeanProperty(prop,obj,val)

  • BeanWrapper.java
private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {
  try {
    Invoker method = metaClass.getSetInvoker(prop.getName());// 获取setter 方法
    Object[] params = {value};
    try {
      method.invoke(object, params);
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  } catch (Throwable t) {
    throw new ReflectionException("Could not set property '" + prop.getName() + "' of '" + object.getClass() + "' with value '" + value + "' Cause: " + t.toString(), t);
  }
}

总结:

  • Mybatis 默认查询方法的执行流程是: 二级缓存 -> 一级缓存 -> DB
  • Mybatis 生成实例对象 是利用 jDK 反射机制实现. 根据XXX.class 获取到Constructor, 去掉访问权限后调用newInstance()生成实例对象 . 传送门
  • Mybatis 对象赋值的流程是 :
    • 将类对象属性 与 数据表中列字段相匹配 [传送门](#创建列与 JavaBean属性的映射)
    • 利用JDK反射机制生成setter方法, 对JavaBean进行属性注入 传送门

参考:

  • https://tech.meituan.com/2018/01/19/mybatis-cache.html