MyBatis 源码学习13——ResultMap
一、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相关推荐
- 【博学谷学习记录】超强总结,用心分享 | 架构师 Mybatis源码学习总结
Mybatis源码学习 文章目录 Mybatis源码学习 一.Mybatis架构设计 二.源码剖析 1.如何解析的全局配置文件 解析配置文件源码流程 2.如何解析的映射配置文件 Select inse ...
- Mybatis源码学习-动态代理
Mybatis源码学习-动态代理 binding包下面是mybatis的mapper动态代理 // Mybatis官方手册建议通过mapper对象访问mybatis,因为使用mapper看起来更优雅 ...
- 【Mybatis源码学习】概述
[Mybatis源码学习]概述 1.怎样下载源码 1.1 下载地址 1.2 导入Idea 1.2.1 环境 1.2.2 部署与打包 2.源码架构 2.1 核心流程三大阶段 2.1.1 初始化 2.1. ...
- Mybatis源码学习(三)SqlSession详解
前言 上一章节我们学习了SqlSessionFactory的源码,SqlSessionFactory中的方法都是围绕着SqlSession来的.,那么SqlSession又是什么东东呢?这一章节我们就 ...
- MyBatis源码学习笔记(从设计模式看源码)
文章目录 1.源码分析概述 ①.Mybatis架构分析 ②.门面模式 ③.设计模式的原则 2.日志模块分析 ①.适配器模型 ②.动态代理 ③.日志模块分析 3.数据源模块分析 ①.工厂模式 ②.数据源 ...
- mybatis源码学习1--学习源码的目的
在开始分析mybatis源码之前,需要定一个目标,也就是我们不是为了读源码而去读,一定是带着问题去读,在读的时候去寻找到答案,然后再读码的同时整理总结,学习一些高级的编码方式和技巧. 首先我们知道my ...
- Mybatis源码学习笔记之Mybatis二级缓存
简介 Mybatis一级缓存是会话级的缓存,而二级缓存则是应用级别的缓存,默认关闭,二级缓存使用不慎可能会导致脏读. 开启方式(SpringBoot+Mybatis) application. ...
- MyBatis源码:原来 resultMap 解析完是这样
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:阿进的写字台 www.cnblogs.com/homejim ...
- mybatis源码学习篇之——执行流程分析
前言 在正式学习mybatis框架源码之前,需要先弄懂几个问题?myabtis框架是什么?为什么需要mybatis框架?使用mybatis框架带来的好处是什么? 回答这几个问题之前,我们先来看一下,之 ...
最新文章
- 深圳餐厅使用iPad点餐(图)
- 如何造出逼真图像?南洋理工Zheng博士论文《基于深度生成学习的逼真图像合成》197页pdf阐述视觉合成工作...
- Skype的收购为微软带来了什么
- 基于Springboot实现就业管理系统
- 3.JAVA基础复习——JAVA中的类与对象
- python网络编辑 socket篇
- tomcat 8安装 linux,linux下tomcat8安装详解(附图解步骤)
- 设置Visual Studio代码折叠
- 【搞定Go语言】第3天11:Gin框架介绍及使用
- 随身的娱乐!PPC游戏模拟器详细介绍[转贴]
- 阿里云服务器ECS购买教程
- C#——signalr实现简单的网页实时聊天
- 网络摄像机中的IR-CUT详解
- 百度直接搜IP可以查看本机外网IP
- globaldata的用法
- 华南农业大学创新创业
- MT6589开机启动简析
- 海康、大华IpCamera摄像机 RTSP地址和格式
- 造梦西游4显示您与服务器,造梦西游4连接服务器失败怎么办?连接服务器失败解决方法分享...
- 新手小白DEV C++的使用方法
热门文章
- Microsoft Word文档续表设置方法,word跨页表格拆分方法
- Dell Precision T5600 双系统安装
- 《朗读者》:在历史的重负里前行
- Android小米5安装包解析失败问题
- 深度学习引发的思考和三巨头时代
- Linux S_ISUID/ S_ISGID
- java最全基础③进阶
- jQuery——自定义浏览器滚动条,改变滚动条样式。实现滚动条效果
- c语言消隐算法实现,计算机图形学试卷.doc
- 复制目录及目录下的所有文件的批处理_国庆放假你加班?1分钟新建1000个文件夹,有此技能保证不加班...