MyBatis(四)映射文件 之 多对一映射查询

一、多对一映射关系

  POJO 中的属性可能会是一个对象,当我们查询多方的信息时,还想要知道一方的信息,就需要用到一对多查询。
  我们可以使用联合查询,并以级联属性的方式封装对象,使用 association 标签来定义对象的封装规则。
  两个 JavaBean 类:
  Employee 类
public class Employee {
    private Integer id;
    private String lastName;
    private String gender;
    private String email;
    private Department department;
    //省略了 get/set 方法和构造器
}
  Department 类
public class Department {
    private Integer id;
    private String deptName;
    private List<Employee> emps;
    //省略了 get/set 方法和构造器
}
  当我们想要获取员工同时还要获取其对应的部门信息,这个一个多对一的关系,我们就可以使用下面的几种方式来实现。
 

二、使用级联属性封装的方式查询

  mapper 中的方法:

//根据id查询员工和所在的部门信息
public Employee getEmpById(Integer id);

  mapper.xml 中的配置:

    <!--
        场景一:
            查询Employee的同时查询员工对应的部门
            Employee === Department
            一个员工有与之对应的部门信息
     -->
    <!--
        联合查询:级联属性封装结果集
    -->
    <resultMap id="MyDifEmp" type="com.njf.mybatis.bean.Employee">
        <id column="id" property="id"/>
        <result column="last_name" property="lastName"/>
        <result column="email" property="email" />
        <result column="gender" property="gender"/>
        <result column="did" property="department.id"/>
        <result column="dept_name" property="department.deptName"/>
    </resultMap>

    <!--
        public Employee getEmpAndDept(Integer id);
     -->
    <select id="getEmpAndDept" resultMap="MyDifEmp">
        SELECT e.`id` id, e.`last_name` last_name, e.`gender` gender, e.`dept_id` dept_id,
               d.`id` did, d.`dept_name` dept_name
        FROM tbl_employee e, tbl_department d
        WHERE e.`dept_id`=d.`id` AND e.`id` = #{id}
    </select>

  说明:

resultMap:自定义映射,用于自定义某个JavaBean的封装规则,处理复杂的表关系
id:用于设置主键的映射关系,定义主键底层会有优化,column 设置字段名(列),property 设置属性名,
result:设置非主键的映射关系,定义普通列封装规则,column 设置字段名(列),property 设置属性名

  

三、使用association 标签

  修改 mapper.xml 中的配置信息:

    <!--
        使用 association 定义关联的单个对象的封装规则
    -->
    <resultMap id="MyDifEmp2" type="com.njf.mybatis.bean.Employee">
        <id column="id" property="id"/>
        <result column="last_name" property="lastName"/>
        <result column="email" property="email" />
        <result column="gender" property="gender"/>
        <!--
            association:可以指定联合的JavaBean对象
            property="department":指定哪个属性是联合的对象
            javaType:指定这个属性对象的类型【不能省略】
        -->
        <association property="department" javaType="com.njf.mybatis.bean.Department">
            <id column="did" property="id"/>
            <result column="dept_name" property="deptName"/>
        </association>
    </resultMap>

    <!--
        public Employee getEmpAndDept(Integer id);
     -->
    <select id="getEmpAndDept" resultMap="MyDifEmp2">
        SELECT e.`id` id, e.`last_name` last_name, e.`gender` gender, e.`dept_id` dept_id,
               d.`id` did, d.`dept_name` dept_name
        FROM tbl_employee e, tbl_department d
        WHERE e.`dept_id`=d.`id` AND e.`id` = #{id}
    </select>
    通过 association 会帮我们创建一个 property 类型的对象,接着给该对象的属性赋值,然后再赋值给 Emp(resultMap 的 type)。

四、association 分步查询

  在实际的开发中,对于每个实体类都应该有具体的增删改查方法,也就说 DAO 层,因此,对于查询员工信息并且将对应的部门信息也查询出来的需求,就可以通过分步的方式完成查询。

  ① 先通过员工的 id 查询员工信息

  ②  再通过查询出来的员工信息中的外键(部门id)来查询对应的部门信息

  在 DeptMapper 接口中声明方法:

//根据id查询部门信息
public Department getDeptById(Integer id);

   在 DeptMapper 中配置:

    <!--
        public Department getDeptById(Integer id);
    -->
    <select id="getDeptById" resultType="Department">
        select id, dept_name deptName from tbl_department where id = #{id}
    </select>

  在 EmployeeMapper 中声明分步查询的方法:

//分步查询:先查员工信息,再查部门信息
public Employee getEmpByIdStep(Integer id);

  在 EmployeeMapper.xml 中配置:

    <!--
        使用 association 定义关联的单个对象的封装规则
        使用 association 进行分步查询
            1、先按照员工id查询员工信息
            2、根据查询员工信息中的 dept_id 去部门表查出部门信息
            3、部门设置到员工中
    -->
    <!--
        association:定义关联对象的封装规则
property:要处理的属性 select:表明当前属性是调用 select 指定的方法查出的结果,分步查询的SQL的id column:指定将哪一列的值传给这个方法 流程:使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性
--> <resultMap id="MyEmpByStep" type="com.njf.mybatis.bean.Employee"> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="email" property="email" /> <result column="gender" property="gender"/> <association property="department" select="com.njf.mybatis.dao.DepartmentMapper.getDeptById" column="dept_id"> </association> </resultMap> <!-- public Employee getEmpByIdStep(Integer id); --> <select id="getEmpByIdStep" resultMap="MyEmpByStep"> select * from tbl_employee where id=#{id} </select>

  说明:

association:定义关联对象的封装规则
property:要处理的属性
select:分步查询的SQL的id,即接口的全限定名.方法名或namespace.SQL的id
column:分步查询的条件,注意:此条件必须是从数据库查询过得(即调用的select方法的参数)指定将哪一列的值传给这个方法

  

  流程:使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性。

五、association 分步查询使用延迟加载(懒加载,按需加载)

  懒加载是只查询需要使用到的数据,如果使用不到,就不再查询。
  如:若想要查询 Emp 中员工的信息(不包含部门),部门信息就不再查询。如果不使用懒加载,将把所有的数据都查询出来。
  在分步查询的基础上,可以使用延迟加载来提升查询的效率,只需要在全局的 Setting 中进行如下的配置。
    <settings>
        <!-- 开启延迟加载 -->
        <setting name="lazyLoadingEnabled" value="true"/>      // 默认false,不开启
        <!-- 设置加载的数据是按需还是全部,是否查询所有数据 -->
        <setting name="aggressiveLazyLoading" value="false"/>  // 默认 true,查询所有
    </settings>
  设置说明:
<setting name="lazyLoadingEnabled" value="true"/>
是否开启延迟加载

<setting name="aggressiveLazyLoading" value="false"/>
aggressiveLazyLoading:侵入延迟加载,当使用任一属性,所有的属性都会加载;禁用的话,只有用到该属性才会加载,即按需加载
  这两个标签一块使用,当把懒加载关闭了(false), aggressiveLazyLoading 能查询所有的数据。
        如果开启了懒加载(true),但是 aggressiveLazyLoading 还是true,还是会将所有的数据查询出来。
        所以,如果想要使用懒加载(true),aggressiveLazyLoading 必须为 false,即按需查询。
 
  测试:
    @Test
     public void testResultMap() throws IOException {
          //1、获取 sqlSessionFactory
          SqlSessionFactory sqlSessionFactory = getsqlSessionFactory();

          //2、获取 sqlSession 实例,能直接执行已经映射的 SQL 语句
          SqlSession sqlSession = sqlSessionFactory.openSession();
          try {
               //3、获取接口的实现类对象
               /**
                * 推荐使用
                * 会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
                */
               EmployeeMapperPlus mapper = sqlSession.getMapper(EmployeeMapperPlus.class);

               Employee empByIdStep = mapper.getEmpByIdStep(1);
               System.out.println(empByIdStep);
               System.out.println("empByIdStep = " + empByIdStep.getDepartment());

          } finally {
               sqlSession.close();
          }

     }
  运行结果:
   可以看到向数据库发送了两条SQL语句,如果不涉及 Employee的Department信息,就不会查询所在部门的信息。只有用到了Department信息时,才会把部门信息查询出来。(懒加载生效)
  
  注意:延迟加载只对分步查询有效果。
原文地址:https://www.cnblogs.com/niujifei/p/15238891.html