在关系型数据库中,我们经常要处理一对一 、 一对多的关系 。 例如, 一辆汽车需要有一个引擎,这是一对一的
关系。 一辆汽车有 4 个或更多个轮子,这是一对多的关系 。关联元素就是专门用来处理关联关系的。


关联元素 描述
association 一对一关系
collection 一对多关系
discriminator 鉴别器映射

这三个元素主要在我们 resultMap 标志之中配置,之前我们在 基于XML的Mapper配置 —— resultType 和 resultMap 中介绍 resultMap 的属性时,就已有列举,这里我们看看如何来使用。

MyBatis关联查询


我们在一对一、一对多的关系中,使用关联查询时,主要有如下两种方法,一种是嵌套结果,一种是嵌套查询,嵌套结果可以简单的理解就是用一条SQL语句来解决;嵌套查询就是指先查询一张表,再根据得到的信息再去查询,分为两条SQL语句去执行。

关联关系 描述
嵌套结果 使用嵌套结果映射来处理重复的联合结果的子集
嵌套查询 通过执行另外一个 SQL 映射语句来返回预期的复杂类型



一对一关系

现在我们先在数据库加一张 class 表,另外在原来的 user 表中添加一个 class_id 字段,user 表和 class 表是一对一关系的,如下
MyBatis关联查询MyBatis关联查询

这里我们想将查询一个用户的信息,我们可以怎么做呢?我们可以进行单表查询,我们可以先查询出 user 表的信息,然后再通过应用层进行处理,通过查出来的 user 信息,得到其 class_id ,然后再去通过这个 class_id 取得该用户的 class 信息,最后我们就可以在应用层组成一个完整的包括 class 信息的 user 信息了。


那么是不是感觉太麻烦呢?这里阿里巴巴开发手册也有相关说明
MyBatis关联查询


嵌套结果

虽然我们不推荐使用这种,但是在一般的管理系统中,我们还是能够经常使用到的,其性能也不会有太大的影响,我们先来看看一对一关系中的嵌套查询是如何使用的吧。


上述我们所说的一对一关系中,我们会用到 resultMap 中的 association ,我们先来看看这个标签的属性

association 标签嵌套结果方式常用属性:

  • property :对应实体类中的属性名,必填项。
  • javaType : 属性对应的 Java 类型 。
  • resultMap : 可以直接使用现有的 resultMap ,而不需要在这里配置映射关系。
  • columnPrefix :查询列的前缀,配置前缀后,在子标签配置 result 的 column 时可以省略前缀。

然后我们看一下我们的实体类,省略其Getter、Setter方法
MyBatis关联查询MyBatis关联查询


然后我们在 xml 文件中直接用一条SQL语句来查询出我们所需要的结果,我们对两条表中的查出的相同字段,如id,做别名设置。

然后我们先建一个 resultMap ,继承我们主表 User 的 ResultMap ,再在其中做 class 表的映射关系即可。(因为我们会对class表进行别名设置,我们在映射时,column名称,即SQL语句查出来的字段名,每个几乎都要加上class_ 前缀,所以我们可以用 columnPrefix 属性统一设置。)
MyBatis关联查询

另外也可以进行直接映入另外一个 xxxMapper.xml 已有的 resultMap 映射关系,我们继承了 UserMapper的 resultMap,所以我们还需对 class 表中的字段进行映射,这里我们可以把 ClassMapper 中的 已映射好的resultMap 引入。
MyBatis关联查询

执行测试代码,结果如下
MyBatis关联查询
我们可以看到它只执行了一条SQL语句
MyBatis关联查询


嵌套查询

association标签 嵌套查询方式常用属性:

  • select :另 一个映射查询的 id, MyBatis 会额外执行这个查询获取嵌套对象的结果 。
  • column :列名(或别名),将主查询中列的结果作为嵌套查询的参数,配置方式如下:
    column={prop1=col1 , prop2=col2},prop1 和 prop2 将作为嵌套查询的参数。
  • fetchType :数据加载方式,可选值为 lazy 和 eager,分别为延迟加载和积极加载 ,这个配置会覆盖全
    局的 lazyLoadingEnabled 配置。
    MyBatis关联查询
    MyBatis关联查询

因为这里用到了 ClassMapper.xml ,所以我们别忘记了在 MyBatis 配置中加上
MyBatis关联查询

我们再运行测试类,查看其结果,会发现其是通过两条SQL语句完成的。
MyBatis关联查询


但是我们要注意的是嵌套查询中会有一个 “N+1” 的问题,什么意思呢?就是刚刚我们执行了查询 user 表中的信息,结果为一条,但是执行了两条SQL语句,因为查出了结果,结果中会再次查 class 表信息。


那我们我们要是查询 user 表中全部信息,现已知数据库共有四条信息,所以我们执行查询 user 表中的所有信息,会执行一条SQL,查询结果为四条,这四条信息每条都会再去执行一遍查询 class 表的信息,又会执行 4 条SQL,共会执行五条SQL语句,就是主表的查询结果 + 1 的查询,即 “N+1” 问题。

MyBatis关联查询
MyBatis关联查询
我们实现结果发现子查询只执行了三次,这是因为 user 表中有两条信息的 class_id 相同,由于 MyBatis 的缓存原因,没有访问数据库,直接取了其缓存,MyBatis的缓存问题下次再说。



那么我们如何解决 “N+1” 问题呢?这时就需要我们用到刚刚说过的 fetchType 属性。
MyBatis关联查询
MyBatis关联查询

除此之外,我们还需在 MyBatis 的配置文件中的 <settings> 加一些配置,我们在 MyBatis配置 已列举
MyBatis关联查询
其中的 lazyLoadingEnabled 是延迟加载的全局设置,这里我们可以不进行设置,我们如上述,在需要的时候进行特定的设置即可。

另一个 aggressiveLazyLoading 我们就必须进行设置了,我们设置成 false,让其按需加载,让我们需要的时候在进行调用,而不是一次性的加载。
MyBatis关联查询



一对多关系

我们在理解了一对一关系中的嵌套结果及嵌套查询后,那么我们对一对多关系中的嵌套结果及嵌套查询就很好理解了。


这里我们再新增一张用户的 grades 表,与 user 表是一对多关系的
MyBatis关联查询

另外我们会对 User 实体类进行对应的修改,如下
MyBatis关联查询

对于一对多关系中,我们会用到 resultMap 中的 collection ,这个 collection 标签支持的属性以及属性的作用和 association 完全相同。



嵌套结果

我们发现一对多关系中和一对一关系的处理几乎一致。
MyBatis关联查询


嵌套查询

MyBatis关联查询
MyBatis关联查询

因为这里用到了 GradesMapper.xml ,所以我们别忘记了在 MyBatis 配置中加上
MyBatis关联查询



discriminator 鉴别器映射

有时一个单独的数据库查询也许返回很多不同 (但是希望有些关联) 数据类型的结果集。 鉴别器元素就是被设计来处理这个情况的, 还有包括类的继承层次结构。 鉴别器非常容易理 解,因为它的表现很像 Java 语言中的 switch 语句。


比如飞猪APP上的订单,我们用一张表来存储所有的订单,我们可以通过嵌套查询来查询该订单的具体信息,但是这张订单可能是住宿,也可能是美食,也可能是车票等等,这时我们就可以通过订单主表中存储的订单类型,来确定该订单属于哪一类订单,然后通过 discriminator 鉴别器映射去选择去不同的表加载相应的订单详情。


以我们上述例子举一个不怎么恰当的例子,这里我们再创建两个表,男生宿舍和女生宿舍,然后我们根据 user 主表中的性别来,去不同的表中加载宿舍信息。
MyBatis关联查询
MyBatis关联查询MyBatis关联查询


然后我们再看看实体类 User,这里我们可以定义两个字段,一个男生宿舍,一个女生宿舍,但是这里一般建议定义一个字段,然后我们抽出去一个父类出来,如下:
MyBatis关联查询
MyBatis关联查询
MyBatis关联查询
MyBatis关联查询

接下来我们来看看 xml 中是如何使用 discriminator 鉴别器映射
MyBatis关联查询
MyBatis关联查询
MyBatis关联查询

别忘记了在 MyBatis 配置中添加新增的 .xml 文件
MyBatis关联查询

相关文章: