zl程序教程

您现在的位置是:首页 >  其他

当前栏目

Mybatis 强大的结果集映射器resultMap

2023-02-26 09:51:08 时间
  1. 前言

resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的数千行代码。ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

resultMap 可以将查询到的复杂数据,比如多张表的数据、一对一映射、一对多映射等复杂关系聚合到一个结果集当中。日常的业务开发通常都会和它打交道,今天就对 resultMap 进行一个详细讲解。

(福利推荐:阿里云、腾讯云、华为云服务器最新限时优惠活动,云服务器1核2G仅88元/年、2核4G仅698元/3年,点击这里立即抢购>>>

  1. 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 为:

<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>

复制代码
我们来解释这些配置的属性:

<resultMap id="本namespace下唯一" type="对应映射的实体">     <id column="数据库主键字段名或者别名,使用它提高整体性能" property="对应实体属性"/>     <result column="数据库字段名或者别名" property="对应实体属性"/> </resultMap>

复制代码
以上方式是通过 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 可以这样写:

<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>

复制代码
细心的同学发现这里并没有 property 属性,其实当你不声明property 属性时会按照构造方法的参数列表顺序进行注入。

在 Mybatis 3.4.3 引入了 name 属性后我们就可以打乱 constructor 标签内的 arg 元素的顺序了。

<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>

复制代码
2.3 继承关系
像 Java 中的类一样,resultMap 也是可以继承的。下面是两个有继承关系的 Java 类:

那么 RegularEmployee 的 resultMap 就可以这么写:

<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>

复制代码
跟 Java 的继承关键字一样使用 extends 来进行继承。

2.4 一对一关联
明眼人会看出来 2.3 最后一个 resultMap 示例中有一个 association 标签。富贵论坛这个用来做什么用呢?打个比方,每一个正式员工 RegularEmployee会对应一个部门 Department,业务中会有把这种 一对一 关系查询出来的需求。所以 association 就派上了用场。

<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>

复制代码
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>

复制代码
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>

复制代码
切记一定是先声明 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 并把该条数据放进去,每次都会新建一个 List ;当employee_type=0 时也一样。这样的话最终就会返回一个 List 。

  1. 总结

resultMap 能够满足大部分业务场景对于数据映射的需求,今天我们对 Mybatis 中 resultMap 的一些用法进行了讲解,其实 resultMap 还有一些有用的属性,基于篇幅的原因这里不再讲解,可阅读 Mybatis 官方文档。但是请注意虽然 resultMap 功能强大,一定要合理使用,级联过于复杂会影响后期维护和性能。比如当一对多映射时,多的一方如果数据条数过大,会增加内存消耗和读写性能。

Mybatis 强大的结果集映射器resultMap


本站部分内容转载自网络,版权属于原作者所有,如有异议请联系QQ153890879修改或删除,谢谢!
转载请注明原文链接:Mybatis 强大的结果集映射器resultMap

你还在原价购买阿里云、腾讯云、华为云、天翼云产品?那就亏大啦!现在申请成为四大品牌云厂商VIP用户,可以3折优惠价购买云服务器等云产品,并且可享四大云服务商产品终身VIP优惠价,还等什么?赶紧点击下面对应链接免费申请VIP客户吧:

1、点击这里立即申请成为腾讯云VIP客户

2、点击这里立即注册成为天翼云VIP客户

3、点击这里立即申请成为华为云VIP客户

4、点击这里立享阿里云产品终身VIP优惠价

喜欢 (0)
[[email protected]]
分享 (0)