Mybatis結果集自動映射


  在使用Mybatis時,有的時候可以不用定義resultMap,而是直接在<select>語句上指定resultType。這個時候其實就用到了Mybatis的結果集自動映射。Mybatis的自動映射默認是開啟的,其在映射的時候,會先把沒有在resultMap中,定義字段映射的字段按照名稱相同的方式,自動映射到返回類型的對應屬性上。自動映射的時候會忽略大小寫,比如查詢語句中查詢出來一個字段是ID,對應的返回類型有一個屬性id,且有一個setId()方法,那么id跟ID也是可以匹配的,是可以自動映射的,Mybatis就會把查詢出來的結果集中字段ID對應的值賦給返回類型對象的id屬性。

源碼分析

       關於自動映射這塊的邏輯規則可以參考Mybatis的DefaultResultSetHandler的源碼,其核心代碼如下。

private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {

    List<UnMappedColumAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);

    boolean foundValues = false;

    if (autoMapping.size() > 0) {

      for (UnMappedColumAutoMapping mapping : autoMapping) {

        final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);

        if (value != null || configuration.isCallSettersOnNulls()) {

          if (value != null || !mapping.primitive) {

            metaObject.setValue(mapping.property, value);

          }

          foundValues = true;

        }

      }

    }
    return foundValues;
  }

  private List<UnMappedColumAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {

    final String mapKey = resultMap.getId() + ":" + columnPrefix;

    List<UnMappedColumAutoMapping> autoMapping = autoMappingsCache.get(mapKey);

    if (autoMapping == null) {

      autoMapping = new ArrayList<UnMappedColumAutoMapping>();

      final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);

      for (String columnName : unmappedColumnNames) {

        String propertyName = columnName;

        if (columnPrefix != null && !columnPrefix.isEmpty()) {

          if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {

            propertyName = columnName.substring(columnPrefix.length());

          } else {

            continue;

          }

        }

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

            autoMapping.add(new UnMappedColumAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));

          }
        }
      }
      autoMappingsCache.put(mapKey, autoMapping);
    }
    return autoMapping;
  } 

 

  在上面的源碼中createAutomaticMappings()方法中的下面這句就是獲取當前查詢結果集中沒有在resultMap中映射的字段,以進行自動映射。詳情請參考完整的源碼。

        final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);

示例

       現假設有一個User類,其有id、name、username、email、mobile屬性,然后有下面這樣一個查詢及其對應的resultMap定義。我們可以看到我們查詢出來的有id、name、user_name、email和mobile字段,在resultMap中我們只配置字段user_name對應的是username屬性,其它的我們都沒配置,但是查詢出來的結果中User對象的id、name、username、email和mobile屬性都會有值,因為它們會被Mybatis以自動映射策略進行賦值。

  <resultMap type="mybatis.model.User" id="BaseResult">
      <result column="user_name" property="username"/>
   </resultMap>

   <select id="findById" resultMap="BaseResult" parameterType="java.lang.Long" >
      select id,name,username user_name,email,mobile from t_user where id=#{id}
   </select>  

自動映射策略

       Mybatis的自動映射策略默認是開啟的,而且默認是只對非嵌套的resultMap進行自動映射。這是通過Mybatis的全局配置autoMappingBehavior參數配置的。一共有三種取值,分別是NONE、PARTIAL和FULL。

  l  NONE表示不啟用自動映射

  l  PARTIAL表示只對非嵌套的resultMap進行自動映射

  l  FULL表示對所有的resultMap都進行自動映射

   <!-- 自動映射類型,可選值為NONE、PARTIAL和FULL,參考AutoMappingBehavior枚舉 -->
      <setting name="autoMappingBehavior" value="PARTIAL"/>

  除了全局的是否啟用自動映射的配置外,還可以對特定的resultMap設置是否啟用自動映射。這是通過resultMap的autoMapping屬性配置的,可選值是true和false。定義在resultMap上的autoMapping的優先級比全局配置的優先級更高

resultType自動映射分析

       在指定一個查詢語句的返回結果時,可以直接指定resultType,也可以是指定resultMap,然后由指定的resultMap的type屬性指定真實的返回類型。實際上,Mybatis的底層在對結果集進行處理時,都是通過resultMap進行處理的。當我們指定的是resultType時,Mybatis內部會生成一個空的resultMap,然后指定其對應的type為我們指定的resultType類型。那這個時候之所以返回結果能自動映射到resultType類型的對應屬性上,就是上面介紹的Mybatis的自動映射機制的作用。如果在這種情況下,把全局的自動映射關閉,那么Mybatis就不能自動映射,也就得不到我們需要的返回結果了。如下就是直接指定的resultType。

  <select id="findById" resultType="mybatis.model.User" parameterType="java.lang.Long" >
      select id,name,username,email,mobile from t_user where id=#{id}
   </select>

   Mybatis的mapper.xml文件的內容是由XMLMapperBuilder解析的,而其中定義的Mapper語句(select、insert等)則是由XMLStatementBuilder解析的,解析后會生成一個MappedStatement。對於Select語句,其對應的resultMap的解析的核心邏輯如下,更多信息請參考官方源碼。

private List<ResultMap> getStatementResultMaps(String resultMap, Class<?> resultType,String statementId) {
resultMap
= applyCurrentNamespace(resultMap, true); List<ResultMap> resultMaps = new ArrayList<ResultMap>(); if (resultMap != null) { String[] resultMapNames = resultMap.split(","); for (String resultMapName : resultMapNames) { try { resultMaps.add(configuration.getResultMap(resultMapName.trim())); } catch (IllegalArgumentException e) { throw new IncompleteElementException("Could not find result map " + resultMapName, e); } } } else if (resultType != null) { ResultMap inlineResultMap = new ResultMap.Builder( configuration, statementId + "-Inline", resultType, new ArrayList<ResultMapping>(), null).build(); resultMaps.add(inlineResultMap); } return resultMaps; }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM