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

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

參數(shù)映射是通過 TypeHandler 實(shí)現(xiàn)的侈百,那么同理瓮下,結(jié)果集映射也應(yīng)該是通過 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(...) 方法來看:

  @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> 類型的結(jié)果。我們提取一下關(guān)鍵步驟:

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

現(xiàn)在笙蒙,我們就來對(duì)每一步進(jìn)行跟蹤分析抵屿。

1 獲取 ResultSet

ResultSetWrapper rsw = getFirstResultSet(stmt);

步入這行代碼,進(jìn)入本類的 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)于獲取語句+參數(shù)映射的分析我們知道捅位,這個(gè)方法傳入的參數(shù) stmt 是通過動(dòng)態(tài)代理方法生成的晌该,那么上面這個(gè)方法中的第一行代碼:ResultSet rs = stmt.getResultSet(); 自然就會(huì)進(jìn)入動(dòng)態(tài)代理類 PreparedStatementLogger 中的 invoke(...) 方法來獲取 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);
    }
  }

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

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

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ì)分析過亚再,此處便不再贅述郭膛,感興趣的同學(xué)可以查看之前的篇章并結(jié)合自己實(shí)踐跟蹤源碼來回憶一下。

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

ResultMap

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

3 獲取 List<Object>

handleResultSet(rsw, resultMap, multipleResults, null);

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

首先要說明的一點(diǎn)是,這行代碼是在 while 循環(huán)中己肮,即通過遍歷我們拿到的 ResultSetWrapper (封裝了 ResultSet)士袄,來循環(huán)處理每一條結(jié)果集。現(xiàn)在就讓我們步入這行代碼谎僻,進(jìn)入本類的 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 行代碼通過 ObjectFactory 生成 DefaultResultHandler,該 DefaultResultHandler 持有生成的 List 實(shí)例鞍盗。第 7 行代碼則是給第 6 行代碼執(zhí)行生成的 List 填充值需了。而第 8 行代碼則是將分頁處理過后的 List<Object> 加入 List 類型的 multipleResults 中返回。

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

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

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

為了印證這一點(diǎn)涮俄,我們跟蹤 create(...) 方法來看一下 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 類型蛉拙,繼續(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);
    }
  }

顯而易見,原來 MyBatis 使用 ObjectFactory 生成目標(biāo)類的實(shí)例彻亲,是通過反射實(shí)現(xiàn)的孕锄!對(duì)于反射,我想大家已經(jīng)很熟悉了苞尝,這里就不展開分析了畸肆。

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

進(jìn)入 org.apache.ibatis.executor.resultset.DefaultResultSetHandler 類的 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)行邏輯分頁的,這一點(diǎn)我們已經(jīng)在 MyBatis 最佳實(shí)踐篇 1:分頁 一文中分析過豁辉,這里便不再贅述令野。我們重點(diǎn)來看 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ù)庫獲取到的每一條記錄變成我們想要的集合中的 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;
  }

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

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

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

首先我們跟入第一步的代碼绞呈,直到進(jìn)入 org.apache.ibatis.executor.resultset.DefaultResultSetHandler 類的 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è)方法首先要通過 ResultMap 獲取要?jiǎng)?chuàng)建對(duì)象的類型佃声,然后根據(jù)該類型判斷要如何實(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í)候也是通過 ObjectFactory 的 create() 實(shí)現(xiàn)的志鹃。那么也就是說夭问,對(duì)于結(jié)果集 List 的 Bean 對(duì)象的實(shí)例化是通過 ObjectFactory 實(shí)現(xiàn)的,也就是通過反射實(shí)現(xiàn)的曹铃!

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

現(xiàn)在我們拿到了 Bean 對(duì)象甲喝,剩下的就是為這個(gè) Bean 對(duì)象的每個(gè)字段根據(jù)我們查詢的結(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è)方法首先通過 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 來獲韧勺拧:

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

這是我們熟悉的 JDBC 代碼,但是需要提醒一下,我們前面分析過承匣,這里的 ResultSet 是通過動(dòng)態(tài)代理生成的蓖乘,所以調(diào)用其方法不是直接從我們熟知的 ResultSet 獲取,而是通過代理類 ResultSetLogger 來實(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 類的 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 中用到了大量的反射耗式,源碼中專門有一個(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)在我們就可以判斷出來枫慷,第 1 行代碼通過屬性名獲取到對(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)的值誉裆。那么接下來它就會(huì)進(jìn)入我們定義的 Bean顿颅,即 Author 中的 setName(...) 方法:

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

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

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

附:

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市戈盈,隨后出現(xiàn)的幾起案子奠衔,更是在濱河造成了極大的恐慌,老刑警劉巖塘娶,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件归斤,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡刁岸,警方通過查閱死者的電腦和手機(jī)脏里,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來虹曙,“玉大人迫横,你說我怎么就攤上這事≡吞迹” “怎么了矾踱?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)疏哗。 經(jīng)常有香客問我呛讲,道長(zhǎng),這世上最難降的妖魔是什么返奉? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任贝搁,我火速辦了婚禮,結(jié)果婚禮上芽偏,老公的妹妹穿的比我還像新娘雷逆。我一直安慰自己,他們只是感情好哮针,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般十厢。 火紅的嫁衣襯著肌膚如雪等太。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天蛮放,我揣著相機(jī)與錄音缩抡,去河邊找鬼。 笑死包颁,一個(gè)胖子當(dāng)著我的面吹牛瞻想,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播娩嚼,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼蘑险,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了岳悟?” 一聲冷哼從身側(cè)響起佃迄,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎贵少,沒想到半個(gè)月后呵俏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡滔灶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年普碎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片录平。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡麻车,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出萄涯,到底是詐尸還是另有隱情绪氛,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布涝影,位于F島的核電站枣察,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏燃逻。R本人自食惡果不足惜序目,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望伯襟。 院中可真熱鬧猿涨,春花似錦、人聲如沸姆怪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至俺附,卻和暖如春肥卡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背事镣。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工步鉴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人璃哟。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓氛琢,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親随闪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子阳似,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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

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