Mybatis resulttype 和resultMap 概念分析

  • 目录
  • 概述
  • 需求:
  • 设计思路
  • 实现思路分析
  • 1. resultType源码分析
  • 2.resultMap分析
  • 3.比较和区别:
  • 拓展Demo实现
  • 相关代码如下:
  • 实验效果:
  • 分析:
  • 小结:
  • 参考资料和推荐阅读


Efficient work is better than attitude。
talk is cheap, show me the code,make a better result.

目录

概述

今天上午主要调试逻辑。
下午写的代码,出现了一个异常,我当时没反应出来,今天从源码角度来分析一下。

需求:

需求整理如下:
1.resultType源码分析
2.resultMap分析

设计思路

暂无

实现思路分析

1. resultType源码分析

private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null);
    if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
      final MetaObject metaObject = configuration.newMetaObject(resultObject);
      boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty();
	  //判断是否可以使用resultType,如果不做任何额外设置,返回true
      if (shouldApplyAutomaticMappings(resultMap, false)) {
        foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
      }
	  //使用resultMap方式
      foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
      foundValues = lazyLoader.size() > 0 || foundValues;
      resultObject = foundValues ? resultObject : null;
      return resultObject;
    }
    return resultObject;
  }

2.resultMap分析

public <T> void addMapper(Class<T> type) {
    mapperRegistry.addMapper(type);
  }
public void parse() {
    String resource = type.toString();
    if (!configuration.isResourceLoaded(resource)) {
      loadXmlResource();
      configuration.addLoadedResource(resource);
      assistant.setCurrentNamespace(type.getName());
      parseCache();
      parseCacheRef();
      Method[] methods = type.getMethods();
      for (Method method : methods) {
        try {
          // issue #237
          if (!method.isBridge()) {
            parseStatement(method);
          }
        } catch (IncompleteElementException e) {
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }

@ResultMap的解析就在parse方法中,转到parse方法。
进入parseStatement(method)方法中。

void parseStatement(Method method) {
      .....
      String resultMapId = null;
      ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
      if (resultMapAnnotation != null) {
        String[] resultMaps = resultMapAnnotation.value();
        StringBuilder sb = new StringBuilder();
        for (String resultMap : resultMaps) {
          if (sb.length() > 0) {
            sb.append(",");
          }
          sb.append(resultMap);
        }
        resultMapId = sb.toString();
      } else if (isSelect) {
        resultMapId = parseResultMap(method);
      }

      assistant.addMappedStatement(
          mappedStatementId,
          resultMapId,
          getReturnType(method),
          resultSetType,
          flushCache
          );
  }
  1. 字段映射
@Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

这一步有个操作就是获取到MappedStatement对象, 从许多前文中我们知MappedStatement对象中存放着Sql、ResultMap、timeout等等参数,而在后文中就需要从MappedStatement对象中取出ResultMap中,这个等会再说,先看query方法

@Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
    statement.execute(sql);
    return resultSetHandler.<E>handleResultSets(statement);
  }

3.比较和区别:

resultType和resultMap功能类似 ,都是返回对象信息 ,但是resultMap要更强大一些 ,可自定义。因为resultMap要配置一下,表和类的一一对应关系,所以说就算你的字段名和你的实体类的属性名不一样也没关系,都会给你映射出来,但是,resultType就比较鸡肋了,必须字段名一样。

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.getNestedResultMapId() != null) {
        // the user added a column attribute to a nested result map, ignore it
        column = null;
      }
      if (propertyMapping.isCompositeResult()
          || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
          || propertyMapping.getResultSet() != null) {
        Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
        // issue #541 make property optional
        final String property = propertyMapping.getProperty();
        if (property == null) {
          continue;
        } else if (value == DEFERED) {
          foundValues = true;
          continue;
        }
        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;
  }

这一步做的就是从resultMap中取出数据库中表字段集合mappedColumnNames。然后对字段映射关系集合propertyMappings进行遍历。用上述mappedColumnNames判断column是否在mappedColumnNames中,我觉得这一步其实可以省了,column都是从resultMap中的映射关系中取出来的,而mappedColumnNames是从resultMap中取到的,这两个基本是等同的,这里有空以后再看下吧。

接下来其实就没多少说的了,根据字段映射取到对应值,然后进行set操作,最后返回metaObject,组装成list集合后返回给调用端。

上面文章中,其实要可以说的还挺多的,比如说多ResultMap返回,自动映射、注解@Results、@Result自定义映射关系等等,这个以后有空抽时间也来讲一讲吧。

拓展Demo实现

相关代码如下:

1.略

2.略

实验效果:

待完成

分析:

待补充

小结:

主要讲述了mybatis中resultType 和resultMap原理和简单,里面有许多不足,请大家指正~