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

简介: 文章介绍了MyBatis中高级查询的一对多和多对一映射处理,包括创建数据库表、抽象对应的实体类、使用resultMap中的association和collection标签进行映射处理,以及如何实现级联查询和分步查询。此外,还补充了延迟加载的设置和用法。

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

创建数据库表 员工和部门

员工表 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标签上。
可以自行思考一下,开启延迟加载后的状况。
今天的复习结束啦!!!!
在这里插入图片描述

相关文章
|
10天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
6天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2506 14
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
6天前
|
机器学习/深度学习 算法 数据可视化
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
2024年中国研究生数学建模竞赛C题聚焦磁性元件磁芯损耗建模。题目背景介绍了电能变换技术的发展与应用,强调磁性元件在功率变换器中的重要性。磁芯损耗受多种因素影响,现有模型难以精确预测。题目要求通过数据分析建立高精度磁芯损耗模型。具体任务包括励磁波形分类、修正斯坦麦茨方程、分析影响因素、构建预测模型及优化设计条件。涉及数据预处理、特征提取、机器学习及优化算法等技术。适合电气、材料、计算机等多个专业学生参与。
1519 14
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
|
8天前
|
编解码 JSON 自然语言处理
通义千问重磅开源Qwen2.5,性能超越Llama
击败Meta,阿里Qwen2.5再登全球开源大模型王座
531 13
|
1月前
|
运维 Cloud Native Devops
一线实战:运维人少,我们从 0 到 1 实践 DevOps 和云原生
上海经证科技有限公司为有效推进软件项目管理和开发工作,选择了阿里云云效作为 DevOps 解决方案。通过云效,实现了从 0 开始,到现在近百个微服务、数百条流水线与应用交付的全面覆盖,有效支撑了敏捷开发流程。
19282 30
|
1月前
|
人工智能 自然语言处理 搜索推荐
阿里云Elasticsearch AI搜索实践
本文介绍了阿里云 Elasticsearch 在AI 搜索方面的技术实践与探索。
18836 20
|
1月前
|
Rust Apache 对象存储
Apache Paimon V0.9最新进展
Apache Paimon V0.9 版本即将发布,此版本带来了多项新特性并解决了关键挑战。Paimon自2022年从Flink社区诞生以来迅速成长,已成为Apache顶级项目,并广泛应用于阿里集团内外的多家企业。
17524 13
Apache Paimon V0.9最新进展
|
8天前
|
人工智能 自动驾驶 机器人
吴泳铭:AI最大的想象力不在手机屏幕,而是改变物理世界
过去22个月,AI发展速度超过任何历史时期,但我们依然还处于AGI变革的早期。生成式AI最大的想象力,绝不是在手机屏幕上做一两个新的超级app,而是接管数字世界,改变物理世界。
458 48
吴泳铭:AI最大的想象力不在手机屏幕,而是改变物理世界
|
1天前
|
云安全 存储 运维
叮咚!您有一份六大必做安全操作清单,请查收
云安全态势管理(CSPM)开启免费试用
355 4
叮咚!您有一份六大必做安全操作清单,请查收
|
2天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。