[Mybatis]关于Mapper配置文件中resultMap节点的标签用法

在使用 Mybatis 的时候,都会使用resultMap节点来绑定列与bean属性的对应关系,但是一般就只会使用其简单的属性,他还有一些比较复杂的属性可以实现一些高级的功能,在没查看源码之前,我也只会简单的使用,很多高级的用法都没有使用过,通过这次学习,希望能在工作使用,能够写出简洁高效的SQL。

resultMap的定义

先来看看 resultMap 节点的官方定义:

简单的使用:


  
  
  

会把列名和属性名进行绑定,该节点一共有 4 个属性:

1. id :表示该 resultMap,共其他的语句调用

2. type:表示其对于的pojo类型,可以使用别名,也可以使用全限定类名

3. autoMapping:如果设置这个属性,MyBatis将会为这个ResultMap开启或者关闭自动映射。这个属性会覆盖全局的属性 autoMappingBehavior。默认值为:unset。

4. extends:继承,一个 resultMap 可以继承另一个 resultMap,这个属性是不是没有用过 ? ^^

接下来看下它可以有哪些子节点:

  • constructor - 用于注入结果到构造方法中
  • id – 标识ID列
  • result – 表示一般列
  • association – 关联查询
  • collection – 查询集合
  • discriminator - 鉴别器:mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为

constructor 

在查询数据库得到数据后,会把对应列的值赋值给javabean对象对应的属性,默认情况下mybatis会调用实体类的无参构造方法创建一个实体类,然后再给各个属性赋值,如果没有构造方法的时候,可以使用 constructor 节点进行绑定,如现有如下的构造方法:

  public Person(int id, String name, String job, int age) {
        this.id = id;
        this.name = name;
        this.job = job;
        this.age = age;
    }

 则,可以使用 constructor  节点进行绑定:


        
            
            
            
            
        
    

association 

关联查询,在级联中有一对一、一对多、多对多等关系,association主要是用来解决一对一关系的,association 可以有多种使用方式:

比如现在有一个 Person 类,它有一个 Address 属性,关联 Address 对象:

public class Person implements Serializable {

    private int id;

    private String name;

    private String job;

    private int age;

    private Address address;
}

public class Address {
    private int id;

    private String name;

    private long number;

}

关联查询方式一:

 
        
        
        
        
        
    

    

关联查询方式二:

  
        
        
        
        
        
    
    
    
        
        
        
    

关联查询方式三:

 
        
        
        
        
        
            
            
            
        
    

collection 

collection 集合,如果pojo对象有一个属性是集合类型的,可以使用collection 来进行查询:

public class Person implements Serializable {

    private int id;

    private String name;

    private String job;

    private int age;

    private List
addressList; }
  
        
        
        
        
        
            
            
            
        
    

当然还有其他的方法,具体可以参考官网。

discriminator

鉴别器,mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为,有点像 Java的 switch 语句,鉴别器指定了 column 和 javaType 属性。 列是 MyBatis 查找比较值的地方。 JavaType 是需要被用来保证等价测试的合适类型,

比如某列的值等于多少,则返回1,等于多少返回2等等。


  
  
  
  
  
  
  
    
    
    
    
  

以上就是 resultMap 节点的全部使用方法,下面是一个比较复杂的例子,源码解析会按照其来解析,例子来自于官方文档。


  
    
    
  

  
  

  
    
    
  

  
    
    
  
  
  
    
  

resultMap 源码解析

首先需要说明的是,一个 resultMap 节点会解析成一个 ResultMap 对象,而每个子节点(除了discriminator节点)会被解析成 ResultMapping 对象,即一个 ResultMap 包含的是 ResultMapping 对象的集合。

先来看看 ResultMapping 的一个声明:

public class ResultMapping {
  // configuration 对象
  private Configuration configuration;
  private String property;
  private String column;
  private Class javaType;
  private JdbcType jdbcType;
  private TypeHandler typeHandler;
  // 对应的是 resultMap 属性,通过id来引用其他的resultMap
  private String nestedResultMapId;
  // 对应的是 select 属性,通过id来引用其他的select节点的定义
  private String nestedQueryId;
  private Set notNullColumns;
  private String columnPrefix;
  // 处理后的标志,标志有两个 id和constructor
  private List flags;
  // 对应节点的column属性拆分后生成的结果,composites.size()>0会使column为null
  private List composites;
  private String resultSet;
  private String foreignColumn;
  private boolean lazy;
|

ResultMap 的声明如下:

public class ResultMap {
  // ID,表示一个resultMap
  private String id;
  // 该resultMap对应的Javabean类型
  private Class type;
  // 对应的是除了discriminator节点外的其他节点
  private List resultMappings;
  // id 节点的映射集合
  private List idResultMappings;
  // 构造节点的集合
  private List constructorResultMappings;
  // 记录了映射关系中 不带有contructot节点的的映射关系
  private List propertyResultMappings;
  // column集合
  private Set mappedColumns;
  // discriminator 节点
  private Discriminator discriminator;
  private boolean hasNestedResultMaps;
  private boolean hasNestedQueries;
  private Boolean autoMapping;
}

解析:

resultMapElements(context.evalNodes("/mapper/resultMap"));

  private void resultMapElements(List list) throws Exception {
    for (XNode resultMapNode : list) {
      try {
        // 解析每个 resultMap 节点
        resultMapElement(resultMapNode);
      } catch (IncompleteElementException e) {
        // ignore, it will be retried
      }
    }
  }

  private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
    // 注意这里传入的是一个空的集合
    return resultMapElement(resultMapNode, Collections. emptyList());
  }

主要的解析方法:

private ResultMap resultMapElement(XNode resultMapNode, List additionalResultMappings) throws Exception {
    ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
    // ID 属性
    String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier());
    // type属性
    String type = resultMapNode.getStringAttribute("type",resultMapNode.getStringAttribute("ofType",        resultMapNode.getStringAttribute("resultType",resultMapNode.getStringAttribute("javaType"))));
    // extends 属性
    String extend = resultMapNode.getStringAttribute("extends");
    // autoMapping 属性
    Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
    // 从注册的类型管理器里面查找对应的类型
    Class typeClass = resolveClass(type);
    // discriminator 节点
    Discriminator discriminator = null;
    List resultMappings = new ArrayList();
    resultMappings.addAll(additionalResultMappings);
    // 处理子节点
    List resultChildren = resultMapNode.getChildren();
    for (XNode resultChild : resultChildren) {
      if ("constructor".equals(resultChild.getName())) {
        // 处理 constructor 节点
        processConstructorElement(resultChild, typeClass, resultMappings);
      } else if ("discriminator".equals(resultChild.getName())) {
        // 处理discriminator节点
        discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
      } else {
        List flags = new ArrayList();
        if ("id".equals(resultChild.getName())) {
          flags.add(ResultFlag.ID);
        }
        // 处理其他节点,创建 resultMapping 对象并添加到集合中
        resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
      }
    }
    ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
    try {
      // 创建代表该 resultMap 节点的 ResultMap 对象并添加到 ResultMap 集合中。
      return resultMapResolver.resolve();
    } catch (IncompleteElementException  e) {
      // 解析失败,添加到集合,重新解析
      configuration.addIncompleteResultMap(resultMapResolver);
      throw e;
    }
  }

处理 constructor 节点:

private void processConstructorElement(XNode resultChild, Class resultType, List resultMappings) throws Exception {
    List argChildren = resultChild.getChildren();
    for (XNode argChild : argChildren) {
      List flags = new ArrayList();
      // 向集合中添加 contrucator 标志
      flags.add(ResultFlag.CONSTRUCTOR);
      if ("idArg".equals(argChild.getName())) {
        // 添加id标志
        flags.add(ResultFlag.ID);
      }
      // 创建 ResultMapping 对象并添加到集合中
      resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags));
    }
  }

创建 ResultMapping 对象:

  private ResultMapping buildResultMappingFromContext(XNode context, Class resultType, List flags) throws Exception {
    // 解析节点的属性
    String property = context.getStringAttribute("property");
    String column = context.getStringAttribute("column");
    String javaType = context.getStringAttribute("javaType");
    String jdbcType = context.getStringAttribute("jdbcType");
    String nestedSelect = context.getStringAttribute("select");
    String nestedResultMap = context.getStringAttribute("resultMap",
        processNestedResultMappings(context, Collections. emptyList()));
    String notNullColumn = context.getStringAttribute("notNullColumn");
    String columnPrefix = context.getStringAttribute("columnPrefix");
    String typeHandler = context.getStringAttribute("typeHandler");
    String resultSet = context.getStringAttribute("resultSet");
    String foreignColumn = context.getStringAttribute("foreignColumn");
    boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
    Class javaTypeClass = resolveClass(javaType);
    @SuppressWarnings("unchecked")
    // 对应的 typeHandler 类型
    Class> typeHandlerClass = (Class>) resolveClass(typeHandler);
    JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
    // 创建 ResultMapping 对象
    return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy);
  }

之后是创建 ResultMapped 对象并添加到集合中:

ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
// 调用的使用 builderAssistant 的 addResultMap 方法
return resultMapResolver.resolve();
 public ResultMap addResultMap(String id, Class type, String extend, Discriminator discriminator, List resultMappings, Boolean autoMapping) {
    // 为 id 加上 namespace即 namespace.id
    id = applyCurrentNamespace(id, false);
    extend = applyCurrentNamespace(extend, true);

    if (extend != null) {
      if (!configuration.hasResultMap(extend)) {
        throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
      }
       // 获取父级的resultMap
      ResultMap resultMap = configuration.getResultMap(extend);
      List extendedResultMappings = new ArrayList(resultMap.getResultMappings());
      // 因为上面添加过一次,现在要删除重复的
      extendedResultMappings.removeAll(resultMappings);
      // Remove parent constructor if this resultMap declares a constructor.
      boolean declaresConstructor = false;
      for (ResultMapping resultMapping : resultMappings) {
        if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
          declaresConstructor = true;
          break;
        }
      }
      if (declaresConstructor) {
        Iterator extendedResultMappingsIter = extendedResultMappings.iterator();
        while (extendedResultMappingsIter.hasNext()) {
          if (extendedResultMappingsIter.next().getFlags().contains(ResultFlag.CONSTRUCTOR)) {
            extendedResultMappingsIter.remove();
          }
        }
      }
      resultMappings.addAll(extendedResultMappings);
    }
    // 创建 resultMap 
    ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping)
        .discriminator(discriminator)
        .build();
    // 添加到集合
    configuration.addResultMap(resultMap);
    return resultMap;
  }

到这里,就把 resultMap 节点解析完毕了

你可能感兴趣的:(mybatis)