Mybatis - 表关系

目录

前言

为什么要用到resultMap?

建表

配置文件

创建实体类

 创建映射关系

1、使用别名

2、全局配置

3、使用resultMap

二、解决多对一映射关系

查询员工及其所在部门

<1>、级联属性赋值

 <2>、使用标签

<3>、通过association分步查询

*延迟加载

 三、解决一对多的映射问题

查询部门及其属下员工

<1>、单句sql查询

<2>、分步查询


前言

在关系型数据库中,不同表之间通过关联字段(也称为键)建立的关系。这些关系可以用于检索和操作数据,提供了数据的一致性和完整性。

常见的表关系有以下几种:

  1. 一对一关系(One-to-One Relationship):两个表之间的一行数据在另一个表中只有一行对应的关系。这种关系通常用于将多个属性拆分到不同的表中,以降低数据冗余。例如,一个用户表和一个用户详细信息表。

  2. 一对多关系(One-to-Many Relationship):一个表中的一行数据可以对应另一个表中的多行数据的关系。这种关系常用于描述父子关系,例如,一个部门表和一个员工表,一个部门可以有多个员工。

  3. 多对多关系(Many-to-Many Relationship):两个表中的数据可以互相关联多个对应数据的关系。为了表示这种关系,通常需要使用一个连接表(中间表)来存储两个表之间的关联关系。例如,一个学生表和一个课程表,一个学生可以选择多门课程,一门课程也可以被多个学生选择。

我们在之前案例中,一值都是进行的单表操作,我们接下来会演示如何实现多表操作


为什么要用到resultMap?

在MyBatis中,resultMap是用于配置查询结果映射的元素。它定义了如何将数据库查询结果映射到Java对象上。

我们在之前的案例中一直使用的是resultType,它要求字段名与属性名一致,底层会自动建立映射关系,不一致的话就默认赋值null

但是当我们要建立一对多的映射关系时,字段名不一定与属性名一致,我们就要手动建立映射关系了,此时我们使用resultMap

建表

员工表

部门表

配置文件

我们依旧使用之前的配置文件,可以选择使用package,这个与<mappers>里的package作用一样

创建实体类

 

我们发现,数据库字段与实体类属性虽然一一对应,但是命名规则并不相同

数据库字段采用下划线格式emp_name , 其对应的属性采用的驼峰格式empName,

这样的话我们使用以前的resultType形式查询所有就会出现以下结果

因为emp_name  -> empName , resultType就找不到相同命名的字段,就会变成null,接下来我们会有以下方式解决

 创建映射关系

创建mapper接口

1、使用别名

我们在学习mysql的时候就见过,会以起别名的方式来查询

用这种方式让emp_name 变为了

 

2、全局配置

在mybatis的配置文件中添加标签

    <!-- 设置Mybatis的全局配置,如日志,下划线转驼峰等 -->
        <settings>
            <!-- 将_自动映射为驼峰 -->
            <setting name="mapUnderscoreToCamelCase" value="true"/>
        </settings>

要严格按照命名规则 emp_name 与 empName 的格式才会有效

我们此时又可以使用select * 了

测试

3、使用resultMap

resultType的值是我们已经定义好的返回值的实体类,而resultMap中我们要设置字段和属性的映射

注:虽然我们在resultMap中并没有设置其余名字相同字段的映射关系也可以正常运行,但是尽量还是要写上,我不写是因为偷懒


二、解决多对一映射关系

查询员工及其所在部门

<1>、级联属性赋值

我们已经解决了表字段与表属性不一致的问题,现在我们可以实现一对多和多对一的表关系了

为emp实体类新增字段

映射器

我们的员工表中只有部门id,但是却没有部门表中的信息,所以我们单纯的只从单表中查询是无法达到我们想要的效果的

此时我们需要使用到内连接

  1. INNER JOIN(内连接): INNER JOIN 返回两个表中满足连接条件的行。只返回两个表中共同符合条件的数据。

  2. LEFT JOIN(左连接): LEFT JOIN 返回左表中的所有行,以及满足连接条件的右表中的行。如果右表中没有匹配的行,则返回 NULL 值。

  3. RIGHT JOIN(右连接): RIGHT JOIN 返回右表中的所有行,以及满足连接条件的左表中的行。如果左表中没有匹配的行,则返回 NULL 值。

select * from t_emp left join t_dept 
    on t_emp.did = t_dept.did where t_emp.eid = 1

但是此时我们想要创建resultMap映射关系,此时就不能直接<result property="dept" column="did"/>

因为dept是对象,而did是int,所以我们应该这样

 爆红是因为我用了MybatisX的插件,其实并没有出错,底下<select>标签左边的小鸟就是MybatisX插件的效果,点击小鸟就会自动跳转到对应的mapper接口去

测试

成功查询 

 <2>、使用<association>标签

<association>是专门用来处理多对一映射关系的标签,property属性就是就是属性名,javaType是其对应的实体类

同样使用刚才的步骤

测试

<3>、通过association分步查询

以上两种方式均是通过单条sql来实现多表查询,我们现在使用分步查询(其实就是嵌套子查询),进行两次sql查询来获取结果

步骤一、查询员工

我们在empMapper中定义第一步,查询所有的员工

映射文件中同样使用association,不过新增了两个属性,select是子查询的sql语句,column是数据库员工表中字段的起到外键作用的did

    <resultMap id="empAndDeptByStepResultMap" type="Emp">
        <id property="eid" column="eid"/>
        <result property="empName" column="emp_name"/>
        <result property="age" column="age"/>
        <result property="gender" column="gender"/>
        <result property="email" column="email"/>
        <association property="dept"
                     select="com.wal.mapper.DeptMapper.getEmpAndDeptByStep2"
                     column="did"/>
    </resultMap>

    <select id="getEmpAndDeptByStep1" resultMap="empAndDeptByStepResultMap">
        select * from t_emp where eid = #{eid}
    </select>

步骤二、查询部门 

我们既然是分开进行了查询,那么查询部门表信息一定实在deptMapper里面了

注:这里没有使用resultMap是因为我们在上方的全局配置中设置了下划线转驼峰,可以不用写resultMap手动创建映射,当然了如果你想用resultMap也行

 #{did}的数据来源就是我们在第一步中column的定义的did

测试

问:这种方式有什么好处?

        可以实现延迟加载*。我们由于查询员工和查询部门是分开独立的方法和接口,因此假设我们只是单纯想要查询员工或者查询部门,并不想同时查询,那么就可以直接使用符合要求分步方法。此时我们还实现了按需加载,我们获取的数据是什么,就只会执行相应的sql,

因此,我们还是使用这种方式使用的多

*延迟加载

通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载。

由于Mybatis默认不开启延迟加载,所以我们要在核心文件中设置全局配置信息

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

开启了延迟加载之后,使用fetchType = "lazy"(延迟加载) | "eager"(立即加载)可以手动控制该方法是否延迟加载。注意时开启了延迟加载之后,没开启用不了

    <!-- 设置Mybatis的全局配置,如日志,下划线转驼峰,延迟加载等 -->
        <settings>
            <!-- 将_自动映射为驼峰 -->
            <setting name="mapUnderscoreToCamelCase" value="true"/>
            <!-- 开启延迟加载 -->
            <setting name="lazyLoadingEnabled" value="true"/>
        </settings>

我们现在开启了延迟加载,也就是说,如果我们在后面的数据处理时没有使用到管理部门的属性,那么就不会执行第二步部门

我们来看例子理解这句话

我们现在直接打印emp对象,emp种有一个dept属性,直接打印emp的toString方法会把dept也打印出来,也就是数据处理的时候用到了dept

此时我们看到日志种执行了两个sql语句

我们现在只打印emp的emp_name,没有用到dept的任何数据,那么延迟加载就不会加载第二步使用到dept属性的sql语句

这就是延迟加载


 三、解决一对多的映射问题

我们来对比一下一对多和多对一的区别

表关系多对一一对多
属性实现对象集合
标签association

collection

返回类型标签javaTypeofType

查询部门及其属下员工

<1>、单句sql查询

dept实体类

映射器

映射文件

    <resultMap id="deptResultMap" type="Dept">
        <id property="did" column="did"/>
        <result property="deptName" column="dept_name"/>
        <collection property="emps" ofType="Emp">
            <id property="eid" column="eid"/>
            <result property="empName" column="emp_name"/>
            <result property="age" column="age"/>
            <result property="gender" column="gender"/>
            <result property="email" column="email"/>
        </collection>
    </resultMap>
    
    <select id="getDeptAndEmp" resultMap="deptResultMap">
        select * from t_dept inner join t_emp on t_dept.did = t_emp.did
                 where t_dept.did = #{did}
    </select>

虽然emp中还有外键dept属性,但是我们不再为其赋值,不然就会发生dept查询emp,emp查询dept,其中的dept又查询emp的这种套娃逻辑

测试

<2>、分步查询

步骤一、查询部门

映射器

映射文件

步骤二、查询员工

测试

 成功执行两条sql

测试延迟加载

 仅执行一条sql吗,成功加载

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

An1ong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值