MyBatis 源碼分析篇 7:Mapper 方法執(zhí)行的“后果”

我們已經(jīng)知道了 Mapper 方法執(zhí)行的前因,即:獲取語(yǔ)句+參數(shù)映射(MyBatis 源碼分析篇 6:Mapper 方法執(zhí)行的“前因”)。現(xiàn)在就讓我們來(lái)看看其“后果”:結(jié)果集映射。

參數(shù)映射是通過(guò) TypeHandler 實(shí)現(xiàn)的,那么同理,結(jié)果集映射也應(yīng)該是通過(guò) TypeHandler 實(shí)現(xiàn)。

我們還是按照之前的方式,使用 debug 在入口代碼上打斷點(diǎn),步入源碼。入口代碼為:

List<Author> list = mapper.selectByName("Sylvia");

對(duì)應(yīng)的 SQL:

   <resultMap id="AuthorMap" type="Author">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="sex" property="sex" />
        <result column="phone" property="phone" />
    </resultMap>

    <select id="selectByName" resultMap="AuthorMap" >
        select
          id, name, sex, phone
        from author
        where name = #{name}
    </select>

我們接著 MyBatis 源碼分析篇 5:Mapper 方法執(zhí)行之 Executor 一文最后我們找到的 JDBC 代碼位置 org.apache.ibatis.executor.statement.PreparedStatementHandler 的 query(...) 方法來(lái)看:

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

在執(zhí)行完 PreparedStatement 的 execute() 后,自然就要進(jìn)行結(jié)果的處理,我們現(xiàn)在就進(jìn)入最后一行結(jié)果處理的代碼看看:
org.apache.ibatis.executor.resultset.DefaultResultSetHandler:

  @Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
    final List<Object> multipleResults = new ArrayList<Object>();
    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
    //忽略 resultSets 部分...
    return collapseSingleResultList(multipleResults);
  }

這個(gè)方法就是在獲取結(jié)果集并處理成我們最終想要的 List<Author> 類(lèi)型的結(jié)果。我們提取一下關(guān)鍵步驟:

  • 第一步:獲取 PreparedStatement 執(zhí)行數(shù)據(jù)庫(kù)操作后返回的 ResultSet。
    ResultSetWrapper rsw = getFirstResultSet(stmt);(方法的第 4 行代碼)
  • 第二步:獲取 ResultMap。
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();(方法的第 5 行代碼)
    ResultMap resultMap = resultMaps.get(resultSetCount);(方法的第 9 行代碼)
  • 第三步:通過(guò)前兩步獲得的 ResultSet 和 ResultMap 處理獲得最終的 List<Author> 類(lèi)型的結(jié)果。
    handleResultSet(rsw, resultMap, multipleResults, null);(方法的第 10 行代碼)

現(xiàn)在,我們就來(lái)對(duì)每一步進(jìn)行跟蹤分析。

1 獲取 ResultSet

ResultSetWrapper rsw = getFirstResultSet(stmt);

步入這行代碼,進(jìn)入本類(lèi)的 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;
  }

在上一篇關(guān)于獲取語(yǔ)句+參數(shù)映射的分析我們知道,這個(gè)方法傳入的參數(shù) stmt 是通過(guò)動(dòng)態(tài)代理方法生成的,那么上面這個(gè)方法中的第一行代碼:ResultSet rs = stmt.getResultSet(); 自然就會(huì)進(jìn)入動(dòng)態(tài)代理類(lèi) PreparedStatementLogger 中的 invoke(...) 方法來(lái)獲取 ResultSet:

  @Override
  public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, params);
      }          
      if (EXECUTE_METHODS.contains(method.getName())) {
        //這里省略了幾個(gè) else if
      } else if ("getResultSet".equals(method.getName())) {
        ResultSet rs = (ResultSet) method.invoke(statement, params);
        return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
      }
    //這里還有其他代碼,繼續(xù)省略...
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

我們主要來(lái)看 if ("getResultSet".equals(method.getName() 這個(gè)判斷后的代碼:如果現(xiàn)在執(zhí)行的方法是 getResultSet(...),那么首先會(huì)獲取一個(gè) ResultSet 的實(shí)例,接著會(huì)通過(guò) ResultSetLogger.newInstance() 獲取的 ResultSet 覆蓋掉這個(gè) ResultSet。同獲取 PreparedStatement 實(shí)例非常相似,這里采用同樣的方式:動(dòng)態(tài)代理來(lái)通過(guò) ResultSetLogger 代理類(lèi)獲取 ResultSet 實(shí)例。此處便不再贅述。

分析到這里我們就拿到了新出爐的 ResultSet,并將它封裝到了 ResultSetWrapper 類(lèi)中以供接下來(lái)的使用。

2 獲取 ResultMap

List<ResultMap> resultMaps = mappedStatement.getResultMaps();
ResultMap resultMap = resultMaps.get(resultSetCount);

關(guān)于獲取 ResultMap,它是直接從 MappedStatement 中拿到的,而這個(gè) MappedStatement 實(shí)例的內(nèi)容是在獲取 SqlSessionFactory 時(shí)填充的,關(guān)于這一點(diǎn)我們?cè)?MyBatis 源碼分析篇 6:Mapper 方法執(zhí)行的“前因” 中已經(jīng)詳細(xì)分析過(guò),此處便不再贅述,感興趣的同學(xué)可以查看之前的篇章并結(jié)合自己實(shí)踐跟蹤源碼來(lái)回憶一下。

這個(gè)時(shí)候獲得的 ResultMap 是這個(gè)樣子的:

ResultMap

可以看到 ResultMap 已經(jīng)包含了每個(gè)屬性的結(jié)果映射 Map、要映射的列和屬性,用于接下來(lái)的結(jié)果映射。

3 獲取 List<Object>

handleResultSet(rsw, resultMap, multipleResults, null);

通過(guò)前兩步,我們已經(jīng)拿到了 ResultSet 和 ResultMap 了,現(xiàn)在我們就要來(lái)通過(guò)它們處理得到最終我們想要的 List<Object> 了。

首先要說(shuō)明的一點(diǎn)是,這行代碼是在 while 循環(huán)中,即通過(guò)遍歷我們拿到的 ResultSetWrapper (封裝了 ResultSet),來(lái)循環(huán)處理每一條結(jié)果集。現(xiàn)在就讓我們步入這行代碼,進(jìn)入本類(lèi)的 handleResultSet(...) 方法:

  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());
    }
  }

對(duì)于上面這個(gè)方法,其中第 6 、7 行代碼為我們要跟蹤的核心功能。其中,第 6 行代碼通過(guò) ObjectFactory 生成 DefaultResultHandler,該 DefaultResultHandler 持有生成的 List 實(shí)例。第 7 行代碼則是給第 6 行代碼執(zhí)行生成的 List 填充值。而第 8 行代碼則是將分頁(yè)處理過(guò)后的 List<Object> 加入 List 類(lèi)型的 multipleResults 中返回。

現(xiàn)在我們就來(lái)步入第 6 行核心代碼看看:

  public DefaultResultHandler(ObjectFactory objectFactory) {
    list = objectFactory.create(List.class);
  }

在 DefaultResultHandler 類(lèi)的構(gòu)造方法中,調(diào)用了 ObjectFactory 的 create(...) 方法。想必大家對(duì) ObjectFactory 已經(jīng)不再陌生,在前面文檔篇關(guān)于 ObjectFactory 的學(xué)習(xí)中我們知道:MyBatis 在獲取到結(jié)果集后會(huì)通過(guò) ObjectFactory 來(lái)創(chuàng)建結(jié)果對(duì)象的實(shí)例。它也是在 MyBatis 讀取 Configuration XML 的時(shí)候存入的。也就是說(shuō),如果我們沒(méi)有在 mybatis-config.xml 中配置自定義的 objectFactory,它會(huì)使用默認(rèn)的 DefaultObjectFactory,默認(rèn)只是實(shí)例化一個(gè)目標(biāo)類(lèi)。

為了印證這一點(diǎn),我們跟蹤 create(...) 方法來(lái)看一下 DefaultObjectFactory 中的實(shí)現(xiàn):

  @Override
  public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    Class<?> classToCreate = resolveInterface(type);
    // we know types are assignable
    return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
  }

上面這個(gè)方法第 1 行代碼得到了 ArrayList 類(lèi)型,繼續(xù)步入:

  private  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    try {
      Constructor<T> constructor;
      if (constructorArgTypes == null || constructorArgs == null) {
        constructor = type.getDeclaredConstructor();
        if (!constructor.isAccessible()) {
          constructor.setAccessible(true);
        }
        return constructor.newInstance();
      }
      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 && !constructorArgTypes.isEmpty()) {
        for (Class<?> argType : constructorArgTypes) {
          argTypes.append(argType.getSimpleName());
          argTypes.append(",");
        }
        argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
      }
      StringBuilder argValues = new StringBuilder();
      if (constructorArgs != null && !constructorArgs.isEmpty()) {
        for (Object argValue : constructorArgs) {
          argValues.append(String.valueOf(argValue));
          argValues.append(",");
        }
        argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
      }
      throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
    }
  }

顯而易見(jiàn),原來(lái) MyBatis 使用 ObjectFactory 生成目標(biāo)類(lèi)的實(shí)例,是通過(guò)反射實(shí)現(xiàn)的!對(duì)于反射,我想大家已經(jīng)很熟悉了,這里就不展開(kāi)分析了。

現(xiàn)在我們拿到了 ArrayList 實(shí)例,接下來(lái)就剩下往里面塞值了。我們接著看前面提到的第 7 行代碼:handleResultSet(...) 方法的 handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);

進(jìn)入 org.apache.ibatis.executor.resultset.DefaultResultSetHandler 類(lèi)的 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());
    }
  }

其中,方法的第 2 行代碼是要進(jìn)行邏輯分頁(yè)的,這一點(diǎn)我們已經(jīng)在 MyBatis 最佳實(shí)踐篇 1:分頁(yè) 一文中分析過(guò),這里便不再贅述。我們重點(diǎn)來(lái)看 while 循環(huán)中的代碼,該 while 循環(huán)遍歷 ResultSet。

循環(huán)中的第 1 行代碼的作用是對(duì) ResultMap 進(jìn)行進(jìn)一步的處理:判斷你是否定義了 Discriminator,如果定義了,它會(huì)去取你在 Discriminator 中指定的 ResultMap。
循環(huán)中的第 2 行代碼的作用是獲取 Bean 對(duì)象的實(shí)例并為該 Bean 對(duì)象賦值。
循環(huán)中的第 3 行代碼的作用很簡(jiǎn)單,就是將獲取到的 Bean 對(duì)象加入到 List 集合中。

我們想要知道 MyBatis 是如何將數(shù)據(jù)庫(kù)獲取到的每一條記錄變成我們想要的集合中的 Java 對(duì)象的,所以第 2 行代碼是我們要關(guān)注的重點(diǎn)。

步入這行代碼,進(jìn)入 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;
  }

我們來(lái)分析一下這個(gè)方法的主要功能:

  • 第一步:獲取 Bean 對(duì)象
    Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);(方法中第 2 行)
  • 第二步:為 Bean 對(duì)象賦值,填充字段
    foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;(方法中第 9 行)

現(xiàn)在我們就來(lái)依次看一下具體是如何實(shí)現(xiàn)每一步的。

首先我們跟入第一步的代碼,直到進(jìn)入 org.apache.ibatis.executor.resultset.DefaultResultSetHandler 類(lèi)的 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);//對(duì)!就是這一行!
    } 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);
  }

這個(gè)方法首先要通過(guò) ResultMap 獲取要?jiǎng)?chuàng)建對(duì)象的類(lèi)型,然后根據(jù)該類(lèi)型判斷要如何實(shí)例化。因?yàn)樵谠摐y(cè)試中,我們要獲取一個(gè)普通 Bean 對(duì)象 Author 的實(shí)例,它有默認(rèn)的構(gòu)造方法,所以會(huì)進(jìn)入方法的第 9 行代碼:return objectFactory.create(resultType); 這行代碼我想大家已經(jīng)不陌生了,在獲取 ArrayList 對(duì)象的時(shí)候也是通過(guò) ObjectFactory 的 create() 實(shí)現(xiàn)的。那么也就是說(shuō),對(duì)于結(jié)果集 List 的 Bean 對(duì)象的實(shí)例化是通過(guò) ObjectFactory 實(shí)現(xiàn)的,也就是通過(guò)反射實(shí)現(xiàn)的!

通過(guò)反射生成的 Author 對(duì)象

現(xiàn)在我們拿到了 Bean 對(duì)象,剩下的就是為這個(gè) Bean 對(duì)象的每個(gè)字段根據(jù)我們查詢(xún)的結(jié)果賦值了。我們進(jìn)入 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) {
      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);
        //略...
        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;
  }

上面這個(gè)方法首先通過(guò) ResultMap 獲取到要映射的列名列表 mappedColumnNames 和屬性結(jié)果映射列表
propertyMappings。然后遍歷 propertyMappings,從 ResultSet 中獲取每個(gè)列對(duì)應(yīng)的值并將屬性值存儲(chǔ)到我們之前獲得的 Author 對(duì)象中。其中,方法的第 10 行:Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix); 為獲取值,我們跟蹤一下:

  private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
      throws SQLException {
    if (propertyMapping.getNestedQueryId() != null) {
      //略...
    } else {
      final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
      final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
      return typeHandler.getResult(rs, column);
    }
  }

該方法首先獲取該映射的 typeHandler 和列名,用 typeHandler 來(lái)獲取:

  @Override
  public Integer getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    return rs.getInt(columnName);
  }

這是我們熟悉的 JDBC 代碼,但是需要提醒一下,我們前面分析過(guò),這里的 ResultSet 是通過(guò)動(dòng)態(tài)代理生成的,所以調(diào)用其方法不是直接從我們熟知的 ResultSet 獲取,而是通過(guò)代理類(lèi) ResultSetLogger 來(lái)實(shí)現(xiàn),那么接著它會(huì)進(jìn)入到 ResultSetLogger 的 invoke():

  @Override
  public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, params);
      }    
      Object o = method.invoke(rs, params);
      if ("next".equals(method.getName())) {
        if (((Boolean) o)) {
          rows++;
          if (isTraceEnabled()) {
            ResultSetMetaData rsmd = rs.getMetaData();
            final int columnCount = rsmd.getColumnCount();
            if (first) {
              first = false;
              printColumnHeaders(rsmd, columnCount);
            }
            printColumnValues(columnCount);
          }
        } else {
          debug("     Total: " + rows, false);
        }
      }
      clearColumnInfo();
      return o;
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

在該方法的第 5 行代碼我們就得到了最終的值。好,現(xiàn)在我們拿到了字段的值,就差把值塞進(jìn) Author 里了。我們回到 DefaultResultSetHandler 的 applyPropertyMappings(...) 方法中,跟入 metaObject.setValue(property, value); 這行代碼,直到進(jìn)入 BeanWrapper 類(lèi)的 setBeanProperty(...):

  private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {
    try {
      Invoker method = metaClass.getSetInvoker(prop.getName());
      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);
    }
  }

很明顯,這里也用的是反射,try 中的第一行就是在獲取要調(diào)用的方法。MyBatis 中用到了大量的反射,源碼中專(zhuān)門(mén)有一個(gè) reflection 模塊,就是封裝 Java 反射的。我們先跟進(jìn)去看一下:

  public Invoker getSetInvoker(String propertyName) {
    Invoker method = setMethods.get(propertyName);
    if (method == null) {
      throw new ReflectionException("There is no setter for property named '" + propertyName + "' in '" + type + "'");
    }
    return method;
  }

我們看方法的第 1 行代碼:Invoker method = setMethods.get(propertyName);,傳入的參數(shù)是屬性名,setMethods 是個(gè) HashMap<String, Invoker>,key 即為屬性名。setMethods 預(yù)先存儲(chǔ)了 Bean 中所有屬性及其對(duì)應(yīng)的包含 set 方法的 MethodInvoker。

setMethods

那么現(xiàn)在我們就可以判斷出來(lái),第 1 行代碼通過(guò)屬性名獲取到對(duì)應(yīng)的包含了該屬性的 set 方法的 MethodInvoker。然后返回到 BeanWrapper 的 setBeanProperty(...) 方法中,我們繼續(xù)跟入 try 中的代碼:method.invoke(object, params);:

  @Override
  public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
    return method.invoke(target, args);
  }

其中,這時(shí)的 method 為 public void com.zhaoxueer.learn.entity.Author.setName(java.lang.String),target 為 Author 對(duì)象,args 即為屬性對(duì)應(yīng)的值。那么接下來(lái)它就會(huì)進(jìn)入我們定義的 Bean,即 Author 中的 setName(...) 方法:

    public void setName(String name) {
        this.name = name;
    }

這樣,循環(huán)完所有的屬性結(jié)果映射后,我們就會(huì)獲得一個(gè)完美的已經(jīng)被賦了數(shù)據(jù)庫(kù)查詢(xún)結(jié)果的值的 Author 對(duì)象。當(dāng)我們執(zhí)行完所有的循環(huán)后,我們就會(huì)最終拿到我們想要的 List<Author> 的結(jié)果列表。至此,大功告成!

最后,簡(jiǎn)單總結(jié)一下 Mapper 方法執(zhí)行。我們分了四篇分別來(lái)分析了 Mapper 方法是如何執(zhí)行的,以及 Mapper 方法執(zhí)行的前因(獲取語(yǔ)句+參數(shù)映射)后果(結(jié)果映射)。其中,從獲取 Connection、獲取 PreparedStatement 到獲取 ResultSet 都使用動(dòng)態(tài)代理的方式來(lái)實(shí)現(xiàn),分步打印日志的同時(shí)獲取對(duì)象。對(duì)于參數(shù)和結(jié)果映射都使用了 TypeHandler,我們不需要為每個(gè)要映射的屬性或字段顯式配置 TypeHandler,MyBatis 會(huì)根據(jù)參數(shù)值和結(jié)果字段值實(shí)際的類(lèi)型來(lái)自動(dòng)推算 TypeHandler。對(duì)于結(jié)果映射,當(dāng)我們從數(shù)據(jù)庫(kù)獲得 ResultSet 之后,通過(guò)使用 ObjectFactory 來(lái)反射來(lái)實(shí)例化對(duì)應(yīng)的 Java 對(duì)象,并通過(guò)反射的方式將字段值存儲(chǔ)到 Java 對(duì)象中。

附:

當(dāng)前版本:mybatis-3.5.0
官網(wǎng)文檔:MyBatis
項(xiàng)目實(shí)踐:MyBatis Learn
手寫(xiě)源碼:MyBatis 簡(jiǎn)易實(shí)現(xiàn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,835評(píng)論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,676評(píng)論 3 419
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,730評(píng)論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,118評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,873評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,266評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評(píng)論 3 443
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,482評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,036評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,846評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,025評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,279評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,684評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,953評(píng)論 1 289
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,751評(píng)論 3 394
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,016評(píng)論 2 375

推薦閱讀更多精彩內(nèi)容

  • 1. 簡(jiǎn)介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存儲(chǔ)過(guò)程以及高級(jí)映射的優(yōu)秀的...
    笨鳥(niǎo)慢飛閱讀 5,561評(píng)論 0 4
  • 在前面的探索中,我們已經(jīng)知道了 MyBatis 是如何 getMapper 并執(zhí)行 Mapper 接口中的方法來(lái)進(jìn)...
    兆雪兒閱讀 470評(píng)論 0 1
  • MyBatis 理論篇 [TOC] 什么是MyBatis ?MyBatis是支持普通SQL查詢(xún),存儲(chǔ)過(guò)程和高級(jí)映射...
    有_味閱讀 2,930評(píng)論 0 26
  • MyBatis 是支持定制化 SQL、存儲(chǔ)過(guò)程以及高級(jí)映射的優(yōu)秀的持久層框架,其主要就完成2件事情: 封裝JDBC...
    慕容小偉閱讀 1,052評(píng)論 0 2
  • 有人說(shuō),最浪漫的不是“我愛(ài)你”,而是“在一起”。 我大聲地喊出對(duì)你的思念,婉轉(zhuǎn)惆悵中浸透著一個(gè)女子深藏的心事。 或...
    小壹娘閱讀 184評(píng)論 0 0