Mybatis 强大的结果集映射器resultMap

1. 前言
resultMap元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBCResultSets数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份resultMap能够代替实现同等功能的数千行代码。ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
resultMap 可以将查询到的复杂数据,比如多张表的数据、一对一映射、一对多映射等复杂关系聚合到一个结果集当中。日常的业务开发通常都会和它打交道,今天就对 resultMap 进行一个详细讲解。
2. resultMap
接下来我们来看看 resultMap 是如何进行映射的。
2.1 Getter/Setter 注入
我们声明一个数据库对应的实体类:
/**
* @author felord.cn
* @since 16:50
**/
@Data
public class Employee implements Serializable {
private static final long serialVersionUID = -7145891282327539285L;
private String employeeId;
private String employeeName;
private Integer employeeType;
}
那么它对应的 resultMap 为:
<mapper namespace="cn.felord.mybatis.mapper.EmployeeMapper">
<resultMap id="EmployeeMap" type="cn.felord.mybatis.entity.Employee">
<id column="employee_id" property="employeeId"/>
<result column="employee_name" property="employeeName"/>
<result column="employee_type" property="employeeType"/>
</resultMap>
</mapper>
我们来解释这些配置的属性:
<mapper namespace="全局唯一的名称空间">
<resultMap id="本namespace下唯一" type="对应映射的实体">
<id column="数据库主键字段名或者别名,使用它提高整体性能" property="对应实体属性"/>
<result column="数据库字段名或者别名" property="对应实体属性"/>
</resultMap>
</mapper>
以上方式是通过 Getter 和 Setter 方法进行注入,也就是实体类必须有无参构造,对应属性必须有Getter 和 Setter 方法。
2.2 构造注入
Getter 和 Setter 方法进行注入是我们最常用的方式。但是 Mybatis 同样支持构造注入,如果 Employee 存在如下构造方法:
public Employee(String employeeId, String employeeName, Integer employeeType) {
this.employeeId = employeeId;
this.employeeName = employeeName;
this.employeeType = employeeType;
}
那么对应的 resultMap 可以这样写:
<mapper namespace="cn.felord.mybatis.mapper.EmployeeMapper">
<resultMap id="EmployeeMap" type="cn.felord.mybatis.entity.Employee">
<constructor>
<idArg column="employee_id" javaType="String"/>
<arg column="employee_name" javaType="String"/>
<arg column="employee_type" javaType="String"/>
</constructor>
</resultMap>
</mapper>
细心的同学发现这里并没有 property 属性,其实当你不声明property 属性时会按照构造方法的参数列表顺序进行注入。
在 Mybatis 3.4.3 引入了 name 属性后我们就可以打乱 constructor 标签内的 arg 元素的顺序了。
<mapper namespace="cn.felord.mybatis.mapper.EmployeeMapper">
<resultMap id="EmployeeConstructorMap" type="cn.felord.mybatis.entity.Employee">
<constructor>
<idArg column="employee_id" javaType="String" name="employeeId"/>
<!-- 你可以不按参数列表顺序添加-->
<arg column="employee_type" javaType="Integer" name="employeeType"/>
<arg column="employee_name" javaType="String" name="employeeName"/>
</constructor>
</resultMap>
</mapper>
2.3 继承关系
像 Java 中的类一样,resultMap 也是可以继承的。下面是两个有继承关系的 Java 类:

那么 RegularEmployee 的 resultMap 就可以这么写:
<resultMap id="RegularEmployeeMap" extends="EmployeeMap" type="cn.felord.mybatis.entity.RegularEmployee">
<result column="level" property="level"/>
<result column="job_number" property="jobNumber"/>
<association property="department" javaType="cn.felord.mybatis.entity.Department">
<id column="department_id" property="departmentId"/>
<result column="department_name" property="departmentName"/>
<result column="department_level" property="departmentLevel"/>
</association>
</resultMap>
跟 Java 的继承关键字一样使用 extends 来进行继承。
2.4 一对一关联
明眼人会看出来 2.3 最后一个 resultMap 示例中有一个 association 标签。这个用来做什么用呢?打个比方,每一个正式员工 RegularEmployee会对应一个部门 Department,业务中会有把这种 一对一 关系查询出来的需求。所以 association 就派上了用场。
<resultMap id="RegularEmployeeMap" extends="EmployeeMap" type="cn.felord.mybatis.entity.RegularEmployee">
<result column="level" property="level"/>
<result column="job_number" property="jobNumber"/>
<association property="属性名称" javaType="对应的Java类型">
<id column="department_id" property="departmentId"/>
<result column="department_name" property="departmentName"/>
<result column="department_level" property="departmentLevel"/>
</association>
</resultMap>
association可以继续嵌套下去,有可能关联的对象中还有一对一关系。
2.5 一对多关联
有一对一关联,自然会有一对多关联。我们反客为主,一个部门有多个员工,我们可能需要查询一个部门的信息以及所有员工的信息装载到 DepartmentAndEmployeeList中去。
/**
* @author felord.cn
* @since 15:33
**/
public class DepartmentAndEmployeeList extends Department {
private static final long serialVersionUID = -2503893191396554581L;
private List<Employee> employees;
public List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
}
我们可以在 resultMap 中使用 collection 关键字来处理一对多映射关系:
<resultMap id="DepartmentAndEmployeeListMap" extends="DepartmentMap"
type="cn.felord.mybatis.entity.DepartmentAndEmployeeList">
<collection property="employees" ofType="cn.felord.mybatis.entity.RegularEmployee">
<id column="employee_id" property="employeeId"/>
<result column="employee_name" property="employeeName"/>
<result column="level" property="level"/>
<result column="job_number" property="jobNumber"/>
</collection>
</resultMap>
2.6 鉴别器
大家都知道,员工并不都是正式工,还有临时工。有时候我们也期望能够将这两种区分开来,至于原因你懂的。不深入讨论这个问题了。就这个需求而言我们的映射关系又复杂了,我们需要根据某个条件来判断哪条数据是正式工,哪条数据是临时工,然后分别装入下面这个实体类的 regularEmployees、temporaryEmployees中。
/**
* @author felord.cn
* @since 15:33
**/
public class DepartmentAndTypeEmployees extends Department {
private static final long serialVersionUID = -2503893191396554581L;
private List<RegularEmployee> regularEmployees;
private List<TemporaryEmployee> temporaryEmployees;
// getter setter
}
鉴别器(discriminator)元素就是被设计来应对这种情况的,另外也能处理其它情况,例如类的继承层次结构。 鉴别器的概念很好理解——它很像 Java 语言中的 switch 语句。
为此我们需要在 Employee 类中增加一个 int类型的 employeeType属性来区分正式工和临时工,其中 1代表正式工,而 0代表临时工。然后我们来编写查询 DepartmentAndTypeEmployees 的 resultMap :
<resultMap id="DepartmentAndTypeEmployeesMap" extends="DepartmentMap"
type="cn.felord.mybatis.entity.DepartmentAndTypeEmployees">
<collection property="regularEmployees" ofType="cn.felord.mybatis.entity.RegularEmployee">
<discriminator javaType="int" column="employee_type">
<case value="1">
<id column="employee_id" property="employeeId"/>
<result column="employee_name" property="employeeName"/>
<result column="employee_type" property="employeeType"/>
<result column="level" property="level"/>
<result column="job_number" property="jobNumber"/>
</case>
</discriminator>
</collection>
<collection property="temporaryEmployees" ofType="cn.felord.mybatis.entity.TemporaryEmployee">
<discriminator javaType="int" column="employee_type">
<case value="0">
<id column="employee_id" property="employeeId"/>
<result column="employee_name" property="employeeName"/>
<result column="employee_type" property="employeeType"/>
<result column="company_no" property="companyNo"/>
</case>
</discriminator>
</collection>
</resultMap>
切记一定是先声明 DepartmentAndTypeEmployees的两个 List ,然后在 collection 标签内部使用 discriminator 标签。
这里很容易犯以下错误,下面的写法虽然可以查询出数据但是满足不了上述需求:
<resultMap id="DepartmentAndTypeEmployeesMap" extends="DepartmentMap"
type="cn.felord.mybatis.entity.DepartmentAndTypeEmployees">
<discriminator javaType="int" column="employee_type">
<case value="1">
<collection property="regularEmployees" ofType="cn.felord.mybatis.entity.RegularEmployee">
<!--省略-->
</collection>
</case>
<case value="0">
<collection property="temporaryEmployees" ofType="cn.felord.mybatis.entity.TemporaryEmployee">
<!--省略-->
</collection>
</case>
</discriminator>
</resultMap>
这种写法的意思是:当发现该条数据中 employee_type=1 时,就新建一个 List<RegularEmployee> 并把该条数据放进去,每次都会新建一个 List<RegularEmployee> ;当employee_type=0 时也一样。这样的话最终就会返回一个 List<DepartmentAndTypeEmployees> 。
3. 总结
resultMap 能够满足大部分业务场景对于数据映射的需求,今天我们对 Mybatis 中 resultMap 的一些用法进行了讲解,其实 resultMap 还有一些有用的属性,基于篇幅的原因这里不再讲解,可阅读 Mybatis 官方文档。但是请注意虽然 resultMap 功能强大,一定要合理使用,级联过于复杂会影响后期维护和性能。比如当一对多映射时,多的一方如果数据条数过大,会增加内存消耗和读写性能。希望今天的文章对你使用 resultMap 有所帮助,更及时的技术资讯请多多关注:码农小胖哥。
本次文章的 DEMO ,可关注公众号:Felordcn 回复 resultMap 获取。
关注公众号:Felordcn 获取更多资讯
Mybatis 强大的结果集映射器resultMap的更多相关文章
- Java-MyBatis:MyBatis 3 | SQL 语句构建器类
ylbtech-Java-MyBatis:MyBatis 3 | SQL 语句构建器类 1.返回顶部 1. SQL语句构建器类 问题 Java程序员面对的最痛苦的事情之一就是在Java代码中嵌入SQL ...
- MyBatis标签之Select resultType和resultMap
摘要:介绍MyBatis 中Select标签的两个属性resultType和resultMap及其区别. 1 MyBatis动态SQL之if 语句 2 MyBatis动态sql之where标签|转 3 ...
- MyBatis基础入门《九》ResultMap自动匹配
MyBatis基础入门<九>ResultMap自动匹配 描述: Mybatis执行select查询后,使用ResultMap接收查询的数据结果. 实体类:TblClient.java 接口 ...
- jQuery功能强大的图片查看器插件
简要教程 viewer是一款功能强大的图片查看器jQuery插件.它可以实现ACDsee等看图软件的部分功能.它可以对图片进行移动,缩放,旋转,翻转,可以前后浏览一组图片.该图片查看器还支持移动设备, ...
- 分享一款强大的图片查看器插件,手机PC 通吃,功能超级齐全!
一款强大的图片查看器插件,手机PC 通吃,功能超级齐全! 地址:http://photoswipe.com/
- Mybatis中输出映射resultType与resultMap的区别
Mybatis中输出映射resultType与resultMap的区别 (原文地址:http://blog.csdn.net/acmman/article/details/46509375) 一.re ...
- (一) Mybatis源码分析-解析器模块
Mybatis源码分析-解析器模块 原创-转载请说明出处 1. 解析器模块的作用 对XPath进行封装,为mybatis-config.xml配置文件以及映射文件提供支持 为处理动态 SQL 语句中的 ...
- 开源基于docker的任务调度器pipeline,比`quartzs` 更强大的分布式任务调度器
pipeline 分布式任务调度器 目标: 基于docker的布式任务调度器, 比quartzs,xxl-job 更强大的分布式任务调度器. 可以将要执行的任务打包为docker镜像,或者选择已有镜像 ...
- MyBatis 强大之处 多环境 多数据源 ResultMap 的设计思想是 缓存算法 跨数据库 spring boot rest api mybaits limit 传参
总结: 1.mybaits配置工2方面: i行为配置,如数据源的实现是否利用池pool的概念(POOLED – This implementation of DataSource pools JDBC ...
随机推荐
- 解决Vue-cli3.0下scss文件编译过慢、卡顿问题
在使用Vue-cli 3.0构建的项目中,可能存在项目编译过慢的问题,具体表现在编译时会在某一进度比如40%时停顿,等好一会儿才能够编译完成.这使得浏览器中的实时预览也会卡顿,不利于我们快速查看效果, ...
- 《Cisco防火墙》一2.4 总结
本节书摘来自异步社区<Cisco防火墙>一书中的第2章,第2.4节,作者 [巴西]Alexandre M.S.P. Moraes,更多章节内容可以访问云栖社区"异步社区" ...
- 基于opencv的人脸识别程序
1. 解析opencv自带人脸识别源码(……/opencv-3.1.0/samples/cpp/facedetect.cpp) @ 操作系统:Ubuntu 15.04 OpenCV版本:3.1.0 # ...
- CentOS 7 编译错误解决方法集合
解决 error: the HTTP XSLT module requires the libxml2/libxslt 错误 yum -y install libxml2 libxml2-dev yu ...
- Codeforces Round #530 (Div. 1) 1098A Sum in the tree
A. Sum in the tree Mitya has a rooted tree with nn vertices indexed from 11 to nn, where the root ha ...
- 使用Python+TensorFlow2构建基于卷积神经网络(CNN)的ECG心电信号识别分类(二)
心律失常数据库 目前,国际上公认的标准数据库包含四个,分别为美国麻省理工学院提供的MIT-BIH(Massachusetts Institute of Technology-Beth Israel H ...
- 带"反悔"的贪心-超市
题面:https://www.acwing.com/problem/content/description/147/ 超市里有N件商品,每个商品都有利润pi和过期时间di,每天只能卖一件商品,过期商品 ...
- 推荐算法_CIKM-2019-AnalytiCup 冠军源码解读_2
最近在为机器学习结合推荐算法的优化方法和数据来源想办法.抱着学习的态度继续解读19-AnalytiCup的冠军源码. 第一部分itemcf解读的连接:https://www.cnblogs.com/m ...
- 【HBase】HBase与MapReduce的集成案例
目录 需求 步骤 一.创建maven工程,导入jar包 二.开发MapReduce程序 三.运行结果 HBase与MapReducer集成官方帮助文档:http://archive.cloudera. ...
- 从浅入深——理解JSONP的实现原理
由于浏览器的安全性限制,不允许AJAX访问 协议不同.域名不同.端口号不同的 数据接口,浏览器认为这种访问不安全: 可以通过动态创建script标签的形式,把script标签的src属性,指向数据接口 ...