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. idea创建普通Web项目lib目录无法输出,tomcat不报错问题

    idea创建普通Web项目lib目录无法输出,tomcat不报错问题 idea版本:2021.2 tomcat版本:9.0.50 项目结构 创建一个普普通通的web项目,目录结构大概就是这样 . ├─ ...

  2. WordPress如何配置邮件发送?

    WordPress配置了邮件发送最直接的用处就是可以通过邮件找回密码,当然还有其他的用处,比如Wordpress有新用户注册,订单状态.评论等发生变化时给管理员发送邮件提醒等. 经过大量用户实践反馈, ...

  3. spring框架的学习->从零开始学JAVA系列

    目录 Spring框架的学习 框架的概念 框架的使用 Spring框架的引入 概念 作用 内容 SpringIOC的学习 概念 作用 基本使用流程 SpringIOC创建对象的三种方式 通过构造器方式 ...

  4. 大厂Android岗高频面试问题:说说你对Zygote的理解!

    前言 Zygote可以说是Android开发面试很高频的一道问题,但总有小伙伴在回答这道问题总不能让面试满意, 在这你就要搞清楚面试问你对Zygote的理解时,面试官最想听到的和其实想问的应该是哪些? ...

  5. csaw2013reversing2 writeup

    csaw2013reversing2 writeup 1.程序分析 题目是一个exe文件,提示运行即可拿到flag,但是窗口弹出之后会出现一堆乱码.这时候,我们把文件丢入IDA之中,看到程序大致流程如 ...

  6. 跟你说个笑话,硕士毕业两年,月薪10k,天天面向CV编程

    "枯燥乏味的一天,又tm要开始了". 早上10:00,程序员毛毛带着路上买的早餐,打开24英寸的显示屏,去某论坛查一下昨天没有解决的bug. 9 个小时增删改查.搬砖写代码的一天又 ...

  7. Spring源码阅读-BeanFactory体系结构分析

    BeanFactory是Spring中非常重要的一个类,搞懂了它,你就知道了bean的初始化和摧毁过程,对于深入理解IOC有很大的帮助. BeanFactory体系结构 首先看一下使用IDEA生成的继 ...

  8. Activity与Service生命周期

    一. Activity 先展示一张Activity的生命周期图: 1.1 Activity状态 只有下面三个状态是静态的,可以存在较长的时间内保持状态不变.(其它状态只是过渡状态,系统快速执行并切换到 ...

  9. 网络游戏逆向分析-4-分析喊话call参数来源

    网络游戏逆向分析-4-分析喊话call参数来源 好久没更新了,去实习去了,大家见谅一下. 前面找到了喊话功能call函数,然后分析了它的参数有五个,分别的四个push的和一个ecx: 第一次edx = ...

  10. 将数组对象相同key的内容合并

    function fireDuplicate (arr) { var arr = JSON.parse(JSON.stringify(arr)) var ids = [] arr.forEach(fu ...