ResultSetHandler

本文深入解析 MyBatis 中 ResultSetHandler 的工作原理及其核心组件 DefaultResultSetHandler 的实现细节,揭示了 MyBatis 如何高效地处理数据库查询结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


title: ResultSetHandler
date:2017年11月26日16:58:04
categories: Mybatis


ResultSetHandler介绍

ResultSetHandler是处理结果集的接口,在我看来他的实现类DefaultResultSetHandler是最难理解的一个组件,之所以复杂是因为Mybatis中ResultMap的设计非常强大,他可以满足用户的很多需求。

MyBatis是基于“数据库结构不可控”的思想建立的,也就是我们希望数据库遵循第三范式或BCNF,但实际事与愿违,那么结果集映射就是MyBatis为我们提供这种理想与现实间转换的手段了,为了实现这一灵活的转化,给用户带来便利,Mybatis做了很多努力,这些努力的具体实现大多都体现在DefaultResultSetHandler这个类中。所以在后面我们会通过查询过程的Debug来好好分析这个类。

我们先看看ResultSetHandler接口的实现

public interface ResultSetHandler {

  <E> List<E> handleResultSets(Statement stmt) throws SQLException;

  void handleOutputParameters(CallableStatement cs) throws SQLException;

}

这个接口只定义了两个方法

handleResultSets就是用来处理结果集的方法,如何将数据库执行后返回的结果集转化为我们在Mapper文件中定义的ResultMap对应的返回,就是通过这个方法实现的。

handleOutputParameters这个方法目前还没有用到,因为目前还没有使用到存储过程。

handleResultSets

我们接着分析查询的过程

现在代码已经执行到了这个阶段

PreparedStatementHandler

  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    //执行数据库操作
    ps.execute();
    //调用handleResultSets处理结果集
    return resultSetHandler.<E> handleResultSets(ps);
  }

执行ps.execute();后

控制台如下

Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@2ed2d9cb]
==>  Preparing: select * from t_user where t_id in ( ? , ? , ? ) 
==> Parameters: 1(Integer), 3(Integer), 25(Integer)

进入handleResultSets方法

 public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
        //先新建一个存放最终的结果的ArrayList
    final List<Object> multipleResults = new ArrayList<Object>();

    int resultSetCount = 0;
   //得到ResultSet结果集, 然后对ResultSetMetaData元数据封装一下
    ResultSetWrapper rsw = getFirstResultSet(stmt);
    //得到该SqlMapper配置文件指定的ResultMap
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
   //校验ResultMap的数量,不能为0或者不存在,如果是,则会抛出异常
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      //依次从List集合中按index取ResultMap
      ResultMap resultMap = resultMaps.get(resultSetCount);
      //然后加入针对ResultMap处理结果集的方法
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    String[] resultSets = mappedStatement.getResulSets();
    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++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }

getFirstResultSet

private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {

    ResultSet rs = stmt.getResultSet();
    while (rs == null) {
      // move forward to get the first resultset in case the driver
      // doesn't return the resultset as the first result (HSQLDB 2.1)
      if (stmt.getMoreResults()) {
        rs = stmt.getResultSet();
      } else {
        if (stmt.getUpdateCount() == -1) {
          // no more results. Must be no resultset
          break;
        }
      }
    }
    return rs != null ? new ResultSetWrapper(rs, configuration) : null;
  }

这个方法就是为了得到ResultSet结果集,如果成功得到结果集,就将结果集封装一下,返回封装好的ResultSetWrapper对象

这个方法里面用到了一些原生JDBC的API方法

getResultSet
ResultSet getResultSet()
                       throws SQLException以 ResultSet 对象的形式获取当前结果。每个结果只应调用一次此方法。 

返回:
以 ResultSet 对象的形式返回当前结果;如果结果是更新计数或没有更多的结果,则返回 null 
抛出: 
SQLException - 如果发生数据库访问错误,或者在已关闭的 Statement 上调用此方法
另请参见:
execute(java.lang.String)


getMoreResults
boolean getMoreResults(int current)
                       throws SQLException将此 Statement 对象移动到下一个结果,根据给定标志指定的指令处理所有当前 ResultSet 对象;如果下一个结果为 ResultSet 对象,则返回 true。 
当以下表达式为 true 时没有更多结果: 

     // stmt is a Statement object
     ((stmt.getMoreResults(current) == false) && (stmt.getUpdateCount() == -1))

参数:
current - 下列 Statement 常量之一,这些常量指示将对使用 getResultSet 方法获取的当前 ResultSet 对象发生的操作:Statement.CLOSE_CURRENT_RESULT、Statement.KEEP_CURRENT_RESULT 或 Statement.CLOSE_ALL_RESULTS 
返回:
如果下一个结果为 ResultSet 对象,则返回 true;如果其为更新计数或者不存在更多的结果,则返回 false 
抛出: 
SQLException - 如果发生数据库访问错误,在已关闭的 Statement 上调用此方法,或者提供的参数不是以下参数之一:Statement.CLOSE_CURRENT_RESULT、Statement.KEEP_CURRENT_RESULT 或 Statement.CLOSE_ALL_RESULTS 
SQLFeatureNotSupportedException - 如果 DatabaseMetaData.supportsMultipleOpenResults 返回 false,并且 Statement.KEEP_CURRENT_RESULT 或 Statement.CLOSE_ALL_RESULTS 作为参数提供。
从以下版本开始: 
1.4 
--------------------------------------------------------------------------------

getUpdateCount
int getUpdateCount()
                   throws SQLException以更新计数的形式获取当前结果;如果结果为 ResultSet 对象或没有更多结果,则返回 -1。每个结果只应调用一次此方法。 

返回:
以更新计数的形式返回当前结果;如果当前结果为 ResultSet 对象或没有更多结果,则返回 -1 
抛出: 
SQLException - 如果发生数据库访问错误,或者在已关闭的 Statement 上调用此方法
另请参见:
execute(java.lang.String)

我们也有必要了解这个封装的对象ResultSetWrapper

class ResultSetWrapper {

  private final ResultSet resultSet;
  private final TypeHandlerRegistry typeHandlerRegistry;
  private final List<String> columnNames = new ArrayList<String>();
  private final List<String> classNames = new ArrayList<String>();
  private final List<JdbcType> jdbcTypes = new ArrayList<JdbcType>();
  private final Map<String, Map<Class<?>, TypeHandler<?>>> typeHandlerMap = new HashMap<String, Map<Class<?>, TypeHandler<?>>>();
  private Map<String, List<String>> mappedColumnNamesMap = new HashMap<String, List<String>>();
  private Map<String, List<String>> unMappedColumnNamesMap = new HashMap<String, List<String>>();

  public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
    super();
    //注入了TypeHandlerRegistry,TypeHandler的注册中心
    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    //注入了ResultSet
    this.resultSet = rs;
    //得到ResultSet的元数据 可用于获取关于 ResultSet 对象中列的类型和属性信息的对象。
    final ResultSetMetaData metaData = rs.getMetaData();

    final int columnCount = metaData.getColumnCount();
    //对元数据的相关有用信息进行封装
    for (int i = 1; i <= columnCount; i++) {
      //将返回的元数据的ColumnName添加到一个String类型的ArrayList中,如果SQL中使用了AS ColumnLable就取ColumnLable,否则就取ColumnName.

      /*
      getColumnLabel
String getColumnLabel(int column)
                      throws SQLException获取用于打印输出和显示的指定列的建议标题。建议标题通常由 SQL AS 子句来指定。如果未指定 SQL AS,则从 getColumnLabel 返回的值将和 getColumnName 方法返回的值相同。

                      getColumnName
String getColumnName(int column)
                     throws SQLException获取指定列的名称。 

      */

      columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));

      /*
      getColumnType
int getColumnType(int column)
                  throws SQLException获取指定列的 SQL 类型。
      得到元数据指定Column的SQL 类型类型,将类型也存储到一个List集合中
      */
      jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
      /*
      getColumnClassName
String getColumnClassName(int column)
                          throws SQLException如果调用方法 ResultSet.getObject 从列中获取值,则返回构造其实例的 Java 类的完全限定名称。ResultSet.getObject 可能返回此方法所返回的类的子类。 


参数:
column - 第一列是 1,第二个列是 2,…… 
返回:
Java 编程语言中类的完全限定名称,方法 ResultSet.getObject 将使用该名称获取指定列中的值。此名称为用于自定义映射关系的类名称。 


      */
      //将列对应的类完全限定名也加入一个List中
      classNames.add(metaData.getColumnClassName(i));
    }
  }




}

handleResultSet

  private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
    try {
    //是否有父Mapper
      if (parentMapping != null) {
        handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
      } 
      //如果没有
      else {
        //且ResultHandler为空,即Method参数列表中没有ResultHandler
        if (resultHandler == null) {
          //新建一个DefaultResultHandler
          DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
         //然后将新的resultHandler注入到handleRowValues中,对结果集进行处理
          handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
          multipleResults.add(defaultResultHandler.getResultList());
        } else {
          handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
        }
      }
    } finally {
      closeResultSet(rsw.getResultSet()); // issue #228 (close resultsets)
    }
  }

handleRowValues

 private void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    if (resultMap.hasNestedResultMaps()) {
      ensureNoRowBounds();
      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 resultContext = new DefaultResultContext();
    //这个方法与RowBounds有关,目前没有理解这个方法,但是这个方法目前不是特别重要。
    skipRows(rsw.getResultSet(), rowBounds);
    //判断是否需要继续执行
    //shouldProcessMoreRows会去校验上下文的标志位以及行数是否超过RowBounds的限制
    // rsw.getResultSet().next()就很好理解了,看下API的解释就明了了
    /*
    next
boolean next()
             throws SQLException将光标从当前位置向前移一行。ResultSet 光标最初位于第一行之前;第一次调用 next 方法使第一行成为当前行;第二次调用使第二行成为当前行,依此类推。 
当调用 next 方法返回 false 时,光标位于最后一行的后面。任何要求当前行的 ResultSet 方法调用将导致抛出 SQLException。如果结果集的类型是 TYPE_FORWARD_ONLY,则其 JDBC 驱动程序实现对后续 next 调用是返回 false 还是抛出 SQLException 将由供应商指定。 

如果对当前行开启了输入流,则调用 next 方法将隐式关闭它。读取新行时,将清除 ResultSet 对象的警告链。 


返回:
如果新的当前行有效,则返回 true;如果不存在下一行,则返回 false 

    */
    //当执行了rsw.getResultSet().next()后,第一个结果集就出来了
    //控制台新增输出: 
    /*
    <==    Columns: t_id, t_name, address
<==        Row: 1, kobe, los

    */
    while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
      //这个方法目前没有深究,但是看到Discriminated我想应该是校验是否在SqlMapper中用到了discriminate把,之后测试了这一情况再来细说
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
      //这里终于到了正式处理结果集的时候了,
      Object rowValue = getRowValue(rsw, discriminatedResultMap);
      storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
    }
  }

getRowValue(rsw, discriminatedResultMap)

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
  //创建我们要返回的对象类型,这里是User
    Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null);
  // 创建的实例不为空而且也不是基本类型(泛指) 
  if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
      //构造出一个Mybatis自己定义的MetaObject
      final MetaObject metaObject = configuration.newMetaObject(resultObject);
    //判断是否有指定构造方法
      boolean foundValues = resultMap.getConstructorResultMappings().size() > 0;
    //判断是否有autoMapping,如果没有执行if下的语句,目前还没有弄清楚autoMapping是什么属性
    if (shouldApplyAutomaticMappings(resultMap, !AutoMappingBehavior.NONE.equals(configuration.getAutoMappingBehavior()))) {      
      //处理没有显式用result节点声明的ColumnName
        foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
      }
    //处理显式使用result节点声明的ColumnName
      foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
      foundValues = lazyLoader.size() > 0 || foundValues;
      resultObject = foundValues ? resultObject : null;
      return resultObject;
    }

createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
    final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
    final List<Object> constructorArgs = new ArrayList<Object>();
    final Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
  //创建的实例不为空而且也不是基本类型(泛指)
    if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
      final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
      //就看下propertyMappings里面是不是带有查询,即select属性
    //propertyMappings 很显然就对应ResultMap节点中的Result节点
      for (ResultMapping propertyMapping : propertyMappings) {
        //如果带有select属性而且指定了是懒加载
        if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) { // issue gcode #109 && issue #149
          //具体这种方式是如何创建的,目前还没有测试过,后续再细说
          return configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
        }
      }
    }
    return resultObject;
  }

createResultObject

  private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
      throws SQLException {
    //先得到ResultMap要返回的类型,也就是我们在Mapper文件中对ResultMap配置时的type属性
    final Class<?> resultType = resultMap.getType();
    //这个目前测试的都是为null的,看这个命名应该是ResultMap中有指定对应的返回类型的构造方法吧
    final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
    //如果对应的返回类型不是JavaBean或者集合类型等,而是基本类型或者是他的一些包装类和时间日期等类型
    if (typeHandlerRegistry.hasTypeHandler(resultType)) {
    //创建基本类型的返回类型的实例
      return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
    }
    //如果指定了构造方法,使用构造方法来创建实例
    else if (constructorMappings.size() > 0) {
      return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
    } else {
      //要不然使用objectFactory来创建实例
      return objectFactory.create(resultType);
    }
  }

objectFactory.create(resultType)

DefaultObjectFactory

 public <T> T create(Class<T> type) {
    return create(type, null, null);
  }
  public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    Class<?> classToCreate = resolveInterface(type);
    @SuppressWarnings("unchecked")
    // we know types are assignable
    T created = (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
    return created;
  }
 private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    try {
      Constructor<T> constructor;
      //如果没有在ResultMap中指定构造方法,Mybatis默认该类只有默认的无参构造方法
      if (constructorArgTypes == null || constructorArgs == null) {
        /*
        getDeclaredConstructor
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
                                      throws NoSuchMethodException,
                                             SecurityException返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。parameterTypes 参数是 Class 对象的一个数组,它按声明顺序标识构造方法的形参类型。 如果此 Class 对象表示非静态上下文中声明的内部类,则形参类型作为第一个参数包括显示封闭的实例。 

参数:
parameterTypes - 参数数组 
返回:
带有指定参数列表的构造方法的 Constructor 对象 

        */
        //这里调用该方法时没有指定参数,意思即试图返回该类的无参构造方法,如果没有无参构造方法,就会抛出异常,然后再后面的catch住,后抛出给用户
        constructor = type.getDeclaredConstructor();
        if (!constructor.isAccessible()) {
          constructor.setAccessible(true);
        }
        //如果存在无参构造方法,就直接创建一个实例且返回
        return constructor.newInstance();
      }
      //如果有指定构造方法,试图返回相应的有参构造方法,如果失败,抛出异常后catch住重新抛出给用户
      constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
      if (!constructor.isAccessible()) {
        constructor.setAccessible(true);
      }
      return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
    } catch (Exception e) {
      StringBuilder argTypes = new StringBuilder();
      if (constructorArgTypes != null) {
        for (Class<?> argType : constructorArgTypes) {
          argTypes.append(argType.getSimpleName());
          argTypes.append(",");
        }
      }
      StringBuilder argValues = new StringBuilder();
      if (constructorArgs != null) {
        for (Object argValue : constructorArgs) {
          argValues.append(String.valueOf(argValue));
          argValues.append(",");
        }
      }
      throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
    }
  }

当我们分析完这段代码的时候,我们就可以很清楚的知道为什么当我们没有在ResultMap中显式的指定构造方法时,如果我们对我们的JavaBean创建了一个有参构造方法而此时没有显式的给出无参构造方法,这时运行程序,就会抛出Error instantiating异常。

有些同学可能会觉得看源码的用处不大,甚至是浪费时间,但是我不这样觉得,看源码还是有很多好处的。

1、看源码我们可以学习框架的设计思想,学习设计模式。

2、看源码可以加深对JDK API的认识,再复杂的代码都是基于JDK API写出来的,对JDK API的熟悉度会一定程度决定你的编码质量。

3、看源码虽然确实很花时间,但是看了源码之后我们才真正的做到了知其然,也知其所以然,这是一个热爱的代码,热爱编程了必须要有的专研精神,看了源码,我们就知道了,这个框架到底为什么要这样用,为什么那样配置就不对,为什么会报这个错误,这样我们才能利用用框架工具,而不是被框架牵着走。

啰嗦完了,继续往下看。

configuration.newMetaObject(resultObject)

 public MetaObject newMetaObject(Object object) {
    return MetaObject.forObject(object, objectFactory, objectWrapperFactory);
  }
 public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
    if (object == null) {
      return SystemMetaObject.NULL_META_OBJECT;
    } else {
      return new MetaObject(object, objectFactory, objectWrapperFactory);
    }
  }
  private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
    this.originalObject = object;
    this.objectFactory = objectFactory;
    this.objectWrapperFactory = objectWrapperFactory;
    //如果object已经是封装过的ObjectWarpper,就不需要再封装了,直接转化为ObjectWarpper类型就好了
    if (object instanceof ObjectWrapper) {
      this.objectWrapper = (ObjectWrapper) object;
    }
    //如果是已经封装了,去Factory里取
    else if (objectWrapperFactory.hasWrapperFor(object)) {
      this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
    } 
    //如果是Map类型,创建一个MapWrapper
    else if (object instanceof Map) {
      this.objectWrapper = new MapWrapper(this, (Map) object);
    } 
    //如果是Collection类型,一定要注意哦,Collection和Map是平级的哦,Map可不属于Collection
    //Collection下面有List Set等
    else if (object instanceof Collection) {
      this.objectWrapper = new CollectionWrapper(this, (Collection) object);
    } else {
      //否则创建一个JavaBean 普通Java对象的包装类
      this.objectWrapper = new BeanWrapper(this, object);
    }
  }

BeanWarpper的创建

  public BeanWrapper(MetaObject metaObject, Object object) {
    super(metaObject);
    this.object = object;
    this.metaClass = MetaClass.forClass(object.getClass());
  }
public static MetaClass forClass(Class<?> type) {
    return new MetaClass(type);
  }

  private MetaClass(Class<?> type) {
    this.reflector = Reflector.forClass(type);
  }

this.reflector = Reflector.forClass(type);

这里的reflector是MetaClass的一个属性对应的类是Reflector,是Mybatis处理反射最重要的类。

Reflector

public static Reflector forClass(Class<?> clazz) {
    if (classCacheEnabled) {
      // synchronized (clazz) removed see issue #461
      Reflector cached = REFLECTOR_MAP.get(clazz);
      if (cached == null) {
        cached = new Reflector(clazz);
        REFLECTOR_MAP.put(clazz, cached);
      }
      return cached;
    } else {
      return new Reflector(clazz);
    }
  }

 private Reflector(Class<?> clazz) {
    type = clazz;
   //添加默认的无参构造方法
    addDefaultConstructor(clazz);
   //添加Get方法 
   addGetMethods(clazz);
   //添加Set方法 
   addSetMethods(clazz);
   //添加Field属性 
   addFields(clazz);
    readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
    writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
   //下面的两个put元素的操作是很重要的,我们看到他们把对应的PropertyName的大写作为Key,
   //把PropertyName作为Value存入这个HashMap中
    for (String propName : readablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writeablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }

applyAutomaticMappings

private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
  //得到没有显式声明的ColumnName 的 List
    final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
    boolean foundValues = false;
  //后续的赋值操作比较简单,就不详细分析了
    for (String columnName : unmappedColumnNames) {
      String propertyName = columnName;
      if (columnPrefix != null && columnPrefix.length() > 0) {
        // When columnPrefix is specified,
        // ignore columns without the prefix.
        if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
          propertyName = columnName.substring(columnPrefix.length());
        } else {
          continue;
        }
      }
      //对这句代码还是有必要分析一下的,因为这里就是驼峰命名可以不显式声明ColumnName的原因所在
      final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
      if (property != null && metaObject.hasSetter(property)) {
        final Class<?> propertyType = metaObject.getSetterType(property);
        if (typeHandlerRegistry.hasTypeHandler(propertyType)) {
          final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
          final Object value = typeHandler.getResult(rsw.getResultSet(), columnName);
          if (value != null || configuration.isCallSettersOnNulls()) { // issue #377, call setter on nulls
            if (value != null || !propertyType.isPrimitive()) {
              metaObject.setValue(property, value);
            }
            foundValues = true;
          }
        }
      }
    }
    return foundValues;
  }

getUnmappedColumnNames

  public List<String> getUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
    List<String> unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
    if (unMappedColumnNames == null) {
      loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
      unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
    }
    return unMappedColumnNames;
  }

 private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
    List<String> mappedColumnNames = new ArrayList<String>();
    List<String> unmappedColumnNames = new ArrayList<String>();
    final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);
    final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);
    for (String columnName : columnNames) {
      final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);
      if (mappedColumns.contains(upperColumnName)) {
        mappedColumnNames.add(upperColumnName);
      } else {
        unmappedColumnNames.add(columnName);
      }
    }
    mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);
    unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);
  }

loadMappedAndUnmappedColumnNames这个方法将没有显式的在ResultMap节点中的Result节点声明的property和显式声明的property分别存到unmappedColumnNames和unmappedColumnNames中。

findProperty(propertyName, configuration.isMapUnderscoreToCamelCase())

     final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase()); 
//如果设置了数据库列名使用驼峰命名可以不用显式声明result,会把列名的下划线去掉,使其与JavaBean 属性名相同
     public String findProperty(String name, boolean useCamelCaseMapping) {
    if (useCamelCaseMapping) {
      name = name.replace("_", "");
    }
    return findProperty(name);
  }

如果设置了

  <setting name="mapUnderscoreToCamelCase" value="true"/>

上述的useCamelCaseMapping就是true了

applyPropertyMappings

 private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
      throws SQLException {
    final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
    boolean foundValues = false;
    final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
    for (ResultMapping propertyMapping : propertyMappings) {
      final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
      if (propertyMapping.isCompositeResult() 
          || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) 
          || propertyMapping.getResultSet() != null) {
        //我们主要需要看的是这个方法的具体实现,其他的都比较好理解,
        Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
        final String property = propertyMapping.getProperty(); // issue #541 make property optional
        if (value != NO_VALUE && property != null && (value != null || configuration.isCallSettersOnNulls())) { // issue #377, call setter on nulls
          if (value != null || !metaObject.getSetterType(property).isPrimitive()) {
            metaObject.setValue(property, value);
          }
          foundValues = true;
        }
      }
    }
    return foundValues;
  }

getPropertyMappingValue

  private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
      throws SQLException {
    //如果result节点中select属性存在
    if (propertyMapping.getNestedQueryId() != null) {
      //在本次查询中是不会用到这个方法的,因为result节点中并没有
      return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
    } 
    //这一分支应该是这个select属性对应的查询结果ResultMap中的result节点依然有select属性,当然目前还没有验证
    else if (propertyMapping.getResultSet() != null) {
      addPendingChildRelation(rs, metaResultObject, propertyMapping);
      return NO_VALUE;
    } else if (propertyMapping.getNestedResultMapId() != null) {
      // the user added a column attribute to a nested result map, ignore it
      return NO_VALUE;
    } else {
      final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
      final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
      return typeHandler.getResult(rs, column);
    }
  }

如果我们的ResultMap是这样的

。。。待补充

那么就会用到这个方法

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();
  //得到该result节点对应column的值,这个值将作为select对应的select Mapper的参数传入  
  final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, propertyMapping, nestedQueryParameterType, columnPrefix);
    Object value = NO_VALUE;
    if (nestedQueryParameterObject != null) {
      final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
      final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
      //ofType  如果没有指定ofType,应该会取selct Mapper中对应的ResultMap Type把,这个做实验就知道会不会这样取了
      final Class<?> targetType = propertyMapping.getJavaType();
      if (executor.isCached(nestedQuery, key)) {
        executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
      } else {
        final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
        //如果是懒加载,不要立即查出结果,先缓存
        if (propertyMapping.isLazy()) {
          lazyLoader.addLoader(property, metaResultObject, resultLoader);
        } else {
          //执行查询,得到结果
          value = resultLoader.loadResult();
        }
      }
    }
    return value;
  }

loadResult

 public Object loadResult() throws SQLException {
   //selectList并不难理解
    List<Object> list = selectList();
   //extractObjectFromList就是转化下返回的类型
    resultObject = resultExtractor.extractObjectFromList(list, targetType);
    return resultObject;
  }
   private <E> List<E> selectList() throws SQLException {
    Executor localExecutor = 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);
      }
    }
  }

extractObjectFromList

public Object extractObjectFromList(List<Object> list, Class<?> targetType) {
    Object value = null;
    if (targetType != null && targetType.isAssignableFrom(list.getClass())) {
      value = list;
    } else if (targetType != null && objectFactory.isCollection(targetType)) {
      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;
  }

总结

到这里对ResultSetHandler的讲解就稍微告一段落了

目前还有MapKey(差实验),鉴别器(),缓存,懒加载还没有讲到。

ResultSetHandler是Mybatis最复杂的一部分,它对应着ResultMap的处理,而ResultMap的配置也是配置文件中最难的。

在这里再说下我之前的一个疑问,也是自己对泛型没有很好的理解到位。

当我第一次看到这段代码时

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.<E> handleResultSets(ps);
  }

我对这行

   return resultSetHandler.<E> handleResultSets(ps);

是蛮疑惑的,resultSetHandler.\ handleResultSets(ps);

这种在调用方法前使用泛型的,代表这个方法肯定是泛型方法,否则肯定是不能这样用的。

但是我们发现在DefaultResultSetHandler中这个方法的返回类型是List\,并不是一个泛型方法呀?

public List<Object> handleResultSets(Statement stmt) throws SQLException {...}

但是依然在返回给我们结果的时候进行了强制的类型转化,也就是实现了泛型方法的效果。

这是为什么呢?在一开始我是十分想不通的。

当然在ResultSetHandler接口中,这是一个泛型方法

  <E> List<E> handleResultSets(Statement stmt) throws SQLException;

这说明实现了这个接口的方法,即使在实现类指定了某个具体的类型,还是具有泛型方法的特性的,还是会被当做泛型方法的。

至于为什么会这样,我的理解是,只要实现的方法是泛型方法,即使实现类里明确指定了特定的返回类型,依然会在编译时期,完成类型的强制转化,依然是具有泛型方法特性的。

最后,我们举个例子来验证一下:

package com.wangcc.MyJavaSE.Generic;

import java.util.List;

public interface ResultSetHandler {
    public <E> List<E> handleResultSets();
}
public class DefaultResultSetHandler implements ResultSetHandler {

    public List<Object> handleResultSets() {
        // TODO Auto-generated method stub
        List<Object> list = new ArrayList<Object>();
        list.add("Kobe");
        list.add("james");
        return list;
    }

}
public class GenericTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ResultSetHandler resultSetHandler = new DefaultResultSetHandler();
        List<String> list = resultSetHandler.<String>handleResultSets();
        for (String str : list) {
            System.out.println(str);
        }
    }

}

这个测试类是可以正常运行的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值