一、ResultMap:

ResultMap:
保存Java实体属性与数据库表字段之间的映射关系,是实现级联映射和懒加载机制的基础。

MyBatis是一个半自动化的ORM框架,可以将数据库中的记录转换为Java实体对象,但是Java实体属性通常采用驼峰命名法,而数据库字段习惯采用下画线分割命名法,因此需要用户指定Java实体属性与数据库表字段之间的映射关系。

MyBatis的Mapper配置中提供了一个<resultMap>标签,用于建立数据库字段与Java实体属性之间的映射关系。

<resultMap>子标签:

<constructor>:该标签用于建立构造器映射。该标签有两个子标签,<idArg>标签用于配置主键映射,标记出主键,可以提高整体性能;<arg>标签用于配置普通字段的映射。

<id>:用于配置数据库主键映射,标记出数据库主键,有助于提高整体性能。

<result>:用于配置数据库字段与Java实体属性之间的映射关系。

<association>:用于配置一对一关联映射,可以关联一个外部的查询Mapper或者配置一个嵌套的ResultMap。

<collection>:用于配置一对多关联映射,可以关联一个外部的查询Mapper或者配置一个嵌套的ResultMap。

<discriminator>:用于配置根据字段值使用不同的ResultMap。该标签有一个子标签,<case>标签用于枚举字段值对应的ResultMap,类似于Java中的switch语法。

ResultMap解析过程:
MyBatis在启动时,所有配置信息都会被转换为Java对象,标签信息会转为ResultMap对象。

ResultMap类

• Id:通过<resultMap>标签的id属性和Mapper命名空间组成的全局唯一的Id。

• Type:通过<resultMap>标签的type属性指定与数据库表建立映射的Java实体。

• resultMappings:通过<result>标签配置的所有数据库字段与Java实体属性之间的映射信息。

• idResultMappings:通过<id>标签配置的数据库主键与Java实体属性的映射信息。需要注意的是,<id>标签与<result>标签没有本质的区别。
• constructorResultMappings:通过<constructor>标签配置的构造器映射信息。

• propertyResultMappings:通过<result>标签配置的数据库字段与Java实体属性的映射信息。

• mappedColumns:该属性存放所有映射的数据库字段。当使用columnPrefix属性配置了前缀时,MyBatis会对mappedColumns属性进行遍历,为所有数据库字段追加columnPrefix属性配置的前缀。

• mappedProperties:该属性存放所有映射的Java实体属性信息。

• discriminator:该属性为在<resultMap>标签中通过<discriminator>标签配置的鉴别器信息。

• hasNestedResultMaps:该属性用于标识是否有嵌套的ResultMap,当使用<association><collection>标签以JOIN查询方式配置一对一或一对多级联映射时,<association><collection>标签相当于一个嵌套的ResultMap,因此hasNestedResultMaps属性值为true。

• hasNestedQueries:该属性用于标识是否有嵌套的查询,当使用<association><collection>标签关联一个外部的查询Mapper建立一对一或一对多级联映射时,hasNestedQueries属性值为true。

• autoMapping:autoMapping属性为true,表示开启自动映射,即使未使用<result>或<id>标签配置映射字段,MyBatis也会自动对这些字段进行映射。

<resultMap>标签解析生成ResultMap对象的过程

MyBatis中的Mapper配置信息解析是通过XMLMapperBuilder类完成的,该类提供了一个parse()方法,用于解析Mapper中的所有配置信息:

在XMLMapperBuilder的parse()方法中,调用XMLMapperBuilder类的configurationElement()方法,然后configurationElement()方法又调用resultMapElements()方法对所有<resultMap>标签进行解析。

resultMapElements()方法最终会调用重载的resultMapElement()方法对每个<resultMap>标签进行解析:

1.首先获取<resultMap>标签的所有属性信息,
2.然后对<id>等子标签进行解析,通过buildResultMappingFromContext(),创建字段的ResultMapping对象
3.接着创建一个ResultMapResolver对象,
4.调用ResultMapResolver对象的resolve()方法返回一个ResultMap对象。

ResultMapResolver对象的resolve()方法调用了MapperBuilderAssistant的addResultMap()方法:

1.首先判断该ResultMap是否继承了其他ResultMap。如果是,则获取父ResultMap对象,然后去除父ResultMap中的构造器映射信息,将父ResultMap中配置的映射信息添加到当前ResultMap对象。

2.然后通过建造者模式在ResultMap.Builder类中创建一个ResultMap对象,然后为ResultMap对象的所有属性赋值。

3.把ResultMap对象添加到Configuration对象的属性resultMaps中,key为ResultMap对象的id,value为ResultMap对象。

二、映射实现原理:

StatementHandler组件与数据库完成交互后,会使用ResultSetHandler组件对结果集进行处理。
PreparedStatementHandler类的query()方法:

1.调用PreparedStatement对象的execute()方法完成与数据库交互,

2.调用ResultSetHandler对象的handleResultSets()方法对结果集进行处理。

ResultSetHandler接口只有一个默认的实现,即DefaultResultSetHandler类。

DefaultResultSetHandler类中handleResultSets()方法

1.为了简化对JDBC中ResultSet对象的操作,将ResultSet对象包装成ResultSetWrapper对象,

2.然后获取MappedStatement对象对应的ResultMap对象,接着调用重载的handleResultSet()方法,handleResultSet()方法中做了一些逻辑判断,但最终都会调用DefaultResultSetHandler类的handleRowValues()方法。

在DefaultResultSetHandler类的handleRowValues()方法中处理结果集时,对嵌套的ResultMap和非嵌套ResultMap做了不同处理:

方法中判断ResultMap中是否有嵌套的ResultMap,
当使用或标签通过JOIN查询方式进行级联映射时,hasNestedResultMaps()方法的返回值为true,调用handleRowValuesForNestedResultMap()方法;
当使用和标签关联一个外部的查询Mapper时,ResultMap对象的hasNestedResultMaps属性值为false,调用handleRowValuesForSimpleResultMap()方法。

有嵌套ResultMap时的处理逻辑,handleRowValuesForNestedResultMap()方法:

对结果集对象进行遍历,处理每一行数据。
首先调用resolveDiscriminatedResultMap()方法处理标签中通过标签配置的鉴别器信息,根据字段值获取对应的ResultMap对象,
然后调用DefaultResultSetHandler类的getRowValue()方法将结果集中的一行数据转换为Java实体对象。

在getRowValue()方法中:

1.调用createResultObject()方法处理通过<constructor>标签配置的构造器映射,根据配置信息找到对应的构造方法,然后通过MyBatis中的ObjectFactory创建ResultMap关联的实体对象。

2.调用applyAutomaticMappings()方法处理自动映射,对未通过<result>标签配置映射的数据库字段进行与Java实体属性的映射处理。
方法中首先获取未指定映射的所有数据库字段和对应的Java属性,然后获取对应的字段值,通过反射机制为Java实体对应的属性值赋值。

3.调用applyPropertyMappings()方法处理<result>标签配置的映射信息。对所有<result>标签配置的映射信息进行遍历,然后找到数据库字段对应的值,为Java实体属性赋值。applyPropertyMappings()方法的实现代码如下:

4.调用DefaultResultSetHandler类的applyNestedResultMappings()方法处理嵌套的结果集映射。applyNestedResultMappings()方法实现如下:

在applyNestedResultMappings()方法中:
1.首先获取嵌套ResultMap对象,
2.然后根据嵌套ResultMap的Id从缓存中获取嵌套ResultMap对应的Java实体对象,如果能获取到,则调用linkObjects()方法将嵌套Java实体与外部Java实体进行关联。
如果缓存中没有,则调用getRowValue()方法创建嵌套ResultMap对应的Java实体对象并进行属性映射,然后调用linkObjects()方法与外部的Java实体对象进行关联。

没有嵌套的ResultMap,handleRowValuesForSimpleResultMap()方法:

1.首先调用skipRows()方法跳过RowBounds对象指定偏移的行,

2.然后遍历结果集中所有的行,对标签配置的鉴别器进行处理,获取实际映射的ResultMap对象,

3.接着调用getRowValue()方法处理一行记录,将数据库行记录转换为Java实体对象。getRowValue()方法实现如下:

在getRowValue():
1.创建ResultLoaderMap对象,该对象用于存放懒加载的属性及对应的ResultLoader对象,MyBatis中的ResultLoader用于执行一个查询Mapper,然后将执行结果赋值给某个实体对象的属性。

2.调用createResultObject()方法创建ResultMap对应的Java实体对象,我们需要重点关注该方法的实现,代码如下:

createResultObject()方法中:

1.首先调用重载的createResultObject()方法使用ObjectFactory对象创建Java实体对象,

2.然后判断ResultMap中是否有嵌套的查询,如果有嵌套的查询并且开启了懒加载机制,则通过MyBatis中的ProxyFactory创建实体对象的代理对象。
ProxyFactory接口有两种不同的实现,分别为CglibProxyFactory和JavassistProxyFactory。
也就是说,MyBatis同时支持使用Cglib和Javassist创建代理对象,具体使用哪种策略创建代理对象,可以在MyBatis主配置文件中通过proxyFactory属性指定。

3.调用applyAutomaticMappings()方法处理自动映射,

4.调用applyPropertyMappings()方法处理标签配置的映射字段,该方法中除了为Java实体属性设置值外,还将指定了懒加载的属性添加到ResultLoaderMap对象中。

MyBatis 源码学习13——ResultMap相关推荐

  1. 【博学谷学习记录】超强总结,用心分享 | 架构师 Mybatis源码学习总结

    Mybatis源码学习 文章目录 Mybatis源码学习 一.Mybatis架构设计 二.源码剖析 1.如何解析的全局配置文件 解析配置文件源码流程 2.如何解析的映射配置文件 Select inse ...

  2. Mybatis源码学习-动态代理

    Mybatis源码学习-动态代理 binding包下面是mybatis的mapper动态代理 // Mybatis官方手册建议通过mapper对象访问mybatis,因为使用mapper看起来更优雅 ...

  3. 【Mybatis源码学习】概述

    [Mybatis源码学习]概述 1.怎样下载源码 1.1 下载地址 1.2 导入Idea 1.2.1 环境 1.2.2 部署与打包 2.源码架构 2.1 核心流程三大阶段 2.1.1 初始化 2.1. ...

  4. Mybatis源码学习(三)SqlSession详解

    前言 上一章节我们学习了SqlSessionFactory的源码,SqlSessionFactory中的方法都是围绕着SqlSession来的.,那么SqlSession又是什么东东呢?这一章节我们就 ...

  5. MyBatis源码学习笔记(从设计模式看源码)

    文章目录 1.源码分析概述 ①.Mybatis架构分析 ②.门面模式 ③.设计模式的原则 2.日志模块分析 ①.适配器模型 ②.动态代理 ③.日志模块分析 3.数据源模块分析 ①.工厂模式 ②.数据源 ...

  6. mybatis源码学习1--学习源码的目的

    在开始分析mybatis源码之前,需要定一个目标,也就是我们不是为了读源码而去读,一定是带着问题去读,在读的时候去寻找到答案,然后再读码的同时整理总结,学习一些高级的编码方式和技巧. 首先我们知道my ...

  7. Mybatis源码学习笔记之Mybatis二级缓存

    简介   Mybatis一级缓存是会话级的缓存,而二级缓存则是应用级别的缓存,默认关闭,二级缓存使用不慎可能会导致脏读. 开启方式(SpringBoot+Mybatis)   application. ...

  8. MyBatis源码:原来 resultMap 解析完是这样

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:阿进的写字台 www.cnblogs.com/homejim ...

  9. mybatis源码学习篇之——执行流程分析

    前言 在正式学习mybatis框架源码之前,需要先弄懂几个问题?myabtis框架是什么?为什么需要mybatis框架?使用mybatis框架带来的好处是什么? 回答这几个问题之前,我们先来看一下,之 ...

最新文章

  1. 深圳餐厅使用iPad点餐(图)
  2. 如何造出逼真图像?南洋理工Zheng博士论文《基于深度生成学习的逼真图像合成》197页pdf阐述视觉合成工作...
  3. Skype的收购为微软带来了什么
  4. 基于Springboot实现就业管理系统
  5. 3.JAVA基础复习——JAVA中的类与对象
  6. python网络编辑 socket篇
  7. tomcat 8安装 linux,linux下tomcat8安装详解(附图解步骤)
  8. 设置Visual Studio代码折叠
  9. 【搞定Go语言】第3天11:Gin框架介绍及使用
  10. 随身的娱乐!PPC游戏模拟器详细介绍[转贴]
  11. 阿里云服务器ECS购买教程
  12. C#——signalr实现简单的网页实时聊天
  13. 网络摄像机中的IR-CUT详解
  14. 百度直接搜IP可以查看本机外网IP
  15. globaldata的用法
  16. 华南农业大学创新创业
  17. MT6589开机启动简析
  18. 海康、大华IpCamera摄像机 RTSP地址和格式
  19. 造梦西游4显示您与服务器,造梦西游4连接服务器失败怎么办?连接服务器失败解决方法分享...
  20. 新手小白DEV C++的使用方法

热门文章

  1. Microsoft Word文档续表设置方法,word跨页表格拆分方法
  2. Dell Precision T5600 双系统安装
  3. 《朗读者》:在历史的重负里前行
  4. Android小米5安装包解析失败问题
  5. 深度学习引发的思考和三巨头时代
  6. Linux S_ISUID/ S_ISGID
  7. java最全基础③进阶
  8. jQuery——自定义浏览器滚动条,改变滚动条样式。实现滚动条效果
  9. c语言消隐算法实现,计算机图形学试卷.doc
  10. 复制目录及目录下的所有文件的批处理_国庆放假你加班?1分钟新建1000个文件夹,有此技能保证不加班...