狂神说MyBatis学习笔记: MyBatis中一对多和多对一处理


前言

笔者在学习MyBatis课程中,听了很多慕课,最终发现还是狂神老师讲的最好。本篇是根据狂神老师的 狂神说MyBatis05:一对多和多对一处理
学习完后的整理笔记,结合了狂神老师的讲解和自己的理解,旨在弄通这个知识点。

如果本文中的一些基本概念不了解,比如resultMap,collection,和association这些字段看不懂,建议先去科普一下MyBatis的基本配置:
简单讲解MyBatis中的resultMap,collection,association

一、多对一关系的查询

数据表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存失败,源站可能有防盗链机制,建议将图片保存下来直接上传上传(im4joVN1F1GS-1605707855087)(C:\Users\WZR11\AppData\Roaming\Typora\typora-user-images\image-20201118200332027.png)(C:\Users\WZR11\AppData\Roaming\Typora\typora-user-images\image-20201118200332027.png)]

实体类:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yHxJvWwG-1605707855094)(C:\Users\WZR11\AppData\Roaming\Typora\typora-user-images\image-20201118200402937.png)]

其中 学生和教师是多对一关系, 所以在学生表中有一个外键,指向教师表的主键

虽然在数据表中,外键是int型的(tid), 只负责指向教师表的主键id. 但是在实体类中没有这个int型的外键(tid), 取而代之的直接是一个教师类型的实体类.

这里很蒙是不是? 原因就在于, 从需求角度考虑, 我们想要查出来的结果是下图这个东西:

在这里插入图片描述

现在是不是明白多了? 实体类不一定要完全和数据库中的字段相对应,它是要为展示数据负责的(最终你想要查出来什么结果,实体类就是什么样子的)

对应的mapper文件:

方法一: 嵌套select查询(按照查询进行嵌套处理)

 <!--
   需求:获取所有学生及对应老师的信息
   思路:
       1. 获取所有学生的信息
       2. 根据获取的学生信息的老师ID->获取该老师的信息
       3. 思考问题,这样学生的结果集中应该包含老师,该如何处理呢,数据库中我们一般使用关联查询?
           1. 做一个结果集映射:StudentTeacher
           2. StudentTeacher结果集的类型为 Student
           3. 学生中老师的属性为teacher,对应数据库中为tid。
              多个 [1,...)学生关联一个老师=> 一对一,一对多
           4. 查看官网找到:association – 一个复杂类型的关联;使用它来处理关联查询
   -->
   <select id="getStudents" resultMap="StudentTeacher">
    select * from student
   </select>
   <resultMap id="StudentTeacher" type="Student">
       <!--association关联属性 property属性名 javaType属性类型 column在多的一方的表中的列名-->
       <association property="teacher"  column="tid" javaType="Teacher" select="getTeacher"/>
   </resultMap>
   <!--
   这里传递过来的id,只有一个属性的时候,下面可以写任何值
   association中column多参数配置:
       column="{key=value,key=value}"
       其实就是键值对的形式,key是传给下个sql的取值名称,value是片段一中sql查询的字段名。
   -->
   <select id="getTeacher" resultType="teacher">
      select * from teacher where id = #{tid}
   </select>

观察该方法,为什么叫嵌套select? 因为你看下面这一行有一个select字段:

 <association property="teacher"  column="tid" javaType="Teacher" select="getTeacher"/>

该select字段直接对应了最下面那个id为getTeacher的标签. 这其实是嵌套了另一个标签的操作. 注意看该标签里面的sql语句:

select * from teacher where id = #{tid}

id = #{tid} , 这个tid就是前面association标签里面 column=“tid” 里传过来的tid。

方法二: 按照结果进行嵌套处理

<!--
按查询结果嵌套处理
思路:
   1. 直接查询出结果,进行结果集的映射
-->
<select id="getStudents2" resultMap="StudentTeacher2" >
  select s.id sid, s.name sname , t.name tname
  from student s,teacher t
  where s.tid = t.id
</select>

<resultMap id="StudentTeacher2" type="Student">
   <id property="id" column="sid"/>
   <result property="name" column="sname"/>
   <!--关联对象property 关联对象在Student实体类中的属性-->
   <association property="teacher" javaType="Teacher">
       <result property="name" column="tname"/>
   </association>
</resultMap>

先讲一下sql语句:

select s.id sid, s.name sname , t.name tname
  from student s,teacher t
  where s.tid = t.id

s是student的别名(之前有配),看不懂的话可以直接写student。t就是teacher,看不懂的话换成teacher也行,不用纠结这里,反正s和t就是代表学生表和老师表的意思

s.id后面跟的sid, 其实是在sql里起了一个别名. 后面的sname和tname也是sql语法中的别名

所以, 后面配置resultMap时的column属性, 本来对应的虽说是数据库字段, 但是由于数据库字段都有别名了, 也应该配置别名的形式.

看下面的association代码:

<!--关联对象property 关联对象在Student实体类中的属性-->
   <association property="teacher" javaType="Teacher">
       <result property="name" column="tname"/>
   </association>

这里是不是看不懂? 别急, 看下面最终查询结果是个什么东西, 你就知道结果集映射是配的啥了

在这里插入图片描述

上图就是查询的结果, 其实就是Student的pojo实体类中的三个字段。

这次再来看前面的结果集映射代码,id和name字段都配好了,剩下的就是teacher字段。

按理说这里需要配置teacher的id和name。但是上面的代码中只配置了name属性,

因为我们的需求根本不需要查teacher的id属性,只需要查teacher->name(select s.id sid, s.name sname , t.name tname)。括号里也就是我们的sql语句,从语句中看需求只需要查出来 student的id属性, student的name属性 和 teacher的name属性这三个即可。

这也解释了上图为啥id全都是0(本来秦老师的id是1),因为我们根本就没配id属性。

二、一对多关系的查询

问题来了,前面已经说了多对一 ,那这里为啥要再重新学一遍一对多呢?

看下图,下图是多对一查出来的结果,学生是多的一方,最后查出来的是学生的信息:

在这里插入图片描述

但是如果我们需求是:以老师为主体,查老师的信息呢?

在这里插入图片描述

想实现上面的需求,首先需要改动实体类:

在这里插入图片描述

最终查出来的信息是长这个样的:

在这里插入图片描述

(注意,这里的students是一个List,泛型是<Student>

废话不多说,下面开始介绍如何查询:

其实和前面的多对一查询的两种方式大同小异,都是按照结果进行嵌套处理 或者 按照查询进行嵌套处理

方式一:按照结果进行嵌套处理

  1. TeacherMapper接口编写方法
//获取指定老师,及老师下的所有学生
public Teacher getTeacher(int id);
  1. 编写接口对应的Mapper配置文件
<mapper namespace="com.kuang.mapper.TeacherMapper">

   <!--
   思路:
       1. 从学生表和老师表中查出学生id,学生姓名,老师姓名
       2. 对查询出来的操作做结果集映射
           1. 集合的话,使用collection!
               JavaType和ofType都是用来指定对象类型的
               JavaType是用来指定pojo中属性的类型
               ofType指定的是映射到list集合属性中pojo的类型。
   -->
   <select id="getTeacher" resultMap="TeacherStudent">
      select s.id sid, s.name sname , t.name tname, t.id tid
      from student s,teacher t
      where s.tid = t.id and t.id=#{id}
   </select>

   <resultMap id="TeacherStudent" type="Teacher">
       <result  property="name" column="tname"/>
       <collection property="students" ofType="Student">
           <result property="id" column="sid" />
           <result property="name" column="sname" />
           <result property="tid" column="tid" />
       </collection>
   </resultMap>
</mapper>

解释一下,sql语句的最后一行 where s.tid = t.id and t.id=#{id}
里面的#{id},就是接口传进来的参数public Teacher getTeacher(int id);

方式二:按照查询进行嵌套处理

  1. TeacherMapper接口编写方法
//获取指定老师,及老师下的所有学生
public Teacher getTeacher2(int id);
  1. 编写接口对应的Mapper配置文件
<select id="getTeacher2" resultMap="TeacherStudent2">
select * from teacher where id = #{id}
</select>
<resultMap id="TeacherStudent2" type="Teacher">
   <!--column是一对多的外键 , 写的是一的主键的列名-->
   <collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getStudentByTeacherId"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
  select * from student where tid = #{id}
</select>

注意看这里的javaType字段,写的是ArrayList!因为传进去的返回值类型的确是List类型,而Student类只是List的泛型,泛型要用ofType来传递。

不要小看这里的细节,出错的往往是这些细节!

查询结果

不管是用上面的哪种方法,结果都是长得像下图这个样的,即获取指定老师下的所有学生及老师的信息:

在这里插入图片描述

总结

这里介绍了两种方法查询:

按照查询进行嵌套处理 和 按照结果进行嵌套处理

按照查询进行嵌套处理就像SQL中的子查询

按照结果进行嵌套处理就像SQL中的联表查询

按照结果进行嵌套处理往往要写复杂的SQL语句,按照查询进行嵌套处理往往要进行复杂的映射配置。

笔者比较喜欢按照结果进行嵌套处理,因为配置resultMap的映射本质就是为了解决属性名和字段不一致的问题,把sql语句写完整了,可以得出多种映射的结果,只需要照着配置就完了。况且掌握复杂查询的SQL语句本身就是一个程序猿的基本功。

最后还差一个多对多关系的处理的总结,日后再更新。

这个知识点如果学不会,完全就是SQL基础没打好的问题 ,建议重新学一下数据库基础。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值