美文网首页Java 杂谈
MyBatis | 映射文件之 ResultMap(一)

MyBatis | 映射文件之 ResultMap(一)

作者: EclipseO2 | 来源:发表于2018-07-27 01:02 被阅读3次

    上一篇文章主要介绍了如何使用 resultType 来实现增删改查,但是如果需要实现更加复杂的查询语句,需要使用 ResultMap,下面我分两部分进行讲解。第一部分是最基本的规则配置,第二部分则是通过栗子说明如何进行更加复杂的查询操作

    一、什么是 ResultMap?

    resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来, 并在一些情形下允许你做一些 JDBC 不支持的事情。 实际上,在对复杂语句进行联合映射的时候,它很可能可以代替数千行的同等功能的代码。 ResultMap 的设计思想是,简单的语句不需要明确的结果映射,而复杂一点的语句只需要描述它们的关系就行了。

    可以看出,和 resultType 相比,ResultMap 可以处理更加复杂的映射关系,我们可以自由定制我们需要的映射规则,这也是我们想要的效果。

    二、如何使用 ResultMap?

    我们先创建两张相关联的表,employee 表和 department 表。其中,每个员工信息包含部门信息,具体操作是,employee 的 dept_id 关联 department 表的 id 字段,即 id 是 dept_id 的外键

    employee表
    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 进行分布查询

    分布查询的步骤如下:

    1. 先按照员工 id 查找员工信息,对应语句为 select * from employee where id = ?
    2. 根据查询到的员工信息中的 dept_id 查找部门信息,对应的语句为 select * from department where id = ?
    3. 将查询到的部门信息关联到与之对应的员工信息

    ①. 查询员工信息的配置

    <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:当开启时,任何方法的调用都会加载该对象的所有属性,否则,每个属性会按需加载

    三、参考

    mybatis 官方文档

    相关文章

      网友评论

        本文标题:MyBatis | 映射文件之 ResultMap(一)

        本文链接:https://www.haomeiwen.com/subject/qjximftx.html