ResultSetHandler

来源:互联网 发布:三菱数控车床编程实例 编辑:程序博客网 时间:2024/06/06 02:05

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方法

getResultSetResultSet getResultSet()                       throws SQLException以 ResultSet 对象的形式获取当前结果。每个结果只应调用一次此方法。 返回:以 ResultSet 对象的形式返回当前结果;如果结果是更新计数或没有更多的结果,则返回 null 抛出: SQLException - 如果发生数据库访问错误,或者在已关闭的 Statement 上调用此方法另请参见:execute(java.lang.String)getMoreResultsboolean 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 --------------------------------------------------------------------------------getUpdateCountint 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.      /*      getColumnLabelString getColumnLabel(int column)                      throws SQLException获取用于打印输出和显示的指定列的建议标题。建议标题通常由 SQL AS 子句来指定。如果未指定 SQL AS,则从 getColumnLabel 返回的值将和 getColumnName 方法返回的值相同。                      getColumnNameString getColumnName(int column)                     throws SQLException获取指定列的名称。       */      columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));      /*      getColumnTypeint getColumnType(int column)                  throws SQLException获取指定列的 SQL 类型。      得到元数据指定Column的SQL 类型类型,将类型也存储到一个List集合中      */      jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));      /*      getColumnClassNameString 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的解释就明了了    /*    nextboolean 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) {        /*        getDeclaredConstructorpublic 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);        }    }}

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

原创粉丝点击