zoukankan      html  css  js  c++  java
  • 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信息时,才会把部门信息查询出来。(懒加载生效)
      
      注意:延迟加载只对分步查询有效果。
  • 相关阅读:
    Bootstrap留言板界面练习
    Bootstrap 学习
    web | [GXYCTF2019]禁止套娃
    re | [GKCTF2020]WannaReverse
    PE文件结构 | 在PE文件的空白区添加代码
    PE文件结构 | RVA与FOA的转换
    re | [NPUCTF2020]BasicASM
    win32 | WinSock2网络编程 | socket-tcp通信
    web | flask 修饰器实现原理
    运维 | 配置LNMP | 基于docker.ubuntu:16.04
  • 原文地址:https://www.cnblogs.com/niujifei/p/15238891.html
Copyright © 2011-2022 走看看