本文主要讲述mybatis的处理表与表之间的关系

一. 介绍t_emp和t_dept表

  1. t_emp表结构

vip

  2. t_dept表结构

vip

二. 数据表的关系

  1. 阐明关系

  一个部门可以有多个员工,但是一个员工只能属于一个部门

  2. 实体类pojo的声明

  1) Employee类的声明如下

public class Employee {
    private Integer empId;
    private String empName;
    private Integer age;
    private String gender;

    private Department dept;

    public Employee() {
    }

    get和set()方法...

    @Override
    public String toString() {
        return "Employee{" +
                "empId=" + empId +
                ", empName='" + empName + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", dept=" + dept +
                '}';
    }
}

  由于一个员工只能属于一个部门,因此在Employee中才会有 private Department dept 成员变量。

  2)Department类声明如下

public class Department {
    private Integer deptId;
    private String deptName;

    private List<Employee> employees;

    public Department() {
    }

    get和set()方法...

    @Override
    public String toString() {
        return "Department{" +
                "deptId=" + deptId +
                ", deptName='" + deptName + '\'' +
                ", employees=" + employees +
                '}';
    }
}

  由于一个部门可以有多个员工,因此在Department类中才会有 private List<Employee> employees 成员变量

三. 使用mybatis解决多对一【一对多】

  1. 多对一的关系

  这里的多对一的关系是指:多个员工在一个部门,思考的角度是站在员工Employee类的。

  1)提出问题:

  当需要查询单个员工信息以及所在部门时,应该如何处理?

  EmpMapper接口声明如下

public interface EmpMapper {
    // 根据id查询员工信息
    Employee selectEmpAndDept(@Param("id") int id);
}

  2)处理方式:

  方式1:采用级联的方式

  EmpMapper.xml文件声明如下

    <!--// 根据id查询员工信息
    Employee selectEmpAndDept(@Param("id") int id);-->
    <!--使用: resultType="Employee"结果: Employee{empId=4, empName='赵六', age=24, gender='男', dept=null}-->
    <!--级联-->
    <resultMap id="resultEmpAndDept1" type="Employee">
        <id column="emp_id" property="empId" />
        <result column="emp_name" property="empName" />
        <result column="dept_id" property="dept.deptId" />
        <result column="dept_name" property="dept.deptName" />
    </resultMap>
    <select id="selectEmpAndDept" resultMap="resultEmpAndDept1">
        SELECT t_emp.*,t_dept.`dept_name`
        FROM t_emp,t_dept
        WHERE t_emp.dept_id = t_dept.dept_id and emp_id = #{id}
    </select>

  前面提过resultMap可以自定义映射名,下面阐述<resultMap>标签

    <!--
    resultMap:
        id:resultMap标签的唯一标识
        type:返回类型
        <id>:表t_emp的主键的字段名
        <result>:表t_emp的非主键的字段名
        其中
        <result column="dept_id" property="dept.deptId" />
            column:字段名dept_id,property:实体类的属性名dept.deptId
            意义是将查询到的部门信息--封装--》Department对象 --赋值--》Employee类中的dept成员变量
    -->
    <resultMap id="resultEmpAndDept1" type="Employee">
        <id column="emp_id" property="empId" />
        <result column="emp_name" property="empName" />
        <result column="dept_id" property="dept.deptId" />
        <result column="dept_name" property="dept.deptName" />
    </resultMap>        

  注意:<result column="dept_id" property="dept.deptId" />,是Employee类的成员对象dept . 属性名

  方式2:采用 association 标签

  EmpMapper.xml文件声明如下

    <resultMap id="resultEmpAndDept2" type="Employee">
        <id column="emp_id" property="empId"></id>
        <result column="emp_name" property="empName"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
        <!--association的
                     property:是需要处理映射关系的属性名,如dept
                     javaType:是设置要处理属性的类型名称-->
        <association property="dept" javaType="Department">
            <id column="dept_id" property="deptId"></id>
            <id column="dept_name" property="deptName"></id>
        </association>
    </resultMap>
    <select id="selectEmpAndDept" resultMap="resultEmpAndDept2">
        SELECT t_emp.*,t_dept.`dept_name`
        FROM t_emp,t_dept
        WHERE t_emp.dept_id = t_dept.dept_id and emp_id = #{id}
    </select>        

  下面阐述<association>标签 的内容

   <!--association的
                 property:是需要处理映射关系的属性名,如dept
                 javaType:是设置要处理属性的类型名称-->
    <association property="dept" javaType="Department">
        <id column="dept_id" property="deptId"></id>
        <id column="dept_name" property="deptName"></id>
    </association>    

  方式3:采用分步查询

  先根据员工编号,查询员工信息,在根据员工信息中的部门编号,查询相对应的部门信息

  因此,需要创建DeptMapper接口,DeptMapper接口的声明如下

public interface DeptMapper {

    // 根据id查询部门
    Department selectDeptById(@Param("id") int id);
}

  DeptMapper.xml文件声明如下

<!--namespace绑定mapper的接口所在的包名.接口名-->
<mapper namespace="com.hspedu.mapper.DeptMapper">
    <!--// 根据id查询部门
    Department selectDeptById(@Param("id") int id);-->
    <select id="selectDeptById" resultType="Department">
        select * from t_dept where dept_id = #{id}
    </select>

</mapper>

  EmpMapper.xml文件声明如下

  <resultMap id="resultEmpAndDept3" type="Employee">
        <id column="emp_id" property="empId"></id>
        <result column="emp_name" property="empName"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
        <!--association的
            property:设置需要处理映射关系的属性的属性名
            select:设置分步查询的sql语句的唯一标识
            column:将select标签中查询出的某一个字段作为分步查询的条件
            fetchType:是否开启延迟加载【针对某一个sql语句】eager立刻,lazy延迟
            -->
        <association property="dept" fetchType="eager"
                     select="com.hspedu.mapper.DeptMapper.selectDeptById"
                     column="dept_id">
        </association>
    </resultMap>
    <select id="selectEmpAndDeptThree" resultMap="resultEmpAndDept3" >
        select * from t_emp where emp_id = #{id}
    </select>

  下面阐述 <association> 标签的内容

   <!--association的
          property:设置需要处理映射关系的属性的属性名
          select:设置分步查询的sql语句的唯一标识
          column:将select标签中查询出的某一个字段作为分步查询的条件
          fetchType:在方法中是否开启延迟加载【针对某一个sql语句】eager立刻,lazy延迟
    -->
    <association property="dept" fetchType="eager"
                select="com.hspedu.mapper.DeptMapper.selectDeptById"
                column="dept_id">
    </association>

  注意:association标签中的select的意义,

  在t_emp表中查询到的员工信息字段中的dept_id值作为参数,传入到DeptMapper接口的selectDeptById()方法中,

  在t_dept表中按照部门编号dept_id查询相对应的部门信息。

  分步查询 插入一个知识点:延迟加载

  a> 引入延迟加载

    当我们只是想查询员工信息的姓名时,例如 

    @Test
    // 查询指定id的员工信息
    public void test02(){
        Employee employee = mapper.selectEmpAndDept(4);
        System.out.println(employee.getEmpName());
    }

    如果没有延迟加载的话,执行结果如下

DEBUG 02-04 15:37:05,646 ==> Parameters: 4(Integer) (BaseJdbcLogger.java:137) 
DEBUG 02-04 15:37:05,669 ====>  Preparing: select * from t_dept where dept_id = ? (BaseJdbcLogger.java:137) 
DEBUG 02-04 15:37:05,669 ====> Parameters: 100(Integer) (BaseJdbcLogger.java:137) 
DEBUG 02-04 15:37:05,675 <====      Total: 1 (BaseJdbcLogger.java:137) 
DEBUG 02-04 15:37:05,677 <==      Total: 1 (BaseJdbcLogger.java:137) 
赵六

    即执行了查询员工信息的sql语句,又执行了查询部门信息的sql语句,如何让它不执行查询部门信息的sql语句呢?

  b> 延迟加载的配置

    在mybatis-config.xml文件中添加如下setting标签,引入延迟加载

    <settings>
        <!--将MySQL中_映射为java的驼峰-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
       <!-- 开启延时加载【懒加载】-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--强制加载所有的懒配置-->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

    并将<association fetchType="eager"> 的  fetchType = "lazy",即在执行该sql语句时,使用延迟加载,执行结果如下

DEBUG 02-04 15:42:26,231 ==>  Preparing: select * from t_emp where emp_id = ? (BaseJdbcLogger.java:137) 
DEBUG 02-04 15:42:26,261 ==> Parameters: 4(Integer) (BaseJdbcLogger.java:137) 
DEBUG 02-04 15:42:26,322 <==      Total: 1 (BaseJdbcLogger.java:137) 
赵六

    只执行了查询员工信息的sql语句,说明延迟加载有了效果。

   c> 延迟加载总结:

    引入延迟加载,在mybatis配置文件中添加setting标签......【全局 延迟加载】

    如果对于某一个sql语句,不想使用延迟加载,则<association fetchType="eager"> 【局部 不使用延迟加载】

  3)总结:

  根据传入的emp_id在t_emp表中查询员工信息,员工信息的字段中包含有dept_id,然后依据dept_id,在t_dept表中查询相应的部门信息,

  将部门信息--反射-->Department类的对象--赋值-->Employee类的成员变量dept。

  2. 一对多关系

  这里的一对多是一个部门可以有多个员工,是在Department类角度考虑的。

  1)提出问题

  按照部门编号查询一个部门有多少员工?

  DeptMapper接口声明如下

public interface DeptMapper {

    // 查询一个部门有多少员工
    Department selectDeptAndEmp(@Param("id") int id);
}

  2)处理方式

  方式1:使用collection标签

  DeptMapper.xml文件声明如下

    <!--// 查询一个部门有多少员工
    Department selectDeptAndEmp(@Param("id") int id);-->
    <resultMap id="resultDeptAndEmp1" type="Department">
        <id column="dept_id" property="deptId"></id>
        <result column="dept_name" property="deptName"></result>
        <collection property="employees" ofType="Employee">
            <id column="emp_id" property="empId"></id>
            <result column="emp_name" property="empName"></result>
            <result column="age" property="age" ></result>
            <result column="gender" property="gender"></result>
        </collection>
    </resultMap>
    <select id="selectDeptAndEmp" resultMap="resultDeptAndEmp1">
        SELECT t_emp.*,t_dept.*
        FROM t_emp,t_dept
        WHERE t_emp.`dept_id` = t_dept.`dept_id` and t_dept.dept_id = #{id}
    </select>

  下面阐述collection标签

    <collection property="employees" ofType="Employee">
            <id column="emp_id" property="empId"></id>
            <result column="emp_name" property="empName"></result>
            <result column="age" property="age" ></result>
            <result column="gender" property="gender"></result>
     </collection>

  property:实体类的成员变量【属性】,ofType:表示将查询的结果--封装-->实体类对象--装入-->集合

  方式2:分步查询

  第一步:在t_dept表中根据传入的dept_id查询部门信息

  第二步:在t_emp中根据第一步中得到的dept_id查询员工信息

  EmpMapper接口的声明如下

public interface EmpMapper {

    // 根据部门编号查询员工信息
    List<Employee> selectEmployeesByDeptId(@Param("id") int id);
}

  EmpMapper.xml文件声明如下

    <!-- // 根据部门编号查询员工信息
    List<Employee> selectEmployeesByDeptId(@Param("id") int id);-->
    <resultMap id="resultEmployeesByDeptId" type="Employee">
        <id column="emp_id" property="empId"></id>
        <result column="emp_name" property="empName"></result>
        <result column="age" property="age"></result>
        <result column="gender" property="gender"></result>
    </resultMap>
    <select id="selectEmployeesByDeptId" resultMap="resultEmployeesByDeptId">
        select * from t_emp where dept_id = #{id}
    </select>

  DeptMapper.xml文件声明如下

     <resultMap id="resultDeptAndEmp" type="Department">
        <id column="dept_id" property="deptId"></id>
        <result column="dept_name" property="deptName"></result>
        <association property="employees" fetchType="lazy"
                     select="com.hspedu.mapper.EmpMapper.selectEmployeesByDeptId"
                     column="dept_id"></association>
    </resultMap>
    <select id="selectDeptAndEmpTwo" resultMap="resultDeptAndEmp">
        select * from t_dept where dept_id = #{id}
    </select>

  使用association标签,

  property:实体类需要映射处理的成员名【属性】;

  select:调用EmpMapper接口的 selectEmployeesByDeptId()方法,即 根据得到的部门信息中的dept_id,在t_emp表中查询该部门的所有员工信息。

  3)总结

  由dept_id在t_emp查询的结果集--反射-->Employee对象--装入-->Employee集合--赋值-->Department类的employees成员变量。