Mybatis源码解析——ResultSetHandler

ResultSetHandler

负责将数据库返回的ResultSet映射为结果对象

public interface ResultSetHandler {

//处理结果集,生成对应的结果对象集
  <E> List<E> handleResultSets(Statement stmt) throws SQLException;

//处理结果集,生成对应的游标对象集
  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

//处理存储过程的输出参数
  void handleOutputParameters(CallableStatement cs) throws SQLException;

}

DefaultResultSetHandler

ResultSetHandler接口的默认实现,之后的分析均基于该实现类分析

ResultSetWrapper

DefaultResultSetHandler在拿到ResultSet后不会直接使用,而是将其封装为ResultSetWrapper供后续使用,ResultSetWrapper记录了ResultSet的一些元数据以及对应的辅助方法,如ResultSet中每列的

  • 列名(构造器中初始化)
  • java类型(构造器中初始化)
  • jdbc类型(构造器中初始化)
  • 对应的typeHandler集合
  • 明确映射的列名集合
  • 未被明确映射的列名(即resultMap没有制定映射关系的列)
  • ……

loadMappedAndUnMappedColumnNames()会遍历ResultSet中的ColumnName与ResultMap中指定映射的列名进行对比,加入到mappedMap/unmappedMap中,key为resultMap的id和列名前缀

简单映射

handleResultSet() 会使用指定的或默认的ResultSetHandler处理ResultSet,handleRowValues()是具体的方法,根据是否含有嵌套,分为handleRowValuesForNestedResultMap()和handleRowValuesForSimpleResultMap()两个方法。

非嵌套映射

核心方法为handleRowValuesForSimpleResultMap()

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
    skipRows(rsw.getResultSet(), rowBounds);
    while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
      Object rowValue = getRowValue(rsw, discriminatedResultMap);
      storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
    }
  }

其执行流程图如图所示

在这里插入图片描述

步骤如下:

  1. 根据RowBounds中的offset定位到指定的行记录
  2. 检测是否还有需要映射的记录
  3. 根据鉴别器Discriminated返回的ResultMapId确定映射使用的ResultMap对象
  4. 对ResultSet中的一行记录进行映射
    a. 创建结果对象
    b. 判断是否开启了自动映射
    c. 自动映射未明确映射的列
    d. 映射明确映射的列
  5. 保存映射得到的结果对象到ResultHandler中

resultHandler用于存储结果对象

skipRows

根据rowBounds中的offset多次调用ResultSet.next()方法移到指定记录

shouldProcessMoreRows

使用DefaultResultContext存储结果对象,并对结果对象数量进行计数,通过比较DefaultResultContext.isStopped()以及计数与rowBounds.limit()的比较来决定是否继续进行结果映射

resolveDiscriminatedResultMap

根据Discriminated返回的ResultMapId确定映射使用的ResultMap对象

getRowValue
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
    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, null) || foundValues;
      }
      foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
      foundValues = lazyLoader.size() > 0 || foundValues;
      rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
    }
    return rowValue;
  }
  1. createResultObject
    一共四种场景

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
      throws SQLException {
    final Class<?> resultType = resultMap.getType();
    final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
    final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
    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);
    } else if (shouldApplyAutomaticMappings(resultMap, false)) {
    //第四种
      return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
    }
    throw new ExecutorException("Do not know how to create an instance of " + resultType);
  }

  • 第一种:结果集只有一列,且存在typeHandler可以将该列转换为resultType类型的值,则先拿到这个类型的typeHandler对象并使用该对象进行直接转换(如只查一个年龄,返回来一个数字,直接转换成int类型返回了)
  • 第二种:ResultMap中记录了<Constructor>节点信息,通过反射选择合适的构造方法创建对象
  • 第三种:使用默认无参构造方法,用ObjectFactory创建对象
  • 第四种:通过自动映射查找合适的构造方法,需要在构造方法上添加@AutoMapConstructor注解。会将构造方法的参数和结果集中的数据按照相同顺序(第一个对应第一个,第二个对应第二个)进行类型比对,如果类型一致即可调用构造函数,得到的对象的属性值可能是错误的,但属性值会在后面的映射过程将错误的覆盖掉。
ApplyAutomaticMappings

对于没有明确在ResultMap中指定映射关系的属性(如直接指定ResultType的),Mybatis会自动按照下划线转驼峰的规则进行匹配,其匹配的流程如下:

  1. 从ResultSetWrapper中拿到所有未明确映射的列名集合
  2. 遍历1集合,将列明去掉前缀并下划线转驼峰获得对应属性名
  3. 使用反射metaObject检查是否存在该属性的setter方法
  4. 查找对应类型的typeHandler
  5. 构造unMappedColumnAutoMapping对象(包含列明、属性名以及对应typeHandler)
  6. 遍历5列表使用反射进行设值
applyPropertyMappings
  1. 从ResultSetWrapper中拿到所有明确映射的列名集合
  2. 从ResultMap中拿到PropertyResultMappings集合
  3. getPropertyMappingValue()获取属性值,并通过反射设值,有三种情况
  • 对于复杂对象,需要进行嵌套处理
  • 对于基本类型,直接使用typeHandler获取值
  • 多结果集场景
storeObject

将结果对象保存到ResultHandler.list中,进行下一条记录映射

嵌套映射

核心方法为handleRowValuesForNestedResultMap()
步骤如下:

  1. 根据RowBounds中的offset定位到指定的行记录

  2. 检测是否还有需要映射的记录

  3. 根据鉴别器Discriminated返回的ResultMapId确定映射使用的ResultMap对象

  4. 通过createKey()为记录生成cacheKey

  5. 查找cacheKey字典nestedResultObjects中存放的半成品

  6. 检测ResultOrdered属性

  7. getRowValue处理嵌套映射

    如果外层对象不存在(nestedResultObjects.get() != null

    a. 创建结果对象
    b. 判断是否开启了自动映射来决定是否处理未明确映射的列
    c. 处理明确映射的列
    d. 将a结果对象放入ancestorObjects集合中
    e. 调用applyNestedResultMappings()处理下一层嵌套映射,在这个方法中得到的结果对象会注入到ancestorObjects集合中的外层对象中,结束返回这里
    f. 移除ancestorObjects集合中的外层对象
    g. 将a的对象放入半成品nestedResultObjects中

    如果存在
    执行d,e,f步骤

  8. 映射完成,storeObject将结果对象放入ResultHandler中

createKey

每个ResultSet都会被创建一个CacheKey作为key存在一个nestedResultObjects的HashMap中,这个map存的对象是半成品对象,在处理结果集时会优先从这里寻找半成品对象,如果存在说明当前进行的是嵌套映射,需要将当前的结果注入到外层对象(也就是这个半成品对象)的属性中去。
如何保证在外层和内层处理时对应的外部的resultMap是同一个?即其cacheKey相同?
cacheKey的创建规则如下:

  1. ResuleMapId+<id>/<idArg>节点对应的列名+对应的值
  2. ResultMap中明确映射的列名及对应的值
  3. 如果ResultType为Map类型,则所有列名和对应值
  4. 如果ResultType不为Map类型,则未映射列名与对应值

上述规则创建的CacheKey再与其外层CacheKey组合成为全局唯一的CacheKey

ResultOrdered

当该值为true时,每个外层对象处理结束进行下一个外层对象的嵌套映射时,会将nestedResultObjects进行情况,能够节省内存使用,避免中间出现内存不足的情况

延迟加载

对象的某些属性在使用的时候才会加载到内存中,Mybatis通过动态代理的形式,返回延迟加载属性对象的代理对象,在使用时代理对象会执行数据库加载得到真正的数据。
延迟加载针对的是嵌套查询,嵌套查询和主查询是两个不同的statemetn,定义在resultMap中。在主查询结束进行结果映射时,解析映射到嵌套查询位置会根据延迟加载的配置来决定是当前还是延迟执行嵌套查询

延迟加载的配置:

  1. resultMap中明确配置了fetchType属性,根据该属性决定是否延迟加载
  2. mybatis-config.xml中的<setting name="lazyLoadingEnabled" value = "true">打开延迟加载的开关
  3. mybatis-config.xml中的<setting name="aggressiveLazyLoading" value = "false">为true时有一个延迟加载的属性得到加载时,所有的延迟加载属性均会进行加载,默认false
ResultLoaderMap和ResultLoader

ResultLoader中保存了进行一次延迟加载的所有信息,包括配置对象,Executor,SQL语句,类型,CacheKey等,其核心方法是loadResult()方法,该方法会使用Executor执行延迟加载的SQL语句并返回对象

ResultLoaderMap中使用loadMap字段(HashMap<String,LoadPair>)保存对象中延迟加载属性及其对应的ResultLoader对象,key为大写的属性名其提供的loadAll()和load()方法均是调用了LoadPair.load()方法,最终指向ResultLoader.loadResult()方法

代理对象会持有ResultLoaderMap及出发延迟加载的方法列表,如果当前方法在列表中,则进行延迟加载。

延迟加载的体现

在applyPropertyMappings()方法调用getPropertyMappingValue()获取属性值时,会检测是否延迟加载,默认使用JavasistProxyFactory创建代理对象

如图所示
在这里插入图片描述

发布了98 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Mutou_ren/article/details/102794613
今日推荐