MyBatis 的关联关系也被称为高级结果映射。
当实体与实体存在多对一、一对多、多对多三种关系时,MyBatis 提供了多种方法将查询结果组装进实体类对象当中。
假设现有 Employee 和 Department 两个实体类以及对应的 MySQL 表,如下所示:
public class Department {
private int id;
private String name;
private int number;
// 省略 getter 和 setter
}
public class Employee {
private int id;
private int number;
private String name;
private String gender;
private int age;
private Department dep;
// 省略 getter 和 setter
}
CREATE TABLE department
(
id INT(11) NOT NULL,
name VARCHAR(255) NOT NULL,
number INT(11) NOT NULL,
PRIMARY KEY(id)
);
CREATE TABLE employee
(
id INT(11) NOT NULL,
age INT(11) NOT NULL,
gender VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
number INT(11) NOT NULL,
dep_id INT(11) NULL,
PRIMARY KEY(id)
);
1、多对一
以 Employee 和 Department 两个实体类举例,因 Employee 类有一个 Department 的属性,所以 Employee 实际上和 Department 形成了多对一的关系。当要使用 MyBatis 查询所有的 Employee 对象和每个 包含的 Department 对象时,可以使用别名方法或者定义 ResultMap 方法。
别名方法是对查询的列设置别名,代码如下所示:
<select id="search" resultType="cc.loac.test.entity.Employee">
SELECT e.*, d.id AS 'dep.id', d.number AS 'dep.number', d.name AS 'dep.name' FROM
employee AS e LEFT JOIN department AS d ON e.dep_id = d.id
</select>
MyBatis 会将 dep.id 对应的值组装到 Employee 实例类 dep 属性的 id 属性上
上面方法虽然方便,但是阅读性不佳。因此 MyBatis 提供了自定义结果集 <resultMap>
标签和多对一 <association>
标签搭配使用的方法。修改后的代码如下所示:
<select id="search" resultMap="EmpAndDep">
SELECT e.*, d.name AS depName, d.number AS depNumber FROM employee AS e LEFT JOIN department AS d ON e.dep_id = d.id ORDER by e.id
</select>
<resultMap type="cc.loac.test.entity.Employee" id="EmpAndDep">
<id property="id" column="id" />
<result property="number" column="number" />
<result property="name" column="name" />
<result property="gender" column="gender" />
<result property="age" column="age" />
<association property="dep" javaType="cc.loac.test.entity.Department">
<id property="id" column="dep_id" />
<result property="number" column="depNumber" />
<result property="name" column="depName" />
</association>
</resultMap>
另外 MyBatis 还提供了一种嵌套查询的方法,代码如下所示:
<select id="search" resultMap="EmpAndDep">
<!-- 省略查询代码 -->
</select>
<resultMap type="cc.loac.test.entity.Employee" id="EmpAndDep">
<!-- 省略基本属性 -->
<association property="dep" column="dep_id" javaType="cc.loac.test.entity.Department"
select="cc.loac.test.dao.DepartmentDap.searchById">
</association>
</resultMap>
事实上这种方法是对 Employee 类型的结果集进行循环,在循环中根据 dep_id 去查询对应的 Department 数据并组装成 Department 类型的对象,再设置到 Employee 对象的 dep 属性中。
该方法避免了 JOIN 查询性能问题,并且适合做懒加载和缓存等场景。
2.一对多
如果在实体类 Department 中新定义一个 List<Employee>
类型的 emps 属性,则 Department 与 Employee 实体类为一对多的关系。
面对一对多的情况,MyBatis 提供了 <resultMap>
标签搭配 <collection>
标签的解决方案。
假如我们现在要查询所有部门,并且要关联各部门所包含的所有员工,代码如下所示:
<select id="searchDepAndEmp" resultMap="DepAndEmp">
SELECT e.*, d.number AS depNumber, d.name AS depName FROM department AS d LEFT JOIN employee AS e ON d.id = e.dep_id
</select>
<resultMap type="cc.loac.test.entity.Department" id="DepAndEmp">
<id property="id" column="d_id" />
<result property="number" column="depNumber" />
<result property="name" column="depName" />
<collection property="emps" ofType="cc.loac.test.entity.Employee">
<id property="id" column="id" />
<result property="number" column="number" />
<result property="name" column="name" />
<result property="gender" column="gender" />
<result property="age" column="age" />
</collection>
</resultMap>
3.多对多
多对多关系在面向对象实体中,其实就是在各方都有对方泛型的集合,从任意一方来看对方都死一对多的关系,所有同样使用一对多的方法进行处理。