Mybatis ResultMap复合映射使用以及源码分析

我们知道在mybatis中可以针对一列值作为入参进行嵌套查询,那么如果入参为多个时该如何处理呢? mybatis支持复合映射,下面通过示例代码看看复合映射的使用

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

  

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


这是怎样的一个处理过程呢?下面看看时序图

下面通过代码看看Mybatis是如何处理的

 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);
    //类型处理器
    TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler);
    //解析混合列
    List<ResultMapping> composites = parseCompositeColumnName(column);
    //构建ResultMapping
    return new ResultMapping.Builder(configuration, property, column, javaTypeClass)
        .jdbcType(jdbcType)
            //对嵌套查询ID进行namespace处理
        .nestedQueryId(applyCurrentNamespace(nestedSelect, true))
            //对嵌套ResultMap进行namespace处理
        .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>();
    //如果columnName不为null 同时colunmnName中含有"=" 或者含有","号
    if (columnName != null && (columnName.indexOf(‘=‘) > -1 || columnName.indexOf(‘,‘) > -1)) {
      //分割字符串
      StringTokenizer parser = new StringTokenizer(columnName, "{}=, ", false);
      while (parser.hasMoreTokens()) {
        //获取属性
        String property = parser.nextToken();
        //获取列
        String column = parser.nextToken();
        //构建复合的ResultMapping
        ResultMapping complexResultMapping = new ResultMapping.Builder(
            configuration, property, column, configuration.getTypeHandlerRegistry().getUnknownTypeHandler()).build();
        composites.add(complexResultMapping);
      }
    }
    return composites;
  }

  

这样得到的结果是:

至此可以发现{id=id}被解析为了一个复合resultMapping那么在使用的时候又是如何处理的呢?

在DefaultResultSetHandler中对于构造方法中的嵌套查询处理如下,如果配置的是复合映射在处理复合映射的内部映射

//获取嵌套查询构造参数的值
  private Object getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix) throws SQLException {
    //嵌套查询ID
    final String nestedQueryId = constructorMapping.getNestedQueryId();
    //嵌套查询MappedStatement
    final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
    //嵌套查询参数类型
    final Class nestedQueryParameterType = nestedQuery.getParameterMap().getType();
    //获取嵌套查询入参值
    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 (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 {
    //创建参数对象
    final Object parameterObject = instantiateParameterObject(parameterType);
    final MetaObject metaObject = configuration.newMetaObject(parameterObject);
    boolean foundValues = false;
    //遍历复合结果映射
    for (ResultMapping innerResultMapping : resultMapping.getComposites()) {
      //获取参数类型
      final Class propType = metaObject.getSetterType(innerResultMapping.getProperty());
      //获取类型处理器
      final TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(propType);
      //获取值
       final Object propValue = typeHandler.getResult(rs, prependPrefix(innerResultMapping.getColumn(), columnPrefix));
      // 如果参数值不为null则设置到参数对象
      if (propValue != null) {
        metaObject.setValue(innerResultMapping.getProperty(), propValue);
        foundValues = true;
      }
    }
    return foundValues ? parameterObject : null;
  }

  

此时获取到的入参 id值为1 ,同样在其它嵌套查询中也可以使用复合映射

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

  

原文地址:https://www.cnblogs.com/wei-zw/p/9001906.html

时间: 2024-07-29 17:32:23

Mybatis ResultMap复合映射使用以及源码分析的相关文章

MyBatis框架的使用及源码分析(十一) StatementHandler

我们回忆一下<MyBatis框架的使用及源码分析(十) CacheExecutor,SimpleExecutor,BatchExecutor ,ReuseExecutor> , 这4个Excecutor执行sql操作的最终都调用了StatementHandler 来执行,我们拿SimpleExecutor来看: public int doUpdate(MappedStatement ms, Object parameter) throws SQLException { Statement st

【MyBatis】MyBatis Tomcat JNDI原理及源码分析

一. Tomcat JNDI JNDI(java nameing and drectory interface),是一组在Java应用中访问命名和服务的API,所谓命名服务,即将对象和名称联系起来,使得可以通过名称访问并获取对象. 简单原理介绍:点击访问 tomcat已经集成该服务(内置并默认使用DBCP连接池),简单来说就是键值对的mapping,而且在tomcat服务器启动的首页configuration中就已经有完成的示例代码.要想使用tomcat的JNDI服务,只需要导入相关的jar包,

Mybatis结合Spring注解自动扫描源码分析

作为一个想做架构师的程序员,必须是一个优秀的程序员,在引入某一个框架的时候,必须要研究源码,将新的开源框架的风险变为可控性. 1.Spring结合Mybatis最常用的配置. <!--理论加实践,才是架构师嘚最佳实践 --> <!--JDBC Data Source --> < bean id= "testdataSource" class= "org.springframework.jdbc.datasource.DriverManagerDa

Mybatis Interceptor 拦截器原理 源码分析

Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最好了解下它的原理,以便写出安全高效的插件. 代理链的生成 Mybatis支持对Executor.StatementHandler.PameterHandler和ResultSetHandler进行拦截,也就是说会对这4种对象进行代理. 通过查看Configuration类的源代码我们可以看到,每次都

MyBatis框架的使用及源码分析(十二) ParameterHandler

在StatementHandler使用prepare()方法后,接下来就是使用ParameterHandler来设置参数,让我们看看它的定义: package org.apache.ibatis.executor.parameter; import java.sql.PreparedStatement; import java.sql.SQLException; /** * A parameter handler sets the parameters of the {@code Prepare

Mybatis源码分析之Cache二级缓存原理 (五)

一:Cache类的介绍 讲解缓存之前我们需要先了解一下Cache接口以及实现MyBatis定义了一个org.apache.ibatis.cache.Cache接口作为其Cache提供者的SPI(ServiceProvider Interface) ,所有的MyBatis内部的Cache缓存,都应该实现这一接口 Cache的实现类中,Cache有不同的功能,每个功能独立,互不影响,则对于不同的Cache功能,这里使用了装饰者模式实现. 看下cache的实现类,如下图: 1.FIFOCache:先进

mybatis源码分析(一)

mybatis源码分析(sqlSessionFactory生成过程) 1. mybatis框架在现在各个IT公司的使用不用多说,这几天看了mybatis的一些源码,赶紧做个笔记. 2. 看源码从一个demo引入如下: public class TestApp { private static SqlSessionFactory sqlSessionFactory; static { InputStream inputStream; String resource = "mybatis-confi

MyBatis源码分析-SQL语句执行的完整流程

MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录.如何新建MyBatis源码工程请点击MyBatis源码分析-IDEA新建MyBatis源码工程. MyBatis框架主要完成的是以下2件事情: 根据JD

【MyBatis源码分析】环境准备

前言 之前一段时间写了[Spring源码分析]系列的文章,感觉对Spring的原理及使用各方面都掌握了不少,趁热打铁,开始下一个系列的文章[MyBatis源码分析],在[MyBatis源码分析]文章的基础之上,可以继续分析数据库连接池.Spring整合MyBatis源码.Spring事物管理tx等等. [MyBatis源码分析]整个文章结构相较[Spring源码分析]稍微改一改,后者会在每一部分源码分析的开头列出要分析的源码的实例,比如: 分析Bean流程加载,就会先写Bean的代码示例及xml