在上一篇文章 MyBatis | 映射文件之 ResultMap(一)中,简述了如何使用 ResultMap,以及如何使用 ResultMap 进行一对一的关联查询。这一篇我将说明如何使用 ResultMap 进行一对多的关联查询
一、说明与准备
为了便于学习,我仍然会贴出表结构和 POJO 的设计,下面是实验前的环境搭建
employee 的 dept_id 关联 department 表的 id 字段,即 id 是 dept_id 的外键。这和上一篇的创建是一致的
Employee.java
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender;
private Department department;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
...
}
Department:这里我设置了一个类型为 Employee 的 List 集合,对应数据库表中一个部门可以有多个员工
public class Department {
private Integer id;
private String departName;
private List<Employee> employees;
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
public List<Employee> getEmployees() {
return employees;
}
...
}
二、使用 ResultMap 进行一对多关联查询
需求是,使用 sql 语句,查询出每个部门的信息,以及每个部门所关联的员工信息。由于一个部门对应多个员工,即一对多,这里我们使用 LEFT JOIN 对字段进行连接
1. 使用 collection 进行两张表的关联查询
类似于上一篇中介绍的一对一的关联查询,不过上一篇中关联的是一个对象,不这里关联的是一个集合。至于 collection
,我会结合代码进行讲解
①. 首先创建一个集合类
DepartmentMapper.java
public interface DepartmentMapper {
public Department getDepartmentAndList(Integer id);
}
②. 配置 sql 映射文件
下面是 DepartmentMapper.xml 的配置信息,sql 语句如下,当然是使用外键关联
<select id="getDepartmentAndList" resultMap="dept">
SELECT d.id did, d.depart_name, e.id eid, e.last_name, email, gender
FROM department d
LEFT JOIN employee e
ON d.id = e.dept_id
WHERE d.id = #{id}
</select>
接下来写 resultMap 的配置:外层的 id 和 result 标签,是配置 Department 类(表)的,collection 标签里面的 id 和 result 标签,都是用来配置与 Department 类关联的 Employee 类(表)的。(PS:由于我在 mybatis.xml 主配置文件中配置了使用别名,因此类型都是以别名显示。否则需要全类名。)
collection
标签里定义的集合对象,这里定义的是 Employee 类型的集合
property
:指定 Department 对象里面包含的 Employee 集合对象,即 List<Employee> employees
ofType
:指定集合的类型
<!-- 使用嵌套结果集的方式, 使用 collection 标签定义关联的集合类型的封装规则 -->
<resultMap type="Department" id="dept">
<!-- Deparment 类的配置 -->
<id column="id" property="id"/>
<result column="depart_name" property="departName"/>
<!-- Employee 类的配置 -->
<collection property="employees" ofType="Employee">
<id column="id1" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
</collection>
</resultMap>
③. 结果
测试语句我不写了,我们传入 Employee 的 id 为 1,直接贴出结果
Department [id=1, departName=开发部]
[Employee [id=1, lastName=Tom, email=tom@126.com, gender=0],
Employee [id=2, lastName=Peter, email=peter@qq.com, gender=1],
Employee [id=7, lastName=ljk, email=lkj@123.com, gender=1],
Employee [id=15, lastName=lkj, email=lkj@123.com, gender=2]]
同时贴出使用同样 sql 语句和同样 id 的数据库查询的结果
+-----+-------------+------+-----------+--------------+--------+
| did | depart_name | eid | last_name | email | gender |
+-----+-------------+------+-----------+--------------+--------+
| 1 | 开发部 | 1 | Tom | tom@126.com | 0 |
| 1 | 开发部 | 2 | Peter | peter@qq.com | 1 |
| 1 | 开发部 | 7 | ljk | lkj@123.com | 1 |
| 1 | 开发部 | 15 | lkj | lkj@123.com | 2 |
+-----+-------------+------+-----------+--------------+--------+
可以看到,使用 resultMap 查询出来的结果和数据库查询的结果一致,而且 Employee 确实是以 List 集合的形式出现的。
⭐⭐⭐遇到的坑:
在查询的过程中,遇到了一个毕竟大的坑🕳,当时找了快几个小时╯︿╰,这里说明一下,防患于未来。。
先斩后奏把。column 里面的值一定要与查询出来的列名相一致, 如果使用列的别名, 则 column 的值就是列的别名
拿上面的 sql 为例,我们看一下使用别名查询出来的列名
| did | depart_name | eid | last_name | email | gender |
以 id 这个字段为例子,我们注意一下 resultMap 里面配置的两个 id 标签对应的 column 对应的值,可以看到对应的是 sql 语句(SELECT d.id did, e.id eid ... FROM department d...
)中设置的别名 did
和 eid
。
<resultMap type="Department" id="dept">
<id column="did" property="id"/>
...
<collection property="employees" ofType="Employee">
<id column="eid" property="id"/>
....
</collection>
</resultMap>
如果你没有设置别名(SELECT d.id, e.id ... FROM department d...
),同时你在两个 id 标签的 column 直接填 id,那么就会出问题。。血的教训😭
| id | depart_name | id1 | last_name | email | gender |
不用别名查询出来的表的列名结构是这样的,如果真要用,可以这样配置。这样也是可以查询出结果的,不过并不推荐。还是使用别名把!!!
<resultMap type="Department" id="dept">
<id column="d1" property="id"/>
...
<collection property="employees" ofType="Employee">
<id column="id1" property="id"/>
....
</collection>
</resultMap>
2. 使用 collection 进行两张表的分布查询
使用 association 进行分布查询,实则分三步:
- 先根据部门 id 查询出部门所有信息:
select * from department where id = ?
- 根据部门查询出来的 id(外键),用于查询部门对应的多个员工信息:
select * from employee where dept_id = ?
- 将员工信息关联到部门信息
先创建一个接口类
public interface DepartmentMapper {
public Department getDepartmentStep(Integer id);
}
查询部门 Department 类的配置
<select id="getDepartmentStep" resultMap="empStep">
select id, depart_name from department where id = #{id}
</select>
查询员工 Employee 类的配置
<select id="getEmployeeByDeptId" resultType="Employee">
select id, last_name, gender, email, dept_id from employee where dept_id = #{deptId}
</select>
配置 resultMap,注意 collection 中的 column 属性对应数据库中的列名
<resultMap type="Department" id="empStep">
<!-- Department 类的配置 -->
<id column="id" property="id"/>
<result column="depart_name" property="departName"/>
<!--
使用 collection 定义分段的集合类型的属性
-->
<collection property="employees"
select="edu.just.mybatis.dao.EmployeeMapper.getEmployeeByDeptId"
column="id">
</collection>
</resultMap>
结果:
[Employee [id=1, lastName=Tom, email=tom@126.com, gender=0, department=null],
Employee [id=2, lastName=Peter, email=peter@qq.com, gender=1, department=null],
Employee [id=7, lastName=ljk, email=lkj@123.com, gender=1, department=null],
Employee [id=15, lastName=lkj, email=lkj@123.com, gender=2, department=null]]
3. 使用延迟加载
collection 标签中的延迟加载比较简单,直接在 collection 里面设置 fetchType
属性值为 lazy
即可
fetchType="lazy"
表示使用延迟加载
fetchType="eager"
表示立即加载
<resultMap type="Department" id="empStep">
...
<collection property="employees"
...
fetchType="lazy">
</collection>
</resultMap>