4.结果映射
本章目标
- 结果映射
- 关联
- 集合
本章内容
一、结果映射
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
也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射
- 嵌套结果映射 –
- 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
单向关联的配置在MyBatis中非常简单,只需要在
2、双向关联
双向关联表示关系在两个表之间是相互关联的,每个表的对象都包含了对另一个表的引用。这样,我们可以通过一个表的对象访问到关联表的对象,也可以通过关联表的对象访问到原始表的对象。
以前面的例子为例,我们可以在User对象中定义一个关联属性List
双向关联的配置相对复杂一些,需要在
需要注意的是,无论是单向关联还是双向关联,都需要在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实体类来解决,不一定要使用关联
思维导图
本文来自博客园,作者:icui4cu,转载请注明原文链接:https://www.cnblogs.com/icui4cu/p/18832170