Mybatis中的resultMap和resultType 和parameterMap与 parameterType的区别

说实话Mybatis的知识点不少,在之前讲过mybatis的一级缓存与二级缓存(作用于在Executor组件)。而今天的主题是parameterMap 、parameterType、resultType、parameterMap,它们作用于ParameterHandler组件与ResultSetHandler 组件层。如果有人问你这类问题,其实是在考你的基本功(是否知道mybatis的架构或源码),这样才能get到对方关注的点。

在XML 映射文件中随处可用:parameterMap 、parameterType、resultType、parameterMap

<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" >
  select 
  <include refid="Base_Column_List" />
  from bas_branchroute
  where branch_route_id = #{branchRouteId,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.String" >
  delete from bas_branchroute
  where branch_route_id = #{branchRouteId,jdbcType=VARCHAR}
</delete>
<delete id="deleteByExample" parameterType="com.xxx.oms.model.BasBranchrouteExample" >
  delete from bas_branchroute
  <if test="_parameter != null" >
    <include refid="Example_Where_Clause" />
  </if>
</delete>

基本用法

parameterMap

这是引用外部 parameterMap 的已经被废弃的方法。请使用内联参数映射和 parameterType 属性。

parameterType

将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler) 推断出具体传入语句的参数,默认值为未设置(unset)。

resultType

从这条语句中返回的期望类型的类的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。可以使用 resultType 或 resultMap,但不能同时使用。

resultMap

外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂映射的情形都能迎刃而解。可以使用 resultMap 或 resultType,但不能同时使用。

数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,而 ResultMap 就是 MyBatis 对这个问题的答案。它支持的元素有constructor(实例化类时注入)、association(关联一个对象)、collection(关联多个对象),可以表示对象间一对一、一对多等关系。更多介绍见官方文档

<!-- 构造方法 -->
<resultMap id="userMap" type="User">
	<constructor>
	   <idArg column="id" javaType="int"/>
	   <arg column="username" javaType="String"/>
	   <arg column="age" javaType="_int"/>
	</constructor>
</resultMap>
<!-- 一对一 -->
<resultMap id="userMap" type="User">
	<id property="id" column="id"></id>
	<result property="username" column="username"></result>
	<result property="password" column="password"></result>
	<result property="address" column="address"></result>
	<result property="email" column="email"></result>
	
	<association property="role" javaType="Role">
		<id property="id" column="role_id"></id>
		<result property="name" column="role_name"></result>
	</association>
</resultMap>
<!-- 一对多 -->
<resultMap id="userMap" type="User">
	<id property="id" column="id"></id>
	<result property="username" column="username"></result>
	<result property="password" column="password"></result>
	<result property="address" column="address"></result>
	<result property="email" column="email"></result>
	
	<collection property="roles" ofType="Role">
		<id property="id" column="role_id"></id>
		<result property="name" column="role_name"></result>
	</collection>
</resultMap>
<!-- 一对多:嵌套 Select 查询 -->
<resultMap id="menusMap" type="Menu">
	<id property="id" column="id"></id>
	<result property="name" column="name"></result>
	<result property="url" column="url"></result>
	<result property="m_desc" column="m_desc"></result>
	<result property="parent_id" column="parent_id"></result>
    <!-- ofType="Menu" 对应返回数据的类型-->
	<!--select="getMenus" 指定了SELECT语句的id-->
    <!--column="{parent_id=id}" 则是列的别名,参数的表达式-->
	<collection property="childMenu" ofType="Menu" select="getMenus"  column="{parent_id=id}"></collection>
</resultMap>

系统架构

关键组件

  • SqlSession:selectOne、selectList、selectMap、select、insert、update、delete、commit、rollback、flushStatements、close、clearCache、getConfiguration、getMapper、getConnection
  • Executor:update, query, flushStatements, commit, rollback, getTransaction, close, isClosed
  • StatementHandler:prepare, parameterize, batch, update, query
  • ParameterHandler:getParameterObject, setParameters
  • ResultSetHandler:handleResultSets, handleOutputParameters

内部交互

StatementHandler是非常重要的一层,它主要是对Statement的各种特殊处理(不负责执行),它支持三种Statement、
Prepared、Callable模式,默认值:Prepared。RoutingStatementHandler提供了一种适配模式化的统一入口。

源码解析

你理解了上面的内容,再回头看源码就不复杂了!源码分析基于mybatis-3.4.4版本展开。

ParameterHandler

public interface ParameterHandler {
  //获取参数对象
  Object getParameterObject();
  //设置参数对象
  void setParameters(PreparedStatement ps) throws SQLException;
}
public class DefaultParameterHandler implements ParameterHandler {
    public void setParameters(PreparedStatement ps) {
        ErrorContext.instance().activity("setting parameters").object(this.mappedStatement.getParameterMap().getId());
        List<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings();
        if (parameterMappings != null) {
            for(int i = 0; i < parameterMappings.size(); ++i) {//遍历参数
                ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    String propertyName = parameterMapping.getProperty();//参数名称
                    Object value;//参数值
                    if (this.boundSql.hasAdditionalParameter(propertyName)) {//是否有这个参数
                        value = this.boundSql.getAdditionalParameter(propertyName);//获取参数值
                    } else if (this.parameterObject == null) {
                        value = null;
                    } else if (this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass())) {
                        value = this.parameterObject;
                    } else {
                        MetaObject metaObject = this.configuration.newMetaObject(this.parameterObject);//这个是什么鬼,不急慢慢看
                        value = metaObject.getValue(propertyName);
                    }

                    TypeHandler typeHandler = parameterMapping.getTypeHandler();
                    JdbcType jdbcType = parameterMapping.getJdbcType();
                    if (value == null && jdbcType == null) {
                        jdbcType = this.configuration.getJdbcTypeForNull();
                    }

                    try {
                        typeHandler.setParameter(ps, i + 1, value, jdbcType);
                    } catch (TypeException var10) {
                        throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var10, var10);
                    } catch (SQLException var11) {
                        throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var11, var11);
                    }
                }
            }
        }

    }
}

问题1:确实setParameters()是关键点,它是什么时候被触发的呢?

它分别为 PreparedStatementHandler 和 CallableStatementHandler (执行存储过程)的  parameterize()中被调用。它在构建Statement 时被触发。

public class SimpleExecutor extends BaseExecutor {
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
      //构建Statement对象
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    //此时很关键,最终路径:RoutingStatementHandler.parameterize -> PreparedStatementHandler.parameterize
    stmt = handler.prepare(connection);
    handler.parameterize(stmt);
    return stmt;
  }
}
public class PreparedStatementHandler extends BaseStatementHandler {
  public void parameterize(Statement statement) throws SQLException {
    //最终执行这个
    parameterHandler.setParameters((PreparedStatement) statement);
  }
}

问题2:MetaObject是什么鬼?

MetaObject是将Properties映射为bean属性,实际上就是提供 类|集合|Map 的一种自动识别的访问形式。PropertyTokenizer是的属性分词器,提供了对象和集合的一种概念形式,如: object.parent.name(只是记录了 object 而已,可以通过next方法创建一个对象,返回new PropertyTokenizer(“parent”) 再次调用next 返回new PropertyTokenizer(“name”))。

//基本用法
@Test
public void testGetSet(){
    Animal animal = new Animal();
    //testBean
    MetaObject metaObject = MetaObject.forObject(animal, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
    metaObject.setValue("name","bean");
    System.out.println(animal.getName());
    //testMap
    animal.setName("map");
    Map<String,Animal> map = new HashMap<>();
    metaObject = MetaObject.forObject(map, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
    metaObject.setValue("deer",animal);
    System.out.println(map.get("deer").getName());
    //testCollection
    animal.setName("collection");
    List<Animal> list = new ArrayList<>();
    metaObject = MetaObject.forObject(list, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
    metaObject.add(animal);
    System.out.println(list.get(0).getName());
    //testTokenizer
    animal.setParent(new Animal());
    metaObject = MetaObject.forObject(animal, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
    metaObject.setValue("parent.name","tokenizer");
    System.out.println(animal.getParent().getName());
}
//源码
public class MetaObject {
 private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
    this.originalObject = object;//原始对象
    this.objectFactory = objectFactory;
    this.objectWrapperFactory = objectWrapperFactory;

    if (object instanceof ObjectWrapper) {//原始对象为ObjectWrapper
      this.objectWrapper = (ObjectWrapper) object;
    } else if (objectWrapperFactory.hasWrapperFor(object)) {
      this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
    } else if (object instanceof Map) {//原始对象为Map,提供了 object.setName,object.setAge 以及类似 object.getParent().setName(),object.getParent().getParent().setName() 的赋值方式
      this.objectWrapper = new MapWrapper(this, (Map) object);
    } else if (object instanceof Collection) {//原始对象为Collection,提供了集合的add,addAll 方法
      this.objectWrapper = new CollectionWrapper(this, (Collection) object);
    } else {//默认为BeanWrapper,提供的赋值与map相同
      this.objectWrapper = new BeanWrapper(this, object);
    }
  }
  //构建
  public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
    if (object == null) {
      return NULL_META_OBJECT;//记录Class相关的getter
    } else {
      return new MetaObject(object, objectFactory, objectWrapperFactory);
    }
  }
  public Object getValue(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
      if (metaValue == MetaObject.NULL_META_OBJECT) {
        return null;
      } else {
        return metaValue.getValue(prop.getChildren());
      }
    } else {
      return objectWrapper.get(prop);
    }
  }
  public void setValue(String name, Object value) {
    //属性分词器,识别属性.符号
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
      if (metaValue == MetaObject.NULL_META_OBJECT) {
        if (value == null && prop.getChildren() != null) {
          return; // don't instantiate child path if value is null
        } else {
          metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
        }
      }
      metaValue.setValue(prop.getChildren(), value);
    } else {
      objectWrapper.set(prop, value);
    }
  }
}

ResultSetHandler

还是按老的套路,以handleResultSets()为突破口,它主要是对Statement执行后产生的结果集转化成List。

public interface ResultSetHandler {
    //将Statement执行后产生的结果集转化成list
    <E> List<E> handleResultSets(Statement var1) throws SQLException;
    //将Statement执行后产生的结果集转化成cursor
    <E> Cursor<E> handleCursorResultSets(Statement var1) throws SQLException;
    //处理存储过程执行后的输出参数
    void handleOutputParameters(CallableStatement var1) throws SQLException;
}
public class DefaultResultSetHandler implements ResultSetHandler {
  @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);
      //根据resultMap处理rsw生成java对象,最关键的一步,最终调用getRowValue
      handleResultSet(rsw, resultMap, multipleResults, null);
      //获取结果集的下一个结果
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {//和resultMaps的遍历处理类似
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }
  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);
          //最终会调用getRowValue
          handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
          multipleResults.add(defaultResultHandler.getResultList());
        } else {//最终会调用getRowValue
          handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
        }
      }
    } finally {
      // issue #228 (close resultsets)
      closeResultSet(rsw.getResultSet());
    }
  }
  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;
      }
      //property映射,ResultLoaderMap属性嵌套查询且懒加载
      foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
      foundValues = lazyLoader.size() > 0 || foundValues;
      rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;
    }
    return rowValue;
  }
  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)) {//存在适用的typeHanlder类,事实上一般为基本数据类型或者其封装类
      return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
    } else if (!constructorMappings.isEmpty()) {//有参构造函数的constructor映射
      return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
    } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {//接口或者无参构造函数
      return objectFactory.create(resultType);
    } 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);
  }
}

它的被调用链不复杂,基本上可以直接定位到SimpleStatementHandler

此外还有一点细节:在xml中select的查询中配置的不是resultMap而是resultType,在mybatis中也是按照resultMap进行存储的,具体验证可看下面源码分析。

public class SimpleStatementHandler extends BaseStatementHandler {
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
    statement.execute(sql);
    return resultSetHandler.<E>handleResultSets(statement);//直接调用我们关注的方法
  }
}
public class MapperBuilderAssistant extends BaseBuilder {
  //在addMappedStatement方法(构建MappedStatement,它非常关键)中被调用
  private List<ResultMap> getStatementResultMaps(
      String resultMap,
      Class<?> resultType,
      String statementId) {
    resultMap = applyCurrentNamespace(resultMap, true);

    List<ResultMap> resultMaps = new ArrayList<ResultMap>();
    if (resultMap != null) {//resultMap配置支持多个用,分割的resultMap 
      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) {//resultType会构建一个inlineResultMap
      ResultMap inlineResultMap = new ResultMap.Builder(
          configuration,
          statementId + "-Inline",
          resultType,
          new ArrayList<ResultMapping>(),
          null).build();
      resultMaps.add(inlineResultMap);
    }
    return resultMaps;
  }
}
/**
* StatementHandler中通过它可以获取关于Statement所有细节
**/
public final class MappedStatement {

  private String resource;
  private Configuration configuration;//配置
  private String id;//mapper中的id
  private Integer fetchSize;//batch需要
  private Integer timeout;//超时
  private StatementType statementType;
  private ResultSetType resultSetType;
  private SqlSource sqlSource;//数据源
  private Cache cache;
  private ParameterMap parameterMap;
  private List<ResultMap> resultMaps;
  private boolean flushCacheRequired;
  private boolean useCache;
  private boolean resultOrdered;
  private SqlCommandType sqlCommandType;
  private KeyGenerator keyGenerator;
  private String[] keyProperties;
  private String[] keyColumns;
  private boolean hasNestedResultMaps;
  private String databaseId;
  private Log statementLog;
  private LanguageDriver lang;
  private String[] resultSets;
  public BoundSql getBoundSql(Object parameterObject) {//可执行的sql
  ...
  }
}

看源码其实没有什么技巧,要大胆提问+你的猜想,然后去验证,并从中体会学习优秀的设计。

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值