上一篇文章主要介绍了如何使用 resultType 来实现增删改查,但是如果需要实现更加复杂的查询语句,需要使用 ResultMap,下面我分两部分进行讲解。第一部分是最基本的规则配置,第二部分则是通过栗子说明如何进行更加复杂的查询操作
一、什么是 ResultMap?
resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来, 并在一些情形下允许你做一些 JDBC 不支持的事情。 实际上,在对复杂语句进行联合映射的时候,它很可能可以代替数千行的同等功能的代码。 ResultMap 的设计思想是,简单的语句不需要明确的结果映射,而复杂一点的语句只需要描述它们的关系就行了。
可以看出,和 resultType 相比,ResultMap 可以处理更加复杂的映射关系,我们可以自由定制我们需要的映射规则,这也是我们想要的效果。
二、如何使用 ResultMap?
我们先创建两张相关联的表,employee 表和 department 表。其中,每个员工信息包含部门信息,具体操作是,employee 的 dept_id 关联 department 表的 id 字段,即 id 是 dept_id 的外键
然后创建两个 POJP 类,这在后面的测试中会用到
Department.java
public class Department {
private Integer id;
private String departName;
private List employees;
public void setEmployees(List employees) {
this.employees = employees;
}
public List getEmployees() {
return employees;
}
...
}
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;
}
...
}
我们可以看到,Employee 类中包含了 Department 类,对应了员工表中每个员工对应一个部门,而一个部门却可以有多个员工
1. 使用 resultMap 查询一张表
我们先自定义一个标签名为
的 sql 映射规则,之后在 sql 中调用这个规则
说明:
type
:自定义规则的 JavaBean 类, 可以使用别名
id
:为了后面方便引用的
column
:指定列名
property
:指定对应的 JavaBean 属性
除了主键以外的属性,都可以用 result 标签来标注。id 定义的主键底层会有优化,当然也可以都使用 result。如果不指定对应的列和属性, 即 id 和 result 标签都不配置,mybatis 也会帮我们自动封装,不过建议每个映射都自己对应
2. 使用 resulutMap 查询两张表
需求是,使用 sql 语句,查询出每个员工的信息,以及员工所在的部门信息,一个员工对应一个部门,即一对一关系
方法一:使用关联操作
我们先写好查询员工信息的 sql 语句,包括使用外键关联到的部门信息,此时我们使用 resultMap 自定义查询规则。使用关联查询, department 对象为在 Employee类 中给 Department 类设置的引用对象。
方法二:使用 assocation 定义单个对象的封装规则
sql 语句和方法一一致,我们使用另一种自定义 resultMap 的查询方式。使用 assocation 定义单个对象的封装规则
property
:要将关联查询的用户信息映射到Orders中的哪个属性
javaType
:指定这个联合对象的类型
方法三:使用 assocation 进行分布查询
分布查询的步骤如下:
- 先按照员工
id
查找员工信息,对应语句为select * from employee where id = ?
- 根据查询到的员工信息中的
dept_id
查找部门信息,对应的语句为select * from department where id = ?
- 将查询到的部门信息关联到与之对应的员工信息
①. 查询员工信息的配置
②. 查询部门信息的配置
③. 使用 assocation 自定义 resultMap
定义单个对象的封装规则
select
:使用与该类(Employee)联合的类(Department)定义的方法(getDepartment,这里要使用全类名的方式给出,由于两个 sql 配置不在同一 mapper 下
column
:指定将哪一列的值作为参数传给 select 属性定义的方法所需要的参数。注意:如果 sql 语句中列名使用别名,则 column 里面的值一定要和别名相同
④. 最后在查询语句中使用上述定义的 resultMap 配置文件
DepartmentMapper.java
public interface DepartmentMapper {
public Department getDepartment(Integer id);
}
3. 关于延迟加载
既然已经提到使用分布查询,那就不得不提到延迟加载,这种机制是建立在分布查询的基础上的
3.1 什么是延迟加载?
在没有使用延迟加载情况下,比如我们在 assocation 分布查询的基础上,只想查询出
员工的姓名, 那么 mybatis 在执行完第一条 sql 语句后(查询员工个人信息语句,此时已经查询出了员工姓名),还会接着执行第二条 sql 语句(查询员工部门信息)。很显然,这样会显得冗余,查询效率低下。
延迟加载:又称按需加载,在分布查询的基础上,mybaits 只会按照我们需要的数据进行查询,当没有涉及当需要查询关联表的数据时候,则停止查询。比如我们只需要查询出员工(Employee)的姓名,延迟加载会只执行第一条 sql 语句,此时已经查询出员工姓名了,此时立刻停止执行语句。
当我们只查询 Employee 的 lastName 属性,测试代码输出结果进行对比如下:
//使用延迟加载,只执行一条 sql 语句
DEBUG 07-27 00:38:19,927 ==> Preparing: select * from employee where id = ? (BaseJdbcLogger.java:159)
DEBUG 07-27 00:38:19,960 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:159)
DEBUG 07-27 00:38:20,001 <== Total: 1 (BaseJdbcLogger.java:159)
Tom
//没有使用延迟加载,执行了两条 sql 语句
DEBUG 07-27 00:43:13,811 ==> Preparing: select * from employee where id = ? (BaseJdbcLogger.java:159)
DEBUG 07-27 00:43:13,846 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:159)
DEBUG 07-27 00:43:13,862 ====> Preparing: select * from department where id = ? (BaseJdbcLogger.java:159)
DEBUG 07-27 00:43:13,862 ====> Parameters: 1(Integer) (BaseJdbcLogger.java:159)
DEBUG 07-27 00:43:13,865 <==== Total: 1 (BaseJdbcLogger.java:159)
DEBUG 07-27 00:43:13,865 <== Total: 1 (BaseJdbcLogger.java:159)
Tom
3.2 如何设置
只需要在总的配置文件 mybatis-config.xml 中,进行如下配置
lazyLoadingEnabled
:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
aggressiveLazyLoading
:当开启时,任何方法的调用都会加载该对象的所有属性,否则,每个属性会按需加载
三、参考
mybatis 官方文档