专栏首页关忆北.从Mapper到JavaBean源码层面解析ResultMap是怎么映射的

从Mapper到JavaBean源码层面解析ResultMap是怎么映射的

起点:

源码下载:

本文以3.3.x分支版本源码为例。

在源码工程的test中以NestedQueryCacheTest测试类下的testThatNestedQueryItemsAreRetrievedFromCache为例:

该方法中调的Mapper为:

<resultMap id="selectAuthor" type="org.apache.ibatis.domain.blog.Author">
  <id column="id" property="id" />
  <result property="username" column="username" />
  <result property="password" column="password" />
  <result property="email" column="email" />
  <result property="bio" column="bio" />
  <result property="favouriteSection" column="favourite_section" />
</resultMap>

<select id="selectAuthor" resultMap="selectAuthor">
  select id, username, password, email, bio, favourite_section
  from author where id = #{id}
</select>

resultMap工作原理

MyBatis中的标签主要用于返回javaType列和自定义列以及配合、标签实现一对一、一对多查询映射关系。MyBatis通过ResultMapElement类实现对的解析,解析过程中实质上是构造ResultMapping对象,记录结果集中某一列与JavaBean中一个属性的对应关系

启动测试,在XMLMapperBuilder第108行打断点,通过XNode类型的context参数可获取到目标Mapper的包路径及名称,然后缓存下来,这样就保证了在多次访问时,Mapper文件我们只需要加载一次。

private void configurationElement(XNode context) {
  try {
    String namespace = context.getStringAttribute("namespace");
    if (namespace == null || namespace.equals("")) {
      throw new BuilderException("Mapper's namespace cannot be empty");
    }
    builderAssistant.setCurrentNamespace(namespace);
    cacheRefElement(context.evalNode("cache-ref"));
    cacheElement(context.evalNode("cache"));
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    sqlElement(context.evalNodes("/mapper/sql"));
    buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
  }
}

ResultMapElement中,resultMapElements中定义了resulteMap xml文件的解析方法。在该方法中,入参是XNode类型的List列表。xml是一种数据展现和存储的方式,为获取xml中的数据,我们需要Java-XML中间做一层转化,XNode就是MyBatis定义解析XML节点中属性和对应值的工具。

private void resultMapElements(List<XNode> list) throws Exception {
  for (XNode resultMapNode : list) {
    try {
      resultMapElement(resultMapNode);
    } catch (IncompleteElementException e) {
      // ignore, it will be retried
    }
  }
}

XMLMapperBuilder第242行断点,查看List的值:

可见,MyBatis已经通过XNode获取到了xml文件中的resultMap编写的代码,接下来就是要在for循环中解析resultMap中每一行的数据。

解析resultMap每一行的映射关系

第一步获取id,默认拼装所有父节点的Id或property(唯一标识)

第二步,获取类型

type是resultMap标签的,ofType是collection标签的,resultType是select、insert、update、delete标签的。标签允许多个type,优先级为: 【type】>【ofType】>【resultType】>【javaType】

第三步,判断该resultMap是否继承自其它resultMap,是否开启了自动映射配置。

autoMapping:自动映射:自动根据大小写实现SQL column <—>JavaBean(POJO) field转换

第四步,根据type找到对应类

第五步,将从XNode中获取到的全部节点,将每一行数据都放入到List中,经buildResultMappingFromContext()完成映射。

private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> 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");
  //判断该标签中是否嵌套ResultMap
  String nestedResultMap = context.getStringAttribute("resultMap",
      processNestedResultMappings(context, Collections.<ResultMapping> emptyList()));
  String notNullColumn = context.getStringAttribute("notNullColumn");
  String columnPrefix = context.getStringAttribute("columnPrefix");
  String typeHandler = context.getStringAttribute("typeHandler");
  String resulSet = 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")
  Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
  JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
  //返回构造参数映射
  return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resulSet, foreignColumn, lazy);
}

构造参数映射(普通对象)

构造参数映射(嵌套对象)

private String processNestedResultMappings(XNode context, List<ResultMapping> resultMappings) throws Exception {
  if ("association".equals(context.getName())
      || "collection".equals(context.getName())
      || "case".equals(context.getName())) {
    if (context.getStringAttribute("select") == null) {
      //递归调用解析元素方法
      ResultMap resultMap = resultMapElement(context, resultMappings);
      return resultMap.getId();
    }
  }
  return null;
}

当解析到的Mapper中的方法中嵌套了另一个、的resultMap时,那么会递归调用映射方法,并将返回值做为本Map的一个参数返回。

从上面代码上能看得出成为嵌套ResultMap的规则为:association,collection,case标签,且不包含select属性.

本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!
本文分享自作者个人站点/博客:https://blog.csdn.net/weixin_42313773复制
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • Mybatis源码之映射器解析

    映射器是由Java接口和XML文件(或注解)共同组成的,Java接口主要定义调用者接口,XML文件是配置映射器的核心文件,包括以下元素:

    Java宝典
  • Mybatis【配置文件】

    映射文件 配置文件和映射文件还有挺多的属性我还没有讲的,现在就把它们一一补全 在mapper.xml文件中配置很多的sql语句,执行每个sql语句时,封装为Ma...

    Java3y
  • mybatis的resultMap完美解析(含github实例)!!!

    在 select 语句中查询得到的是一张二维表, 水平方向上看是一个个字段, 垂直方向上看是一条条记录。作为面向对象的语言, Java 中的的对象是根据类定义创...

    用户5224393
  • Mybatis Mapper.xml 配置文件中 resultMap 节点的源码解析

    在上篇文章 Mybatis 解析 SQL 源码分析一 介绍了 Maper.xml 配置文件的解析,但是没有解析 resultMap 节点,因为该解析比较复杂,也...

    Java技术编程
  • MyBatis标签详解

    MyBatis 真正的核心在映射文件中。比直接使用 JDBC 节省95%的代码。而且将 SQL 语句独立在 Java 代码之外,可以进行更为细致的 SQL 优化...

    全栈程序员站长
  • 从源码角度分析 MyBatis 工作原理

    如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:

    2020labs小助手
  • MyBatis —— 参数处理

    指定参数名,多个参数还是封装一个map,但是此时key 使用的是@Param注解指定的值通过#{指定的key} 从map中获取指定的参数值

    桑鱼
  • 从源码角度分析 MyBatis 工作原理

    如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:

    冬夜先生
  • MyBatis 从浅入深 随笔整理

    (1)每个MyBatis的哟ing有都以一个SqlSessionFactory对象的实例为核心

    房上的猫
  • MyBatis框架及原理分析

    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://myba...

    java架构师
  • 硬菜要软着吃?不如试试这篇【MyBatis】

    大家好,我是小菜,一个渴望在互联网行业做到蔡不菜的小菜。可柔可刚,点赞则柔,白嫖则刚!「死鬼~看完记得给我来个三连哦!」

    蔡不菜丶
  • MyBatis 源码分析 - 映射文件解析过程

    在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程。由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因。所以我将映射文件解析过程的分...

    田小波
  • Mybatis_day01

    Mybatis_day01 前言 Jdbc演变到mybatis jdbc jdbc编程 publicstaticvoid main(String[] args)...

    Java帮帮
  • 7. MyBatis多表查询 - 一对一 - 一对多 - 多对多

    在上面我们已经准备好了 sql 表结构,在 sql 中具有 一对一、一对多、多对多 三种关系。而如果在 mybatis 的角度来看,却只有 一对一、一对多 两种...

    Devops海洋的渔夫
  • Mybatis 文档(一)

    既然有了 SqlSessionFactory,顾名思义,我们就可以从中获得 SqlSession 的实例了。SqlSession 完全包含了面向数据库执行 SQ...

    Remember_Ray
  • 《Mybatis 手撸专栏》第11章:流程解耦,封装结果集处理器

    上班就像打怪升级,拿着一把西瓜刀,从南天门砍到北天门。但时间长了,怪越来越凶了,西瓜刀也不得手了。咋办,在游戏里大家肯定是想办法换装备了、买武器了、学技能了,这...

    小傅哥
  • Mybatis_day02

    Mybatis第二天 课程安排 对订单商品数据模型进行分析 高级映射: 实现一对一、一对多,多对多查询 延迟加载 查询缓存 一级缓存 二级缓存(了解mybati...

    Java帮帮
  • Java Mybatis基础知识总结

    对象关系映射(Object Relational Mapping,简称ORM)是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。 简单的说,ORM是...

    赵哥窟
  • Mybatis 实战:一对多关系

    公司有若干名员工,此时,该公司与其员工之间的关系就属于一对多的关系。根据以上信息,我们可以创建公司信息与员工信息两张表。其中,公司表沿用上一个例子中的表。根据公...

    耕耘实录

扫码关注腾讯云开发者

领取腾讯云代金券