上一篇文章主要介绍了如何使用 resultType 来实现增删改查,但是如果需要实现更加复杂的查询语句,需要使用 ResultMap,下面我分两部分进行讲解。第一部分是最基本的规则配置,第二部分则是通过栗子说明如何进行更加复杂的查询操作
一、什么是 ResultMap?
resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来, 并在一些情形下允许你做一些 JDBC 不支持的事情。 实际上,在对复杂语句进行联合映射的时候,它很可能可以代替数千行的同等功能的代码。 ResultMap 的设计思想是,简单的语句不需要明确的结果映射,而复杂一点的语句只需要描述它们的关系就行了。
可以看出,和 resultType 相比,ResultMap 可以处理更加复杂的映射关系,我们可以自由定制我们需要的映射规则,这也是我们想要的效果。
二、如何使用 ResultMap?
我们先创建两张相关联的表,employee 表和 department 表。其中,每个员工信息包含部门信息,具体操作是,employee 的 dept_id 关联 department 表的 id 字段,即 id 是 dept_id 的外键
department 表
然后创建两个 POJP 类,这在后面的测试中会用到
Department.java
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;
}
...
}
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 查询一张表
我们先自定义一个标签名为 <resultMap>
的 sql 映射规则,之后在 sql 中调用这个规则
<resultMap type="Employee" id="emp">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
</resultMap>
<!-- resultMap: 自定义结果集映射规则 -->
<select id="getEmployee" resultMap="emp">
select * from employee where id = #{id}
</select>
说明:
<resultMap type="edu.just.mybatis.bean.Employee" id="emp">
type
:自定义规则的 JavaBean 类, 可以使用别名
id
:为了后面方便引用的
<id column="id" property="id"/>
column
:指定列名
property
:指定对应的 JavaBean 属性
除了主键以外的属性,都可以用 result 标签来标注。id 定义的主键底层会有优化,当然也可以都使用 result。如果不指定对应的列和属性, 即 id 和 result 标签都不配置,mybatis 也会帮我们自动封装,不过建议每个映射都自己对应
2. 使用 resulutMap 查询两张表
需求是,使用 sql 语句,查询出每个员工的信息,以及员工所在的部门信息,一个员工对应一个部门,即一对一关系
方法一:使用关联操作
我们先写好查询员工信息的 sql 语句,包括使用外键关联到的部门信息,此时我们使用 resultMap 自定义查询规则。使用关联查询, department 对象为在 Employee类 中给 Department 类设置的引用对象。
<select id="getEmployeeAndDepartment" resultMap="emp2">
SELECT e.id id, e.last_name lastName, e.email email, e.gender gender, e.dept_id dept_id, d.depart_name depart_name
FROM employee e, department d
WHERE e.dept_id = d.id AND e.id = #{id}
</select>
<resultMap type="Employee" id="emp2">
<!-- 关联 Employee 表的信息 -->
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<!-- 关联 Department 表的信息 -->
<result column="id" property="department.id"/>
<result column="depart_name" property="department.departName"/>
</resultMap>
方法二:使用 assocation 定义单个对象的封装规则
sql 语句和方法一一致,我们使用另一种自定义 resultMap 的查询方式。使用 assocation 定义单个对象的封装规则
property
:要将关联查询的用户信息映射到Orders中的哪个属性
javaType
:指定这个联合对象的类型
<select id="getEmployeeAndDepartment" resultMap="emp3">
SELECT e.id, e.last_name lastName, e.email email, e.gender gender, e.dept_id, d.depart_name depart_name
FROM employee e, department d
WHERE e.dept_id = d.id AND e.id = #{id}
</select>
<resultMap type="Employee" id="emp3">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<!-- 配置 Employee 关联的 Department 的信息 -->
<association property="department" javaType="edu.just.mybatis.bean.Department">
<id column="id" property="id"/>
<result column="depart_name" property="departName"/>
</association>
</resultMap>
方法三:使用 assocation 进行分布查询
分布查询的步骤如下:
- 先按照员工
id
查找员工信息,对应语句为select * from employee where id = ?
- 根据查询到的员工信息中的
dept_id
查找部门信息,对应的语句为select * from department where id = ?
- 将查询到的部门信息关联到与之对应的员工信息
①. 查询员工信息的配置
<select id="getEmployee" resultMap="emp">
select * from employee where id = #{id}
</select>
②. 查询部门信息的配置
<select id="getDepartment" resultType="edu.just.mybatis.bean.Department">
select * from department where id = #{id}
</select>
③. 使用 assocation 自定义 resultMap
<assocation>
定义单个对象的封装规则
select
:使用与该类(Employee)联合的类(Department)定义的方法(getDepartment,这里要使用全类名的方式给出,由于两个 sql 配置不在同一 mapper 下
column
:指定将哪一列的值作为参数传给 select 属性定义的方法所需要的参数。注意:如果 sql 语句中列名使用别名,则 column 里面的值一定要和别名相同
<resultMap type="Employee" id="empStep">
<!-- 配置 Employee 信息 -->
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<!-- 配置 Department 信息 -->
<association property="department"
select="edu.just.mybatis.dao.DepartmentMapper.getDepartment"
column="dept_id">
</association>
</resultMap>
④. 最后在查询语句中使用上述定义的 resultMap 配置文件
<select id="getEmployeeAndDepartment" resultMap="empStep">
SELECT e.id id, e.last_name last_Name, e.email email, e.gender gender, e.dept_id dept_id, d.depart_name depart_name
FROM employee e, department d
WHERE e.dept_id = d.id AND e.id = #{id}
</select>
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 中,进行如下配置
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
lazyLoadingEnabled
:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
aggressiveLazyLoading
:当开启时,任何方法的调用都会加载该对象的所有属性,否则,每个属性会按需加载
网友评论