4.结果映射

本章目标

  1. 结果映射
  2. 关联
  3. 集合

本章内容

一、结果映射

resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从90%的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些JDBC不支持的操作。

实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份resultMap 能够代替实现同等功能的数千行代码。ResultMap的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了

1、常用属性

  • constructor - 用于在实例化类时,注入结果到构造方法中 (一般不用)
    • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
    • arg - 将被注入到构造方法的一个普通结果
  • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
  • result – 注入到字段或 JavaBean 属性的普通结果
  • association – 一个复杂类型的关联;许多结果将包装成这种类型
    • 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
  • collection – 一个复杂类型的集合
    • 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
  • discriminator – 使用结果值来决定使用哪个resultMap (一般不用)
    • case – 基于某些值的结果映射
      • 嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射

由于constructor和discriminator使用场景并不多,此处我们不涉及,有兴趣的同学可以自己研究,官网参考

2、id和result的属性 - 映射普通类型

2.1、Id 和 Result 的属性

属性 描述
property 映射到列结果的字段或属性。如果用来匹配的 JavaBean 存在给定名字的属性,那么它将会被使用
column 数据库中的列名,或者是列的别名
javaType 一个 Java 类的完全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型
jdbcType JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。

2.2、传统字段不致解决方式

 <!-- 匹配 -->
 <select id="selectUsers" resultType="User">
   select id, username, hashedPassword
   from some_table
   where id = #{id}
 </select>
 <!-- 不匹配 -->
 <select id="selectUsers" resultType="User">
   select
     user_id             as "id",
     user_name           as "userName",
     hashed_password     as "hashedPassword"
   from some_table
   where id = #{id}
 </select>

当列名和属性名不能匹配上,可以在 SELECT语句中设置列别名(这是一个基本的 SQL 特性)来完成匹配,我们并没有看到resultMap,实际上,无论是匹配还是不匹配,==MyBatis 会在幕后自动创建一个 ResultMap,再根据属性名来映射列到JavaBean 的属性上

2.3、显示指定ResutMap(常用)

如果表单字段很多,我们发现需要添加as来匹配列名和属性,那么我们也可以显示指定ResultMap来完成匹配功能

自定义ResultMap:

id元素对应的属性会被标记为对象的标识符,在比较对象实例时使用

result元素都将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段

 <resultMap id="deptResultMap" type="dept">
     <id column="id" property="id"/>
     <result column="dept_name" property="deptName"/>
     <result column="remark" property="remark"/>
 </resultMap>

设置ResultMap:

然后在引用它的语句中设置 resultMap 属性就行了(注意我们去掉了resultType 属性)。比如:

  <select id="queryById" parameterType="int" resultMap="deptResultMap">
         select id, dept_name, remark
         from dept
         where id = #{id}
     </select>

注意:去掉 as deptName

二、关联 - association

[əˌsoʊsiˈeɪʃn]

用户和博客之间是一对多的关系,如果想在查询博客时同时想得到用户信息,那么我们在博客实体为中添加一个用户实体对象,通过关联就可以把博客和博客对应的用户取得。

关联映射的是一个对象

1、分类

关联的不同之处是,你需要告诉 MyBatis 如何加载关联。MyBatis 有两种不同的方式加载关联:

  • 嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
  • 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集。

2、常用属性

属性 描述
property 映射到列结果的字段或属性。如果用来匹配的 JavaBean存在给定名字的属性,那么它将会被使用。否则 MyBatis 将会寻找给定名称的字段。
javaType 一个 Java 类的完全限定名,或一个类型别名。如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。
column 数据库中的列名,或者是列的别名,如果需要嵌套查询,用来传参,一般和select一起用
select 嵌套查询时传select的ID,它会从 column属性指定的列中检索数据,作为参数传递给目标 select 语句。
resultMap 结果映射的 ID

更多属性,参考官网介绍

3、嵌套 Select 查询

查询员工培训记录时得到该同时得到该员工信息

3.1、根据员工Id查询员工信息:

 <mapper namespace="com.woniuxy.hrms.mapper.EmployeeMapper">
     <resultMap id="employeeResultMap" type="employee">
         <id  property="id" column="id"/>
         <result property="empName" column="emp_name"/>
         <result property="phone" column="phone" />
         <result property="address" column="address"/>
         <result property="salary" column="salry"/>
     </resultMap>
     <select id="selectEmployeeByPrimaryKey" resultMap="employeeResultMap">
         select emp_name, phone, address, salary from employee where id = #{id}
     </select>
 </mapper>

3.2、定义培训记录接口:

 public interface EmployeeTrainMapper {
     EmployeeTrain selectByPrimaryKey(Long id);
 }

3.3、培训实体类:

把emp_id修改为员工对象

 package com.woniuxy.hrms.entity;
 
 import java.io.Serializable;
 import java.util.Date;
 import lombok.Data;
 
 /**
  * 员工培训表
  * @TableName employee_train
  */
 @Data
 public class EmployeeTrain implements Serializable {
     /**
      *
      */
     private Long id;
 
     /**
      * 员工编号
      */
     private Employee employee;
 
     /**
      * 培训日期
      */
     private Date date;
 
     /**
      * 培训内容
      */
     private String content;
 
     /**
      * 备注
      */
     private String remark;
 
     private static final long serialVersionUID = 1L;
 
     @Override
     public String toString() {
         return "EmployeeTrain{" +
                 "id=" + id +
                 ", employee=" + employee +
                 ", date=" + date +
                 ", content='" + content + '\'' +
                 ", remark='" + remark + '\'' +
                 '}';
     }
 }

3.4、EmployeeTrainMapper.xml文件

column中的值为employee_train表对应的员工id值

select中的值如果调用提其它接口中的内容,应该使用完整路径的表示方式

 <mapper namespace="com.woniuxy.hrms.mapper.EmployeeTrainMapper">
 
     <resultMap id="BaseResultMap" type="com.woniuxy.hrms.entity.EmployeeTrain">
         <id property="id" column="id" jdbcType="BIGINT"/>
         <result property="date" column="date" jdbcType="DATE"/>
         <result property="content" column="content" jdbcType="VARCHAR"/>
         <result property="remark" column="remark" jdbcType="VARCHAR"/>
         <association property="employee" javaType="employee" column="emp_id"
                      select="com.woniuxy.hrms.mapper.EmployeeMapper.selectEmployeeByPrimaryKey"/>
     </resultMap>
 
     <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
         select
         id,emp_id,date,
         content,remark
         from employee_train
         where id = #{id,jdbcType=BIGINT}
     </select>
  </mapper>

4、嵌套结果映射

4.1、方式一

resultMap直接指向到employeeResultMap

 <mapper namespace="com.woniuxy.hrms.mapper.EmployeeTrainMapper">
 
     <resultMap id="BaseResultMap" type="com.woniuxy.hrms.entity.EmployeeTrain">
         <id property="id" column="id" jdbcType="BIGINT"/>
         <result property="date" column="date" jdbcType="DATE"/>
         <result property="content" column="content" jdbcType="VARCHAR"/>
         <result property="remark" column="remark" jdbcType="VARCHAR"/>
         <association property="employee" javaType="employee" column="emp_id"
                      resultMap="com.woniuxy.hrms.mapper.EmployeeMapper.employeeResultMap"/>
     </resultMap>
 
     <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
         select et.id,
                emp_id,
                date,
                content,
                remark,
                emp_name,
                phone,
                address,
                salary
         from employee_train et
                  inner join employee e on et.emp_id = e.id
         where et.id = #{id,jdbcType=BIGINT}
     </select>
    </mapper>

4.2、方式二(推荐)

直接在association标签中写映射关系

 <mapper namespace="com.woniuxy.hrms.mapper.EmployeeTrainMapper">
 
     <resultMap id="BaseResultMap" type="com.woniuxy.hrms.entity.EmployeeTrain">
         <id property="id" column="id" jdbcType="BIGINT"/>
         <result property="date" column="date" jdbcType="DATE"/>
         <result property="content" column="content" jdbcType="VARCHAR"/>
         <result property="remark" column="remark" jdbcType="VARCHAR"/>
         <association property="employee" javaType="employee" >
             <id  property="id" column="id"/>
             <result property="empName" column="emp_name"/>
             <result property="phone" column="phone" />
             <result property="address" column="address"/>
             <result property="salary" column="salary"/>
         </association>
     </resultMap>
 
     <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
         select et.id,
                emp_id,
                date,
                content,
                remark,
                emp_name,
                phone,
                address,
                salary
         from employee_train et
                  inner join employee e on et.emp_id = e.id
         where et.id = #{id,jdbcType=BIGINT}
     </select>
 </mapper>

三、集合 - collection

集合映射是一个集合

1、分类

集合元素和关联元素几乎是一样的,它们相似的程度之高,我们可以使用嵌套 Select 查询,或基于连接的嵌套结果映射集合:

  • 嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
  • 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集。

2、常用属性

属性 描述
property 映射到列结果的字段或属性。
javaType 用来匹配查询结果对应的java类型。一般都是ArrayList
column 数据库中的列名,或者是列的别名,如果需要嵌套查询,用来传参,一般和select一起用,随便写个aa也可以
select 嵌套查询时传select的ID,它会从 column属性指定的列中检索数据,作为参数传递给目标 select 语句。
resultMap 结果映射的 ID
ofType 映射集合的泛型,即集合里面存放的对象类型

更多属性,参考官网介绍

3、嵌套 Select 查询

查询员工信息时得到该同时得到该员工培训记录

3.1、根据员工Id查询员工培训信息:

 <mapper namespace="com.woniuxy.hrms.mapper.EmployeeTrainMapper">
       <resultMap id="BaseResultMap" type="com.woniuxy.hrms.entity.EmployeeTrain">
         <id property="trainId" column="train_id" jdbcType="BIGINT"/>
         <result property="date" column="date" jdbcType="DATE"/>
         <result property="content" column="content" jdbcType="VARCHAR"/>
         <result property="remark" column="remark" jdbcType="VARCHAR"/>
     </resultMap>
 
     <select id="queryAllByEmployee" resultMap="BaseResultMap">
         select
         id as train_id,emp_id,date,
         content,remark
         from employee_train where emp_id = #{empId};
     </select>
 </mapper>

3.2、定义培训记录接口:

 public interface EmployeeTrainMapper {
     List<EmployeeTrain> queryAllByEmployee(int empId);
 }

3.3、员工实体类:

 @Data
 public class Employee implements Serializable {
     /**
      * 员工编号
      */
     private Long id;
 
     /**
      * 员工姓名
      */
     private String empName;
 
     /**
      * 性别
      */
     private String gender;
 
     /**
      * 年龄
      */
     private Integer age;
     /**
      * 得到该员工培训记录
      */
     private List<EmployeeTrain> trains;
     //……
 }

3.4、EmployeeMapper.xml文件

注意:在查询的字段里一定要有员工的id,因为要根据查询出来的员工id,再去查询员工培训记录信息

 <mapper namespace="com.woniuxy.hrms.mapper.EmployeeMapper">
 
    <resultMap id="employeeResultMap" type="employee">
         <id property="id" column="id"/>
         <result property="empName" column="emp_name"/>
         <result property="phone" column="phone"/>
         <result property="address" column="address"/>
         <result property="salary" column="salary"/>
         <collection property="trains" javaType="ArrayList" column="id" ofType="employeeTrain"
                     select="com.woniuxy.hrms.mapper.EmployeeTrainMapper.queryAllByEmployee"/>
     </resultMap>
     <select id="selectEmployeeByPrimaryKey" resultMap="employeeResultMap">
         select id,emp_name, phone, address, salary
         from employee
         where id = #{id}
     </select>
  </mapper>

在一般情况下,MyBatis 可以推断 javaType 属性,因此并不需要填写。

column中的值为employee表对应的员工id值,也可以随便写,它就是来传参的

select中的值如果调用提其它接口中的内容,应该使用完整路径的表示方式

4、嵌套结果映射

4.1、嵌套结果映射一

注意:如果员工表和员工培训表主键都是id,那么这时集合查询出来的数据会只有一条(或者不存放在集合中),所以我们要把培训表的id起个别名,这样才能保证数据的准确性。或者不要出现字段一致

 <mapper namespace="com.woniuxy.hrms.mapper.EmployeeMapper">
 
     <resultMap id="employeeResultMap" type="employee">
         <id property="id" column="id"/>
         <result property="empName" column="emp_name"/>
         <result property="phone" column="phone"/>
         <result property="address" column="address"/>
         <result property="salary" column="salary"/>
         <collection property="trains" javaType="ArrayList" column="id" ofType="employeeTrain"
                     resultMap="com.woniuxy.hrms.mapper.EmployeeTrainMapper.BaseResultMap"
         />
     </resultMap>
     <select id="selectEmployeeByPrimaryKey" resultMap="employeeResultMap2">
         select e.id,
                emp_name,
                phone,
                address,
                salary,
                et.id as train_id,
                emp_id,
                date,
                content,
                remark
         from employee e
                  left join hrms.employee_train et on e.id = et.emp_id
         where e.id = #{id}
     </select>

这里使用的是左外连接查询

4.2、嵌套结果映射二(推荐)

直接在collection标签中写映射关系

 <mapper namespace="com.woniuxy.hrms.mapper.EmployeeMapper">
 
     <resultMap id="employeeResultMap2" type="employee">
         <id property="id" column="id"/>
         <result property="empName" column="emp_name"/>
         <result property="phone" column="phone"/>
         <result property="address" column="address"/>
         <result property="salary" column="salary"/>
         <collection property="trains" javaType="ArrayList" column="id" ofType="employeeTrain">
             <id property="trainId" column="train_id" jdbcType="BIGINT"/>
             <result property="date" column="date" jdbcType="DATE"/>
             <result property="content" column="content" jdbcType="VARCHAR"/>
             <result property="remark" column="remark" jdbcType="VARCHAR"/>
         </collection>
 
     </resultMap>
     <select id="selectEmployeeByPrimaryKey" resultMap="employeeResultMap2">
         select e.id,
                emp_name,
                phone,
                address,
                salary,
                et.id as train_id,
                emp_id,
                date,
                content,
                remark
         from employee e
                  left join hrms.employee_train et on e.id = et.emp_id
         where e.id = #{id}
     </select>

这里使用的是左外连接查询

四、关联关系的方向

在MyBatis中,关系关联的方向可以分为两种:单向关联和双向关联。

1、单向关联

单向关联表示关系只在一个方向上存在,其中一个表可以关联到另一个表,但反过来不行。在单向关联中,只有一个表的对象包含了关联对象的引用,而关联对象本身不包含对关联表的引用。

例如,假设我们有两个表:User和Order,一个用户可以有多个订单。在这种情况下,我们可以在User对象中定义一个关联属性List orders,表示一个用户关联多个订单。但是,在Order对象中并不包含对用户的引用。

单向关联的配置在MyBatis中非常简单,只需要在中使用标签来定义关联即可。

2、双向关联

双向关联表示关系在两个表之间是相互关联的,每个表的对象都包含了对另一个表的引用。这样,我们可以通过一个表的对象访问到关联表的对象,也可以通过关联表的对象访问到原始表的对象。

以前面的例子为例,我们可以在User对象中定义一个关联属性List orders,表示一个用户关联多个订单。同时,在Order对象中也定义一个关联属性User user,表示一个订单关联一个用户。

双向关联的配置相对复杂一些,需要在中使用标签来定义关联,并在关联对象中使用标签来定义反向关联。

需要注意的是,无论是单向关联还是双向关联,都需要在MyBatis的映射文件中进行配置。关联的方向取决于我们在配置时定义的关联属性和关联对象的引用。

总结来说,MyBatis中的关系关联可以是单向关联或双向关联。单向关联表示关系只在一个方向上存在,而双向关联表示关系在两个表之间是相互关联的。在配置关系关联时,我们需要定义关联属性和关联对象的引用,以确定关联的方向。

3、综合案例

员工实体类

 @Data
 public class Employee implements Serializable {
     /**
      * 员工编号
      */
     private Long id;
 
     /**
      * 得到该员工培训记录
      */
     private List<EmployeeTrain> trains;
 }

员工培训实体类

 @Data
 public class EmployeeTrain implements Serializable {
     /**
      *
      */
     private Long trainId;
 
     /**
      * 员工编号
      */
     private Employee employee;
 }

EmployeeMapper.xml

 <mapper namespace="com.woniuxy.hrms.mapper.EmployeeMapper">
 
     <resultMap id="employeeResultMap" type="employee">
         <id property="id" column="id"/>
         <result property="empName" column="emp_name"/>
         <result property="phone" column="phone"/>
         <result property="address" column="address"/>
         <result property="salary" column="salary"/>
         <collection property="trains"  ofType="employeeTrain">
             <id property="trainId" column="train_id" jdbcType="BIGINT"/>
             <result property="date" column="date" jdbcType="DATE"/>
             <result property="content" column="content" jdbcType="VARCHAR"/>
             <result property="remark" column="remark" jdbcType="VARCHAR"/>
         </collection>
 
     </resultMap>
     <select id="selectEmployeeByPrimaryKey" resultMap="employeeResultMap">
         select e.id,
                emp_name,
                phone,
                address,
                salary,
                et.id as train_id,
                emp_id,
                date,
                content,
                remark
         from employee e
                  left join hrms.employee_train et on e.id = et.emp_id
         where e.id = #{id}
     </select>
 </mapper>

EmployeeTrainMapper.xml

 <mapper namespace="com.woniuxy.hrms.mapper.EmployeeTrainMapper">
     <resultMap id="BaseResultMap" type="com.woniuxy.hrms.entity.EmployeeTrain">
         <id property="trainId" column="train_id" jdbcType="BIGINT"/>
         <result property="date" column="date" jdbcType="DATE"/>
         <result property="content" column="content" jdbcType="VARCHAR"/>
         <result property="remark" column="remark" jdbcType="VARCHAR"/>
         <association property="employee" javaType="employee">
             <id property="id" column="id"/>
             <result property="empName" column="emp_name"/>
             <result property="phone" column="phone"/>
             <result property="address" column="address"/>
             <result property="salary" column="salary"/>
         </association>
     </resultMap>
 
     <select id="queryAllByEmployee" resultMap="BaseResultMap">
         select
         id as train_id,emp_id,date,
         content,remark
         from employee_train where emp_id = #{empId};
     </select>
 
     <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
         select et.id as train_id,
                emp_id,
                date,
                content,
                remark,
                emp_name,
                phone,
                address,
                salary
         from employee_train et
                  inner join employee e on et.emp_id = e.id
         where et.id = #{id,jdbcType=BIGINT}
     </select>
 </mapper>

4、id的重要性

  • 在查询时一定要保证查询的主键字段的存在,并作为两张表的主外键
  • 两张表主键名称不能一致,否则集合查询时只能拿到一条记录,可以通过别名的方法来解决,更多参考
  • 如果查询时并不想显示外键所有数据,只想显示名称,可以考虑通过vo实体类来解决,不一定要使用关联

思维导图

image

posted @ 2025-04-18 09:41  icui4cu  阅读(24)  评论(0)    收藏  举报