MyBatis的多表查询

随着学习的进步,需求的提高,我们在实际开发中用的最多的还是多表查询,就让我们一起学习MyBatis中的多表查询。

数据库准备

Class表

Student表

项目结构

这次使用的是Spring+MyBatis整合的,具体的规范我也不是很清楚,所以并不清楚项目结构是否规范,最终项目结构以实际需求大纲为主。

  • Dao层

    • ClazzMapper、StudentMapper作为MyBatis的*Mapper.xml的接口
    • Tool为提供Service层服务的接口,ToolImpl为该接口的实现类
  • POJO

    • 两个表的实体类,并根据业务需求添加了额外的属性。

并不知道需不需要Tool这个结构,学习阶段没有去实际开发过项目,自己感觉需要吧。。。为service提供服务(假设是个web项目)。


我们需要用MyBatis实现多表查询的方法主要有两种:业务代码实现、SQL语句实现

多表查询一般有几种关系?多表查询分为3种关系:

  • 一对一
  • 一对多
  • 多对多

我们在这里不讨论多对多,因为需要第三方表才能实现。我们讨论前两种!

业务需求:

  • 查询所有学生对应的班级信息(一对一)
  • 查询每个班级中所在学生的信息(一对多)

1.业务代码实现

业务代码实现分两种:一种是使用resultType,另一种是使用resultMap,其实这两者说的是Mapper.xml文件中的操作语句的标签属性名。

1.1 使用resultType实现业务代码的多表查询:

其实在我们学习MyBatis的时候resultType我们经常使用到(如果忘记了可以百度),其作用就是表明查询语句中使用什么数据类型来接收查询到的数据。

什么是业务代码实现MyBatis的多表查询?

业务代码就是用代码实现的查询,有时候多表查询并不一定是需要外键连接,两张表并没有外交连接,但是需求可能就需要结合两张表,我们可以用代码去控制查询时表与表之间的关系。具体怎么操作呢?假如我们现在要实现第一个业务需求(一对一),先写两条查询语句:

  <!-- 根据cid条件查询t_class的信息 -->
<select id="findClass" resultType="clazz">
select * from t_class where cid = #{parma1}
</select> <!-- 查询t_student的所有信息 -->
<select id="findAll" resultType="student">
select * from t_student
</select>

我们可以先将所有学生信息查询出来,因为t_student的croom就t_class表中的cid字段名,然后再用for循环将每个学生的croom作为条件,查询croom对应的班级信息,再存入Student的Clazz属性,最后输出即可。

@Override
public List<Student> findAll() {
List<Student> all = studentMapper.findAll();
for (Student s:all){
Clazz aClass = clazzMapper.findClass(s.getcRoom());
s.setLi(aClass);
}
return all;
}

这样我们就实现了业务代码的多表查询,特点是没有用到连接查询,我们每个查询语句都是单表查询,但是我们利用了java业务代码结合查询语句实现了多表查询。

输出结果:

- ==>  Preparing: select * from t_student
- ==> Parameters:
- <== Total: 7
- ==> Preparing: select * from t_class where cid = ?
- ==> Parameters: 4(Integer)
- <== Total: 1
- ==> Preparing: select * from t_class where cid = ?
- ==> Parameters: 4(Integer)
- <== Total: 1
- ==> Preparing: select * from t_class where cid = ?
- ==> Parameters: 5(Integer)
- <== Total: 1
- ==> Preparing: select * from t_class where cid = ?
- ==> Parameters: 1(Integer)
- <== Total: 1
- ==> Preparing: select * from t_class where cid = ?
- ==> Parameters: 2(Integer)
- <== Total: 1
- ==> Preparing: select * from t_class where cid = ?
- ==> Parameters: 3(Integer)
- <== Total: 1
- ==> Preparing: select * from t_class where cid = ?
- ==> Parameters: 3(Integer)
- <== Total: 1
Student{sid=2, sName='zhangsan', sex='男', age=21, cRoom=4, li=Clazz{cid=4, cName='JAVA005', room='r504', li=null}}
Student{sid=4, sName='lisi', sex='男', age=23, cRoom=4, li=Clazz{cid=4, cName='JAVA005', room='r504', li=null}}
Student{sid=5, sName='lisi', sex='男', age=21, cRoom=5, li=Clazz{cid=5, cName='PYTHON001', room='r505', li=null}}
Student{sid=6, sName='wangwu', sex='男', age=35, cRoom=1, li=Clazz{cid=1, cName='JAVA002', room='r501', li=null}}
Student{sid=7, sName='zhaoliu', sex='男', age=45, cRoom=2, li=Clazz{cid=2, cName='JAVA003', room='r502', li=null}}
Student{sid=8, sName='zhaoliu', sex='男', age=18, cRoom=3, li=Clazz{cid=3, cName='JAVA004', room='r503', li=null}}
Student{sid=9, sName='tome', sex='男', age=33, cRoom=3, li=Clazz{cid=3, cName='JAVA004', room='r503', li=null}}

我们可以看到日志输出一共有7+1(8)条查询记录。这种方法我们是通过用java代码实现两张表的关系,所以执行的sql语句有8条

1.2 使用resultMap实现多表查询:

<!-- 使用resultMap实现多表查询 -->
<select id="findAll" resultMap="rm1">
select * from t_student
</select> <!-- 根据cid条件查询t_class的信息(namespace:com.lyl.dao.ClazzMapper) -->
<select id="findClass" resultType="clazz">
select * from t_class where cid = #{parma1}
</select> <!-- student:接收的数据类型 -->
<resultMap id="rm1" type="student">
<!-- id为该表的主键,需要指出 -->
<id column="sid" property="id"/>
<!--
result标签:column属性表示字段名,property属性表示实体类的属性名。
因为我们使用的sql语句是单表查询查询出的字段名,MyBatis会自动帮我们赋值,
前提是我们实体类的属性名要和表的字段名一一对应,否则就必须手动指明赋值
-->
<result column="sName" property="sName"/>
<result column="sex" property="sex"/>
<result column="age" property="age"/>
<result column="cRoom" property="cRoom"/>
<!--
association标签:当属性类型是单个对象,我们就需要使用该标签来将数据传递给实体类。
select属性:执行的sql语句,Mapper.xml的namespace加上id
column属性:数据表的列名或者标签别名,比如我们传递t_student的croom的值给select的sql语句,MyBatis就会将t_student指定的字段名的值传递给select的sql语句。
我们也可以理解为调用了select的sql语句,croom作为参数传递给:
select * from t_class where cid = #{parma1} 注意:column属性赋值时应该和最终查询到的结果集中的列名对应!!! javaType属性:指定select的sql语句执行完毕返回的结果集的数据类型(clazz)
property属性:将最终处理后的结果传递给指定的实体类属性
-->
<association select="com.lyl.dao.ClazzMapper.findClass" column="cRoom" javaType="clazz" property="li"/>
</resultMap>

业务代码:

@Override
public List<Student> findAll() {
return studentMapper.findAll();
}

查看日志输出结果:

- ==>  Preparing: select * from t_student
- ==> Parameters:
- ====> Preparing: select * from t_class where cid = ?
- ====> Parameters: 4(Integer)
- <==== Total: 1
- ====> Preparing: select * from t_class where cid = ?
- ====> Parameters: 5(Integer)
- <==== Total: 1
- ====> Preparing: select * from t_class where cid = ?
- ====> Parameters: 1(Integer)
- <==== Total: 1
- ====> Preparing: select * from t_class where cid = ?
- ====> Parameters: 2(Integer)
- <==== Total: 1
- ====> Preparing: select * from t_class where cid = ?
- ====> Parameters: 3(Integer)
- <==== Total: 1
- <== Total: 7
Student{sid=2, sName='zhangsan', sex='男', age=21, cRoom=4, li=Clazz{cid=4, cName='JAVA005', room='r504', li=null}}
Student{sid=4, sName='lisi', sex='男', age=23, cRoom=4, li=Clazz{cid=4, cName='JAVA005', room='r504', li=null}}
Student{sid=5, sName='lisi', sex='男', age=21, cRoom=5, li=Clazz{cid=5, cName='PYTHON001', room='r505', li=null}}
Student{sid=6, sName='wangwu', sex='男', age=35, cRoom=1, li=Clazz{cid=1, cName='JAVA002', room='r501', li=null}}
Student{sid=7, sName='zhaoliu', sex='男', age=45, cRoom=2, li=Clazz{cid=2, cName='JAVA003', room='r502', li=null}}
Student{sid=8, sName='zhaoliu', sex='男', age=18, cRoom=3, li=Clazz{cid=3, cName='JAVA004', room='r503', li=null}}
Student{sid=9, sName='tome', sex='男', age=33, cRoom=3, li=Clazz{cid=3, cName='JAVA004', room='r503', li=null}}

结果是执行的查询记录有7+1(8)条。我们这次并没有用上面的业务代码实现多表查询,而是借助了MyBatis的标签——resultMap实现了多表查询。resultMap标签的属性介绍都写在了Mapper.xml文件中。

结合上诉两种方式小结:

  • 两种方式查询的结果是一样的,并且都查询了7+1条语句,7条执行班级的条件查询,1条执行学生的所有查询,简称:"N+1"方式的多表查询,先查询出某个表的全部信息,根据这个表的信息查询出另一个表的信息。
  • 两者都是业务代码实现多表查询,总的来说就是将平常的连接查询语句拆成了多个单表查询,用来实现,虽然业务代码多了一点,但是相对于用一条SQL执行多表查询来说某种程度降低了难度,比如:用一条SQL实现多表查询,如果需求很复杂,这个时候我们可以将其部分拆解为单表查询结合起来,只要适当合理,不仅仅可以降低难度,也可以方便维护。
  • 区分resultMap和resultType的区别:
    • resultType:很简单,就是定义返回结果值的类型
    • resultMap:推荐还是看官方文档吧,我才学疏浅总结的不好怕带歪,总的来说resultMap也是MyBatis的很重要的核心之一,因为官方就是这样形容的:resultMap元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作

      上面这两种就是使用业务装配的方式实现了多表查询(多个单表查询实现多表查询)

2.SQL语句实现

既然是多表查询,那么肯定就有用一条SQL语句查询实现多表查询的。

直接上代码:


<select id="findStu1" resultMap="rm3">
select * from t_student s join t_class c on s.croom = c.cid
</select> <resultMap id="rm3" type="student">
<!-- 将数据库的字段名中的值赋值给实体类的属性 -->
<id column="sid" property="id"/>
<result column="sName" property="sName"/>
<result column="sex" property="sex"/>
<result column="age" property="age"/>
<result column="cRoom" property="cRoom"/>
<!--
单个对象(association)赋值:
property:实体类中对象的属性名
javaType:要赋值的单个对象的类型
此时association不再有select标签,而是findStu1的sql语句已经将所有结果查询出来,我们只要进行一个自定义赋值即可,手动将需要的数据从resultMap中拿出赋值给实体类的属性
--> <!--
将数据数据赋值给clazz对象,并将结果集赋值给Student实体类的li属性
-->
<association property="li" javaType="clazz">
<id column="cid" property="cid"/>
<result column="cName" property="cName"/>
<result column="room" property="room"/>
</association>
</resultMap>

再来看看我们调用的结果:

- ==>  Preparing: select * from t_student s join t_class c on s.croom = c.cid
- ==> Parameters:
- <== Total: 7
Student{sid=2, sName='zhangsan', sex='男', age=21, cRoom=4, li=Clazz{cid=4, cName='JAVA005', room='r504', li=null}}
Student{sid=4, sName='lisi', sex='男', age=23, cRoom=4, li=Clazz{cid=4, cName='JAVA005', room='r504', li=null}}
Student{sid=5, sName='lisi', sex='男', age=21, cRoom=5, li=Clazz{cid=5, cName='PYTHON001', room='r505', li=null}}
Student{sid=6, sName='wangwu', sex='男', age=35, cRoom=1, li=Clazz{cid=1, cName='JAVA002', room='r501', li=null}}
Student{sid=7, sName='zhaoliu', sex='男', age=45, cRoom=2, li=Clazz{cid=2, cName='JAVA003', room='r502', li=null}}
Student{sid=8, sName='zhaoliu', sex='男', age=18, cRoom=3, li=Clazz{cid=3, cName='JAVA004', room='r503', li=null}}
Student{sid=9, sName='tome', sex='男', age=33, cRoom=3, li=Clazz{cid=3, cName='JAVA004', room='r503', li=null}}

可以看到结果是一样的,但是执行的sql语句只有一句。这就是用SQL语句实现多表查询,特点就是用了连接查询。用的依然是resultMap,可以看出resultMap有多重要了!

总结:

  1. 上诉介绍了3中不同的方式实现多表查询,多数使用第二种和第三种,甚至可能搭配使用。
  2. 在第二种方式中如果实体类的属性名与查询结果最终的字段名相同,MyBatis可以帮我们自动映射到实体类中,如果查询时设置了别名,就必须用result标签手动指定赋值。
  3. 第三章方式中就不能省略result标签,你想从resultMap取什么值就必须用result标签表明,如果省略不写,MyBatis不会自动帮我们装配,所以我们必须指明。注意:如果两张表出现了相同字段名,我们必须在SQL语句中使用别名将他们区分,否则MyBatis会以靠前的字段名数据赋值。
  4. 既然单个对象赋值字段名是association,那么如果是一对多中的集合对象呢?我们怎么做?这个时候我们就需要换一个标签——collection,并且将javaType属性换成ofType属性名即可,ofType的属性我们可以看作是集合的泛型类型,其他的用法一样,我们这一点必须要区分取开来。
  5. 如果我们使用了resultMap,我们就要将select中的resultType换成resultMap,且两者不能共存。

更多的我们应该结合官方博客的部分参考来帮助我们学习理解!

MyBatis的多表查询笔记的更多相关文章

  1. 使用Mybatis进行连表查询、left join---https://blog.csdn.net/jinzhencs/article/details/51980518

    使用Mybatis进行连表查询.left join https://blog.csdn.net/jinzhencs/article/details/51980518

  2. SpringBoot集成Mybatis实现多表查询的两种方式(基于xml)

     下面将在用户和账户进行一对一查询的基础上进行介绍SpringBoot集成Mybatis实现多表查询的基于xml的两种方式.   首先我们先创建两个数据库表,分别是user用户表和account账户表 ...

  3. MyBatis实现关联表查询

    一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数据 创建一张教师表和班级表,这里我们假设一个老师只负责教一个班,那么老师和班级之间的关系就是一种一对一的关 ...

  4. MyBatis——实现关联表查询

    原文:http://www.cnblogs.com/xdp-gacl/p/4264440.html 一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数据 创 ...

  5. Mybatis系列(三):Mybatis实现关联表查询

    原文链接:http://www.cnblogs.com/xdp-gacl/p/4264440.html 一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数据 ...

  6. MyBatis—实现关联表查询

    一.一对一关联 1.1.提出需求 根据班级id查询班级信息(带老师的信息) 1.2.创建表和数据 创建一张教师表和班级表,这里我们假设一个老师只负责教一个班,那么老师和班级之间的关系就是一种一对一的关 ...

  7. 07 Mybatis的多表查询1----1对多和多对1---@Results注解用法总结

    1.表与表之间的关系及其举例 表之间的关系有4种:一对多.多对一.一对一.多对多. 举例: (1)用户和订单就是一对多 一个用户可以下多个订单 (2)订单和用户就是多对一 多个订单属于同一个用户 (3 ...

  8. mybatis之联表查询

    今天碰到了一个问题,就是要在三张表里面各取一部分数据然后组成一个list传到前台页面显示.但是并不想在后台做太多判断,(因为涉及到for循环)会拉慢运行速度.正好用的框架是spring+springM ...

  9. MyBatis框架——多表查询

    MyBatis多表查询, 从表中映射主表,使用 association 标签,通过设置 javaType 属性关联实体类: 主表映射从表,使用 collection 标签,通过 ofType 属性关联 ...

随机推荐

  1. 使用vue实现简单的待办事项

    待办事项 效果图 目录结构 详细代码 AddNew.vue <template> <div> <input v-model="content"/> ...

  2. 3个月零基础入门Python+数据分析,详细时间表+计划表分享

    ​大家好,我是白云. 今天想给大家分享的是三个月零基础入门数据分析学习计划.有小伙伴可能会说,英语好像有点不太好,要怎么办?所以今天我给大家分享的资源呢就是对国内的小伙伴很友好,还附赠大家一份三个月学 ...

  3. Vulhub-DC-4靶场

    Vulhub-DC-4靶场 前言 这套靶场的亮点在于对hydra的运用比较多,在遇到大容量字典的时候,BurpSuite可能会因为设置的运行内存的限制,导致字典需要花很长时间导入进去,虽然通过修改配置 ...

  4. netty系列之:对聊天进行加密

    目录 简介 PKI标准 各类证书的后缀和转换 netty中启动SSL server netty中启动SSL client 总结 简介 在之前的文章中,我们讲到了怎么使用netty建立聊天室,但是这样的 ...

  5. Speed up Downloading Files on Linux

    Compared aria2c, axel and wget, aria2c is the best. It support multi-thread download (with "-s ...

  6. Redis如何实现分布式锁

    今天我们来聊一聊分布式锁的那些事. 相信大家对锁已经不陌生了,我们在多线程环境中,如果需要对同一个资源进行操作,为了避免数据不一致,我们需要在操作共享资源之前进行加锁操作.在计算机科学中,锁(lock ...

  7. 从跨域与同源策略谈CSRF防御与绕过

    之前偶然看到群里有小伙汁问这个token相关的问题,当时我酝酿了一下子,没想好怎么总结,今天来说一下 CSRF在过去还属于OWASP TOP10 ,现在已经不是了(补充一点:关于OWASP API 请 ...

  8. idea自定义 tags 删除

    idea custom tags 添加后 如何去除 如何去除 custom tags 随便@一些字符串,这时候alt+enter弹出 Add xxx to custom tags, 这时候按有方向键进 ...

  9. 10BASE—T的主要技术特性

    1)数据传输速率10Mbps基带传输 2)每段双绞线最大长度100m 3)一条通路允许连接HUB数4个,最多5段传输介质 4)拓扑结构星型 5)访问控制方式CSMA/CD 6)帧长度可变,最大1518 ...

  10. Docker创建Gitea(git服务)

    背景 Gitea是流行的自托管Git服务Gogs的社区分支.gogs作者想一个人维护gogs,但是大家想一起维护.就把gogs项目fork了. 下面是gitea的介绍: https://blog.gi ...