Mybatis ResultMap composite mapping use and source code analysis

 We know that in mybatis, we can perform nested queries for a column of values ​​as input parameters, so what should we do if there are multiple input parameters? Mybatis supports compound mapping. Let's look at the use of compound mapping through example code.
<resultMap id="postLiteMap2NestedWithSelect" type="org.apache.ibatis.domain.blog.BlogLite">
    <id column="blog_id" property="id" />
    <collection property="posts" ofType="org.apache.ibatis.domain.blog.PostLite">
      <constructor>
          <arg javaType="org.apache.ibatis.domain.blog.PostLiteId" column="{id=id}" select="selectPostLiteId" />
          <arg javaType="_int" column="blog_id"/>
      </constructor>
    </collection>
  </resultMap>

  <mapper namespace="org.apache.ibatis.domain.blog.mappers.PostMapper">
  <resultMap id="postLiteIdMap" type="org.apache.ibatis.domain.blog.PostLiteId">
      <constructor>
          <idArg javaType="_int" column="id"/>
      </constructor>
  </resultMap>

<select id="selectPostLite2NestedWithSelect" resultMap="postLiteMap2NestedWithSelect">
      select id, 1 as blog_id from post where blog_id is not null
 </select>
<select id="selectPostLiteId" resultMap="postLiteIdMap">
      select ${id} as id from (values(0)) as t
 </select>

  

Inquire
List<BlogLite> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.selectPostLite2NestedWithSelect");



What kind of processing is this? Let's take a look at the sequence diagram
 
 
Let's see how Mybatis handles it through the code

public ResultMapping buildResultMapping(
      Class<?> resultType,
      String property,
      String column,
      Class<?> javaType,
      JdbcType jdbcType,
      String nestedSelect,
      String nestedResultMap,
      String notNullColumn,
      String columnPrefix,
      Class<? extends TypeHandler<?>> typeHandler,
      List<ResultFlag> flags,
      String resultSet,
      String foreignColumn,
      boolean lazy) {
    //
    Class<?> javaTypeClass = resolveResultJavaType(resultType, property, javaType);
    //type handler
    TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler);
    // Parse mixed columns
    List<ResultMapping> composites = parseCompositeColumnName(column);
    //Build ResultMapping
    return new ResultMapping.Builder(configuration, property, column, javaTypeClass)
        .jdbcType(jdbcType)
            // Namespace processing of nested query IDs
        .nestedQueryId(applyCurrentNamespace(nestedSelect, true))
            //Namespace processing of nested ResultMap
        .nestedResultMapId(applyCurrentNamespace(nestedResultMap, true))
        .resultSet(resultSet)
        .typeHandler (typeHandlerInstance)
        .flags(flags == null ? new ArrayList<ResultFlag>() : flags)
        .composites(composites)
        .notNullColumns(parseMultipleColumnNames(notNullColumn))
        .columnPrefix(columnPrefix)
        .foreignColumn(foreignColumn)
        .lazy(lazy)
        .build();
  }

private List<ResultMapping> parseCompositeColumnName(String columnName) {
    List<ResultMapping> composites = new ArrayList<ResultMapping>();
    //If columnName is not null and columnName contains "=" or contains ","
    if (columnName != null && (columnName.indexOf('=') > -1 || columnName.indexOf(',') > -1)) {
      // split the string
      StringTokenizer parser = new StringTokenizer(columnName, "{}=, ", false);
      while (parser.hasMoreTokens()) {
        //get properties
        String property = parser.nextToken();
        //get column
        String column = parser.nextToken();
        //Build a composite ResultMapping
        ResultMapping complexResultMapping = new ResultMapping.Builder(
            configuration, property, column, configuration.getTypeHandlerRegistry().getUnknownTypeHandler()).build();
        composites.add(complexResultMapping);
      }
    }
    return composites;
  }

  

The result of this is:
 
So far, it can be found that {id=id} is parsed into a composite resultMapping, so how is it handled when it is used?
 
In DefaultResultSetHandler, the nested query processing in the constructor is as follows, if the configuration is a composite mapping, the internal mapping of the processing composite mapping
//Get the value of the nested query constructor parameter
  private Object getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix) throws SQLException {
    // Nested query ID
    final String nestedQueryId = constructorMapping.getNestedQueryId();
    //Nested query MappedStatement
    final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
    // Nested query parameter types
    final Class nestedQueryParameterType = nestedQuery.getParameterMap().getType();
    //Get the nested query input parameter value
    final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, constructorMapping, nestedQueryParameterType, columnPrefix);
    Object value = null;
    if (nestedQueryParameterObject != null) {
      final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
      final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
      final Class targetType = constructorMapping.getJavaType();
      final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
      value = resultLoader.loadResult();
    }
    return value;
  }

 private Object prepareParameterForNestedQuery(ResultSet rs, ResultMapping resultMapping, Class parameterType, String columnPrefix) throws SQLException {
    //if it is a composite map
    if (resultMapping.isCompositeResult()) {
      return prepareCompositeKeyParameter(rs, resultMapping, parameterType, columnPrefix);
    } else {
      return prepareSimpleKeyParameter(rs, resultMapping, parameterType, columnPrefix);
    }
  }

 private Object prepareCompositeKeyParameter(ResultSet rs, ResultMapping resultMapping, Class parameterType, String columnPrefix) throws SQLException {
    //create parameter object
    final Object parameterObject = instantiateParameterObject(parameterType);
    final MetaObject metaObject = configuration.newMetaObject(parameterObject);
    boolean foundValues = false;
    // Traverse the composite result map
    for (ResultMapping innerResultMapping : resultMapping.getComposites()) {
      //get parameter type
      final Class propType = metaObject.getSetterType(innerResultMapping.getProperty());
      //get type handler
      final TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(propType);
      //get value
       final Object propValue = typeHandler.getResult(rs, prependPrefix(innerResultMapping.getColumn(), columnPrefix));
      // Set to the parameter object if the parameter value is not null
      if (propValue != null) {
        metaObject.setValue(innerResultMapping.getProperty(), propValue);
        foundValues = true;
      }
    }
    return foundValues ? parameterObject : null;
  }

  

The input parameter id value obtained at this time is 1, and compound mapping can also be used in other nested queries.
  
<resultMap id="addressMapper"
    type="org.apache.ibatis.submitted.column_prefix.Address">
    <constructor>
      <idArg column="id" javaType="int" />
      <arg column="state" javaType="string" />
    </constructor>
    <result property="city" column="city" />
    <result property="hasPhone" column="has_phone" />
    <association property="stateBird" select="selectStateBird"
      column="state" />
    <association property="zip" select="selectZip"
      column="{state=state,city=city}" />
    <association property="phone1" select="selectPhone"
      column="phone1_id" />
    <association property="phone2" select="selectPhone"
      column="phone2_id" />
    <discriminator column="addr_type" javaType="int">
      <case value="1"
        resultType="org.apache.ibatis.submitted.column_prefix.AddressWithCaution">
        <result property="caution" column="caution" />
      </case>
    </discriminator>
  </resultMap>

  

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325558161&siteId=291194637