mybatis复习04高级查询 一对多,多对一的映射处理,collection和association标签的使用

  • Post author:
  • Post category:其他


一对多,和多对一之间的关系。比如,当前有一个实体类为员工类,还有一个实体类为部门类,那么由日常生活便能看出他们之间的关系,一个员工只属于一个部门,然后一个部门包括多个员工。



创建数据库表 员工和部门


员工表 t_emp:


在这里插入图片描述


部门表t_dept:


在这里插入图片描述

填充一些测试的数据:

在这里插入图片描述

在这里插入图片描述



抽象对应的实体类



员工实体类

由一开始的分析得出,员工类应包含员工个人的基本信息,以及对应部门的基本信息。所以在员工类中,应包含部门这一属性,部门也是一个单独的对象,包括部门号和部门名称。

确定了类中的基本属性后,为其添加对应的构造器,这里需要注意的是,员工类的构造器应只具备其员工个人基本属性的构造器,然后添加对应的getter和setter方法以及重写toString方法。

package com.gothic.sunset.pojo;

public class Emp {
    private Integer empId;//员工号

    private String empName;//员工姓名

    private Integer age;//员工年龄

    private String gender;//员工性别

    private Dept dept;//部门

    public Emp() {
    }

    public Emp(Integer empId, String empName, Integer age, String gender) {
        this.empId = empId;
        this.empName = empName;
        this.age = age;
        this.gender = gender;
    }

    public Integer getEmpId() {
        return empId;
    }

    public void setEmpId(Integer empId) {
        this.empId = empId;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

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



部门实体类

部门实体类应包括部门的基本信息和每个部门中所有员工的信息。同理,其构造器也需要注意,只具备部门基本信息的构造器。

package com.gothic.sunset.pojo;

import java.util.List;

public class Dept {
    private Integer deptId;//部门id号

    private String deptName;//部门名称

    private List<Emp> emps;//每个部门中的所有员工

    public Dept() {
    }

    public Dept(Integer deptId, String deptName) {
        this.deptId = deptId;
        this.deptName = deptName;
    }

    public Integer getDeptId() {
        return deptId;
    }

    public void setDeptId(Integer deptId) {
        this.deptId = deptId;
    }

    public String getDeptName() {
        return deptName;
    }

    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }

    public List<Emp> getEmps() {
        return emps;
    }

    public void setEmps(List<Emp> emps) {
        this.emps = emps;
    }

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



多对一的映射处理



级联方式resultMap自定义映射处理


需求:根据员工id,查询员工的所有信息(包括他的基本信息和对应的部门信息)。


首先,在mapper接口中编写对应的方法:

	/**
     * 根据id查询员工信息
     * @param empId
     * @return
     */
    Emp getEmpByEmpId(@Param("empId") Integer empId);

在映射文件xml中编写对应的sql:

	<resultMap id="getEmpAllByEmpId" type="Emp">
        <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>
        <result column="dept_id" property="dept.deptId"></result>
        <result column="dept_name" property="dept.deptName"></result>
    </resultMap>
    <!-- Emp getEmpByEmpId(@Param("empId") Integer empId);-->
    <select id="getEmpByEmpId" resultMap="getEmpAllByEmpId">
        select
            t_emp.*,t_dept.*
        from t_emp
                 inner join t_dept
                           on t_emp.dept_id = t_dept.dept_id
        where t_emp.emp_id = #{empId}
    </select>

这里通过使用resultMap自定义映射关系来将一对多映射处理,然后sql的话使用两表内连接查询。

测试用例:

	@Test
    public void testGetEmpByEmpIdList(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp = mapper.getEmpByEmpId(1);
        System.out.println(emp);
    }

输出:

在这里插入图片描述



使用resultMap中的association标签



association标签级联查询

association:处理多对一的映射关系。

association标签的结构:

  • property:需要处理多对一的映射关系的属性名
  • javaType:该属性的类型
		<association property="" javaType="" ......>
            <id property="" column=""></id>
            <result property="" column=""></result>
        </association>

其中association的属性还包括很多,下图是association标签中的属性:

在这里插入图片描述

在mapper接口中编写一个新的根据id查询的方法:

	/**
     * 根据id查询员工信息 使用association标签 级联查询
     * @param empId
     * @return
     */
    Emp getEmpByEmpIdTwo(@Param("empId") Integer empId);

在映射文件xml中编写对应的sql:

	<resultMap id="getEmpAllByEmpIdTwo" type="Emp">
        <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="Dept" >
            <id  column="dept_id" property="deptId"></id>
            <result  column="dept_name" property="deptName"></result>
        </association>
    </resultMap>
    <!--Emp getEmpByEmpIdTwo(@Param("empId") Integer empId);-->
    <select id="getEmpByEmpIdTwo" resultMap="getEmpAllByEmpIdTwo">
        select
            t_emp.*,t_dept.*
        from t_emp
                 inner join t_dept
                            on t_emp.dept_id = t_dept.dept_id
        where t_emp.emp_id = #{empId}
    </select>

测试用例:

	@Test
    public void testGetEmpByEmpIdListTwo(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp = mapper.getEmpByEmpIdTwo(1);
        System.out.println(emp);
    }

输出:

在这里插入图片描述



association标签分步查询

分步查询,涉及到association标签的另外两个属性:

  • select:设置分布查询的sql的唯一标识(namespace.SQLId或mapper接口的全类名.方法名)
  • column:设置分步查询的条件

同样编写一个新的mapper接口中的方法,来进行测试:

	 /**
     * 根据id查询员工信息 使用association标签 分步查询
     * @param empId
     * @return
     */
    Emp getEmpByEmpIdThree(@Param("empId") Integer empId);

在映射文件xml中编写对应的sql:

	<resultMap id="getEmpAndDeptByStepMap" type="Emp">
        <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  column="dept_id" property="dept" select="com.gothic.sunset.mapper.EmpMapper.getEmpAndDeptByStepTwo" >
        </association>
    </resultMap>
    <!-- Emp getEmpAndDeptByStepOne(@Param("empId") Integer empId);-->
    <select id="getEmpAndDeptByStepOne" resultMap="getEmpAndDeptByStepMap">
        select * from t_emp where emp_id = #{empId}
    </select>


    <resultMap id="getEmpAndDeptByStepTwoMap" type="Dept">
        <id column="dept_id" property="deptId"></id>
        <result column="dept_name" property="deptName"></result>
    </resultMap>
    <!--Dept getEmpAndDeptByStepTwo(@Param("deptId") Integer deptId);-->
    <select id="getEmpAndDeptByStepTwo" resultMap="getEmpAndDeptByStepTwoMap">
        select * from t_dept where dept_id = #{deptId}
    </select>

测试用例:

	@Test
    public void getEmpAndDeptByStep(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        Emp emp = mapper.getEmpAndDeptByStepOne(1);
        System.out.println(emp);
    }

输出:

在这里插入图片描述



一对多的映射处理



resultMap中的collection标签


collection:


collection:用来处理一对多的映射关系

  • property:需要处理一对多的映射关系的属性名
  • ofType:表示该属性对应的集合中存储的数据的类型

    其完整的参数列表:

    在这里插入图片描述


需求:根据部门id查询部门中的所有员工。



collection标签级联查询

首先,在mapper接口中编写对应的方法:

这里,根据id查询部门表返回所有的员工及部门信息,返回值应该是一个list而且其泛型要遵循Dept类的约束(思考一下就会明白,为什么不是遵循Emp类的约束)。

 	/**
     * 根据部门id查询所有员工信息
     * @param deptId
     * @return
     */
    List<Dept> getDeptAndEmpById(Integer deptId);

在映射文件xml中编写对应的sql:

	<resultMap id="getDeptAndEmpMap" type="Dept">
        <id property="deptId" column="dept_id"></id>
        <result property="deptName" column="dept_name"></result>
        <collection property="emps" ofType="Emp">
            <id property="empId" column="emp_id"></id>
            <result property="empName" column="emp_name"></result>
            <result property="age" column="age"></result>
            <result property="sex" column="sex"></result>
            <result property="email" column="email"></result>
        </collection>
    </resultMap>
    <!--List<Dept> getDeptAndEmpById(Integer deptId);-->
    <select id="getDeptAndEmpById" resultMap="getDeptAndEmpMap">
        select * from t_dept left join t_emp on t_dept.dept_id = t_emp.emp_id where t_dept.dept_id = #{dept_id}
    </select>

测试用例:

	@Test
    public void getDeptAndEmpById(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
        List<Dept> emps = mapper.getDeptAndEmpById(1);
        emps.forEach(System.out::println);
    }

输出:

在这里插入图片描述



collection标签分步查询

分步查询可以分为两步:

  • 分步查询第一步:查询部门信息
  • 分步查询第二步:根据部门id查询部门中的所有员工

    在mapper接口中编写对应的新方法:
 	/**
     * 通过分步查询,查询部门及对应的所有员工信息
     * 分步查询第一步:查询部门信息
     * @param deptId
     * @return
     */
    Dept getDeptAndEmpByStepOne(@Param("deptId") Integer deptId);

    /**
     * 通过分步查询,查询部门及对应的所有员工信息
     * 分步查询第二步:根据部门id查询部门中的所有员工
     * @param empId
     * @return
     */
    List<Emp> getDeptAndEmpByStepTwo(@Param("empId") Integer empId);

在映射文件xml中编写对应的sql:

<resultMap id="getDeptAndEmpByStepOneMap" type="Dept">
        <id property="deptId" column="dept_id"></id>
        <result property="deptName" column="dept_name"></result>
        <collection property="emps"
                    select="com.gothic.sunset.mapper.DeptMapper.getDeptAndEmpByStepTwo"
                    column="dept_id">
        </collection>
    </resultMap>
    <!--Dept getDeptAndEmpByStepOne(@Param("deptId") Integer deptId);-->
    <select id="getDeptAndEmpByStepOne" resultMap="getDeptAndEmpByStepOneMap">
        select * from t_dept where dept_id = #{deptId}
    </select>

    <!--List<Emp> getDeptAndEmpByStepTwo(@Param("empId") Integer empId);-->
    <select id="getDeptAndEmpByStepTwo" resultType="Emp">
        select * from t_emp where emp_id = #{empId}
    </select>

测试用例:

这里因为我是恰好部门id为1的员工只有一条数据,所以就这样写了。大家如果单纯为了查看是否正常可以不输出,从DEBUG中就可以看到。

	@Test
    public void getDeptAndEmpByStrp(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
        Dept emps =  mapper.getDeptAndEmpByStepOne(1);
        System.out.println(emps);
    }

输出:

在这里插入图片描述



补充延迟加载

分步查询的优点:可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息:

  • lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
  • aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载

    此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType=“lazy(延迟加载)|eager(立即加载)”.
<settings>
	<!--开启延迟加载-->
	<setting name="lazyLoadingEnabled" value="true"/>
</settings>


fetchType:当开启了全局的延迟加载之后,可以通过该属性手动控制延迟加载的效果,fetchType=”lazy(延迟加载)|eager(立即加载)

,它作用于collection和association标签上。

可以自行思考一下,开启延迟加载后的状况。

今天的复习结束啦!!!!

在这里插入图片描述



版权声明:本文为m0_63622279原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。