第 9 章 MyBatis 的关联映射

通过前面几章的学习,大家已经熟悉了 MyBatis 的基本知识,并能够使用 MyBatis 以及面向对象的方式进行数据库操作,但这些操作只是针对单表实现的。 在实际的开发中,对数据库的操作常常会涉及多张表,这在面向对象中就涉及了对象与对象之间的关联关系。 针对多表之间的操作, MyBatis 提供了关联映射,通过关联映射就可以很好地处理对象与对象之间的关联关系。 本章中,将对 MyBatis 的关联关系映射进行详细讲解。

关联关系概述

在关系型数据库中,多表之间存在着三种关联关系,分别为一对一 、 一对多和多对多, 如图所示。



这三种关联关系的具体说明如下。

  • 一对一:在任意一方引入对方主键作为外键。
  • 一对多:在"多"的一方,添加"一"的一方的主键作为外键。
  • 多对多:产生中间关系表,引入两张表的主键作为外键,两个主键成为联合主键或使用新 的宇段作为主键。

通过数据库中的表可以描述数据之间的关系,同样,在 Java 中,通过对象也可以进行关系描述,如图所示。



在图中,三种关联关系的描述如下。

  • 一对一的关系:就是在本类中定义对方类型的对象,如 A 类中定义 B 类类型的属性 b , B 类中定义 A 类类型的属性 a。
  • 一对多的关系:就是一个 A 类类型对应多个 B 类类型的情况,需要在 A 类中以集合的方 式51入 B 类类型的对象,在 B 类中定义 A 类类型的属性 a。
  • 多对多的关系:在 A 类中定义 B 类类型的集合,在 B 类中定义 A 类类型的集合。

以上就是 Java 对象中,三种实体类之间的关联关系,那么使用 MyBatis 是如何处理 Java 对象中的三种关联关系呢?在接下来的 3 个小节中,将对 MyBatis 中的这几种关联关系的使用进行详细讲解。

一对一

在现实生活中,一对一关联关系是十分常见的。 例如,一个人只能有一个身份证,同时一个身份证也只会对应一个人,它们之间的关系模型图,如图所示。



那么使用 MyBatis 是怎么处理图中的这种一对一关联关系的呢?在前面第 7 章所讲解的<resultMap>元素中,包含了一个<association>子元素, MyBatis 就是通过该元素来处理一对 一关联关系的。
在<association>元素中,通常可以配置以下属性。

  • property: 指定映射到的实体类对象属性,与表字段一一对应。
  • column: 指定表中对应的字段。
  • javaType: 指定映射到实体对象属性的类型。
  • select: 指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。
  • fetchType: 指定在关联查询时是否启用延迟加载。 fetchType 属性有 lazy 和 eager 两个属性值,默认值为 lazy (即默认关联映射延迟加载)。
      <!-- 方式一:嵌套查询  -->
      <association property="card" column="card_id" javaType="IdCard"
                   select="com.neuedu.mapper.IdCardMapper.findCardById" />
      <!-- 方式二:嵌套结果  -->
      <association property="card" javaType="IdCard">
          <id property="id" column="card_id"/>
          <result property="code" column="code"/>
      </association>  

<association>元素的使用非常简单,只需要参考如下两种示例配置即可,具体如下。
小提示:MyBatis 在映射文件中加载关联关系对象主要通过两种方式:嵌套查询和嵌套结果。 嵌套查询是指通过执行另外一条 SQL 映射语句来返回预期的复杂类型;嵌套结果是使用嵌套结果映射 来处理重复的联合结果的子集。 开发人员可以使用上述任意一种方式实现对关联关系的加载。
了解了 MyBatis 中处理一对一关联关系的元素和方式后,接下来就以个人和身份证之间的一 对一关联关系为例,进行详细讲解。
查询个人及其关联的身份证信息是先通过查询个人表中的主键来获个人信息,然后通过表中的外键,来获取证件表中的身份证号信息。 其具体实现步骤如下。
( 1 )创建数据表。 在 mybatis 数据库中分别创建名为 tb_idcard 和 tb_person 的数据表,同时预先插入两条数据。 完成上述操作后,数据库 tb_idcard 和 tb_person 表中的数据如图所示。



( 2 )在 Eclipse 中创建一个名为 mybatis03 的 Web 项目,然后引入相关 JAR 包、 log4j 曰志文件、 MybatisUtils 工具类以及 mybatis-config.xml 核心配置文件。 项目环境搭建完成后的文件结构,如图所示。

( 3 )在项目的 com.neuedu.po 包下创建持久化类 IdCard 和 Person ,编辑后的代码,文件如下所示。

package com.neuedu.po;
/**
* 证件持久化类
*/
public class IdCard {
  private Integer id;
  private String code;
  public Integer getId() {
      return id;
  }
  public void setId(Integer id) {
      this.id = id;
  }
  public String getCode() {
      return code;
  }
  public void setCode(String code) {
      this.code = code;
  }
  @Override
  public String toString() {
      return "IdCard [id=" + id + ", code=" + code + "]";
  }
}
package com.neuedu.po;
/**
* 个人持久化类
*/
public class Person {
  private Integer id;
  private String name;
  private Integer age;
  private String sex;
  private IdCard card; //个人关联的证件
  public Integer getId() {
      return id;
  }
  public void setId(Integer id) {
      this.id = id;
  }
  public String getName() {
      return name;
  }
  public void setName(String name) {
      this.name = name;
  }
  public Integer getAge() {
      return age;
  }
  public void setAge(Integer age) {
      this.age = age;
  }
  public String getSex() {
      return sex;
  }
  public void setSex(String sex) {
      this.sex = sex;
  }
  public IdCard getCard() {
      return card;
  }
  public void setCard(IdCard card) {
      this.card = card;
  }
  @Override
  public String toString() {
      return "Person [id=" + id + ", name=" + name + ", age=" + age + ", sex=" + sex + ", card=" + card + "]";
  }
}

在上述两个文件中,分别定义了各自的属性以及对应的 getter/setter 方法,同时为了方便查看输出结果还重写了 toString()方法。
(4 )在 com.neuedu.mapper 包中,创建证件映射文件 IdCardMapper.xml 和个人映射文件 PersonMapper.xml ,并在两个映射文件中编写一对一关联映射查询的配置信息,文件如下所示。

<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE mapper  
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.neuedu.mapper.IdCardMapper">
  <!-- 根据id查询证件信息  -->
  <select id="findCardById" parameterType="Integer" resultType="IdCard">
      select * from tb_idcard where id = #{id}
  </select>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE mapper  
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.neuedu.mapper.PersonMapper">
  <!-- 嵌套资询:通过执行另外一条 SQL 映射语句来返回预期的特殊类型  -->
  <select id="findPersonById" parameterType="Integer" 
          resultMap="IdCardWithPersonResult">
      select * from tb_person where id = #{id}
  </select>
  <resultMap type="Person" id="IdCardWithPersonResult">
      <id property="id" column="id"/>
      <result property="name" column="name"/>
      <result property="age" column="age"/>
      <result property="sex" column="sex"/>
      <!-- 一对一: association 使用 select 属性引入另外一条 SQL 语句   -->
      <association property="card" column="card_id" javaType="IdCard"
                   select="com.neuedu.mapper.IdCardMapper.findCardById" />
  </resultMap>
</mapper>

在上述两个映射文件中,使用了 MyBatis 中的嵌套查询方式进行了个人及其关联的证件信息查询,因为返回的个人对象中除了基本属性外还有一个关联的 card 属性,所以需要手动编写结果映射。 从映射文件 PersonMapper.xml 中可以看出,嵌套查询的方法是先执行一个简单的 SOL 语句,然后在进行结果映射时,将关联对象在<association>元素中使用 select 属性执行另一条 SOL 语句(即 IdCardMapper.xml 中的 SOL)。
( 5 )在核心配置文件 mybatis-config.xml 中,引入 Mapper 映射文件并定义别名,文件如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration 
 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 
 "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <!-- 引入数据库连接配置文件 -->
  <properties resource="db.properties" />
  <!-- 使用扫描包的形式定义别名 -->
  <typeAliases>
      <package name="com.neuedu.po" />
  </typeAliases>
  <!-- 1.配置环境,默认的环境 id为mysql -->
  <environments default="mysql">
      <!-- 1.2.配置id为mysql的数据库环境 -->
      <environment id="mysql">
          <!-- 使用JDBC的事务管理 -->
          <transactionManager type="JDBC" />
          <!-- 数据库连接池 -->
          <dataSource type="POOLED">
              <property name="driver" value="${jdbc.driver}" />
              <property name="url" value="${jdbc.url}" />
              <property name="username" value="${jdbc.username}" />
              <property name="password" value="${jdbc.password}" />
          </dataSource>
      </environment>
  </environments>

  <!-- 2.配置Mapper的位置 -->
  <mappers>
      <mapper resource="com/neuedu/mapper/IdCardMapper.xml" />
      <mapper resource="com/neuedu/mapper/PersonMapper.xml" />
  </mappers>
</configuration>

在上述核心配置文件中,首先引入了数据库连接的配置文件,然后使用扫描包的形式自定义别名,接下来进行环境的配置,最后配置了 Mapper 映射文件的位置信息。
( 6 )在 com.neuedu.test 包中,创建测试类 MybatisAssociatedTest ,并在类中编写测试方法 findPersonByldTest() ,文件如下所示。

package com.neuedu.test;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.neuedu.po.Person;
import com.neuedu.utils.MybatisUtils;
/**
* MyBatis 关联查询映射测试类
*/
public class MybatisAssociatedTest {
  /**
   * 嵌套查询
   */
  @Test
  public void findPersonByldTest(){
      // 1.通过工具类生成SqlSession对象
      SqlSession sqlSession = MybatisUtils.getSession();
      // 2.使用MyBatis 嵌套查询的方式查询id为1的人的信息
      Person person = sqlSession.selectOne("com.neuedu.mapper.PersonMapper.findPersonById",1);
      // 3.输出查询结果信息
      System.out.println(person);
      // 4.关闭SQlSession
      sqlSession.close();
  }
}

在文件中的 findPersonByldTest() 方法中,首先通过 MybatisUtils 工具类获取了 SqlSession 对象,然后通过 SqlSession 对象的 selectOne() 方法获取了个人信息。 为了查看结果, 这里使用了输出语句输出查询结果信息。 最后程序执行完毕时,关闭了 SqlSession。
使用 JUnit4 执行 findPersonByldTest() 方法后,控制台的输出结果如图所示。



从图中可以看出,使用 MyBatis 嵌套查询的方式查询出了个人及其关联的身份证信息, 这就是 MyBatis 中的一对一关联查询。
虽然使用嵌套查询的方式比较简单,但是从图中可以看出, MyBatis 嵌套查询的方式要执行多条 SOL 语句,这对于大型数据集合和列表展示不是很好,因为这样可能会导致成百上千条关联的 SOL 语句被执行,从而极大地消耗数据库性能并且会降低查询效率。 这并不是开发人员所期望的。 为此,我们可以使用 MyBatis 提供的嵌套结果方式,来进行关联查询。
在 PersonMapper.xml 中,使用 MyBatis 嵌套结果的方式进行个人及其关联的证件信息查询, 所添加的代码如下所示。

  <!-- 嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集  -->
  <select id="findPersonById2" parameterType="Integer" 
          resultMap="IdCardWithPersonResult2">
      select p.*,idcard.code
      from tb_person p, tb_idcard idcard  
      where p.card_id=idcard.id
      and p.id= #{id}     
  </select>
  <resultMap type="Person" id="IdCardWithPersonResult2">
      <id property="id" column="id"/>
      <result property="name" column="name"/>
      <result property="age" column="age"/>
      <result property="sex" column="sex"/>
      <association property="card" javaType="IdCard">
          <id property="id" column="card_id"/>
          <result property="code" column="code"/>
      </association>
  </resultMap>

从上述代码中可以看出, MyBatis 嵌套结果的方式只编写了一条复杂的多表关联的 SOL 语句, 并且在<association>元素中继续使用相关子元素进行数据库表字段和实体类属性的一一映射。
在测试类 MybatisAssociatedTest 中,编写测试方法 findPersonByldTest2() ,其代码如下所示。

  /**
   * 嵌套结果
   */
  @Test
  public void findPersonByld2Test(){
      // 1.通过工具类生成SqlSession对象
      SqlSession sqlSession = MybatisUtils.getSession();
      // 2.使用MyBatis 嵌套查询的方式查询id为1的人的信息
      Person person = sqlSession.selectOne("com.neuedu.mapper.PersonMapper.findPersonById2",1);
      // 3.输出查询结果信息
      System.out.println(person);
      // 4.关闭SQlSession
      sqlSession.close();
  }

使用 JUnit4 执行 findPersonByldTest() 方法后,控制台的输出结果如图所示。



从图中可以看出,使用 MyBatis 嵌套结果的方式只执行了一条 SQL 语句,并且同样查询出了个人及其关联的身份证的信息。

  • 多学一招: MyBatis延迟加载的配置
    在使用 MyBatis 换套查询方式进行 MyBatis 关联查询映射时,使用 MyBatis 的延迟加载在一 定程度上可以降低运行消耗并提高查询效率。 MyBatis 默认没有开启延迅加载,需要在核心配置文件 mybatis-config.xml 中的<settings>元素内进行配置,具体配置方式如下。
  <settings>
      <!-- 打开延迟加载开关  -->
      <setting name="lazyLoadingEnabled" value="true"/>
      <!-- 将积极加载改为消息加载,即按需加载   -->
      <setting name="aggressiveLazyLoading" value="false"/>
  </settings>

在映射文件中, MyBatis 关联映射的<association>元索和<collection>元素中都已默认配置了延迟加载属性, 即默认属性 fetchType="lazy" (属性 fetchType="eager"表示立即加载),所以在配置文件中开启延迟加载后,无须在映射文件中再做配置。

一对多

与一对一的关联关系相比,开发人员接触更多的关联关系是一对多(或多对一)。 例如一个用户可以有多个订单,同时多个订单归一个用户所有。 用户和订单的关联关系如图所示。



那么使用 MyBatis 是怎么处理这种一对多关联关系的呢?在前面第 7 章所讲解的 <resultMap>元素中,包含了一个<collection>子元素, MyBatis 就是通过该元素来处理一对多关联关系的。 <collection>子元素的属性大部分与<association>元素相同,但其还包含一个特殊属性一一ofType。 ofType 属性与 javaType 属性对应,它用于指定实体对象中集合类属性所包含的元素类型。
<collection>元素的使用也非常简单,同样可以参考如下两种示例进行配置,具体代码如下。



在了解了 MyBatis 处理一对多关联关系的元素和方式后,接下来以用户和订单之间的这种一对多关联关系为例,详细讲解如何在 MyBatis 中处理一对多关联关系,具体步骤如下。
( 1 )在 mybatis 数据库中,创建两个数据表,分别为 tb_user 和 tb_orders ,同时在表中预先插入几条数据,完成上述操作后,数据库 tb_user 表和 tb_orders 表中的数据如图所示。


( 2 )在 com.neuedu.po 包中,创建持久化类 Orders 和 User,并在两个类中定义相关属性和方法,文件如下所示。

package com.neuedu.po;
/**
* 订单持久化类
*/
public class Orders {
  private Integer id; //订单id
  private String number; //订单编号
  public Integer getId() {
      return id;
  }
  public void setId(Integer id) {
      this.id = id;
  }
  public String getNumber() {
      return number;
  }
  public void setNumber(String number) {
      this.number = number;
  }
  @Override
  public String toString() {
      return "Orders [id=" + id + ", number=" + number + "]";
  }
}
package com.neuedu.po;
import java.util.List;
/**
* 用户持久化类
*/
public class User {
  private Integer id; //用户编号
  private String username; //用户姓名
  private String address; //用户地址
  private List<Orders> ordersList; //用户关联的订单
  public Integer getId() {
      return id;
  }
  public void setId(Integer id) {
      this.id = id;
  }
  public String getUsername() {
      return username;
  }
  public void setUsername(String username) {
      this.username = username;
  }
  public String getAddress() {
      return address;
  }
  public void setAddress(String address) {
      this.address = address;
  }
  public List<Orders> getOrdersList() {
      return ordersList;
  }
  public void setOrdersList(List<Orders> ordersList) {
      this.ordersList = ordersList;
  }
  @Override
  public String toString() {
      return "User [id=" + id + ", username=" + username + ", address=" + address + ", ordersList=" + ordersList
              + "]";
  }
}

在上述两个文件中,分别定义了各自的属性以及对应的 getter/setter 方法,同时为了方便查看输出结果,还重写了 toString() 方法。
( 3 )在 com.neuedu.mapper 包中,创建用户实体映射文件 UserMapper.xml ,并在文件中编写一对多关联映射查询的配置,文件如下所示。

<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE mapper  
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace 表示命名空间  -->
<mapper namespace="com.neuedu.mapper.UserMapper">
<!-- 一对多:查看某一用户及其关联的订单信息
  注意:当关联查询出的列名相同,则需要使用别名区分  -->
  <select id="findUserWithOrders" parameterType="Integer" resultMap="userWithOrdersResult">
      select u.*,o.id as orders_id,o.number
      from tb_user u,tb_orders o
      where u.id = o.user_id
      and u.id = #{id}
  </select>
  <resultMap type="User" id="userWithOrdersResult">
      <id property="id" column="id"/>
      <result property="username" column="username"/>
      <result property="address" column="address"/>
      <!-- 一对多关联映射:collection
          ofType表示属性集合中元素的类型,List<Orders>属性即Orders类 -->
      <collection property="ordersList" ofType="Orders">
          <id property="id" column="orders_id"/>
          <result property="number" column="number"/>
      </collection>
  </resultMap>
</mapper>

在文件中,使用了 MyBatis 嵌套结果的方式定义了一个根据用户 id 查询用户及其关联的订单信息的 select 语句。 因为返回的用户对象中,包含 Orders 集合对象属性,所以需要手动编写结果映射信息。
( 4 )将映射文件 UserMapper.xml 的路径配置到核心配置文件 mybatis-config.xml 中,其代码如下所示。

      <mapper resource="com/neuedu/mapper/UserMapper.xml" />

( 5 )在测试类 MybatisAssociatedTest 中,编写测试方法 findUserTest() ,其代码如下所示。

  /**
   * 一对多
   */
  @Test
  public void findUserTest(){
      // 1.通过工具类生成SqlSession对象
      SqlSession sqlSession = MybatisUtils.getSession();
      // 2.查询 id 为 1 的用户信息、
      User user = sqlSession.selectOne("com.neuedu.mapper.UserMapper.findUserWithOrders", 1);
      // 3.输出查询结果信息
      System.out.println(user);
      // 4.关闭SQlSession
      sqlSession.close();
  }

使用 JUnit4 执行 findUserTest() 方法后,控制台输出结果如图所示。



从图中可以看出,使用 MyBatis 嵌套结果的方式查询出了用户及其关联的订单集合信息。 这就是 MyBatis 一对多的关联查询。
需要注意的是 ,上述案例从用户的角度出发,用户与订单之间是一对多的关联关系,但如果从单个订单的角度出发,一个订单只能属于一个用户,即一对一的关联关系。 大家可根据前面小节内容实现单个订单与用户之间的一对一关联关系,由于篇幅有限,这里不再重复赘述。

多对多

在实际项目开发中,多对多的关联关系也是非常常见的。 以订单和商品为例,一个订单可以包含多种商品,而一种商品又可以属于多个订单,订单和商品就属于多对多的关联关系, 如图所示。



在数据库中,多对多的关联关系通常使用一个中间表来维护,中间表中的订单 id 作为外键参照订单表的 id ,商品 id 作为外键参照商品表的 id。 这三个表之间的关系如图所示。



了解了数据库中订单表与商品表之间的多对多关联关系后,下面我们就通过具体的案例来讲解如何使用 MyBatis 来处理这种多对多的关系,具体实现步骤如下。
( 1 )创建数据表。 在 mybatis 数据库中新建名称为 tb_product 和 tb_ordersitem 的两个数据表,同时在表中预先插入几条数据。 由于订单表在前面小节中已经创建 , 所以这里只创建了商品表和中间表。 完成上述操作后, tb_product 表和 tb_ordersitem 表中的数据如图所示。


( 2 ) 在 com.neuedu.po 包中,创建持久化类 Product, 并在类中定义相关属性和方法,文件如下所示。

package com.neuedu.po;
import java.util.List;
/**
* 商品持久化类
*/
public class Product {
  private Integer id; //商品 id
  private String name; //商品名称
  private Double price; //商品单价
  private List<Orders> orders; //与订单关联的属性
  public Integer getId() {
      return id;
  }
  public void setId(Integer id) {
      this.id = id;
  }
  public String getName() {
      return name;
  }
  public void setName(String name) {
      this.name = name;
  }
  public Double getPrice() {
      return price;
  }
  public void setPrice(Double price) {
      this.price = price;
  }
  public List<Orders> getOrders() {
      return orders;
  }
  public void setOrders(List<Orders> orders) {
      this.orders = orders;
  }
  @Override
  public String toString() {
      return "Product [id=" + id + ", name=" + name + ", price=" + price + ", orders=" + orders + "]";
  }
}

除了在商品持久化类中需要添加订单的集合属性外,还需要在订单持久化类( Orders.java ) 中增加商品集合的属性及其对应的 getter/setter 方法,同时为了方便查看输出结果,需要重写 toString() 方法, Orders 类中添加的代码如下所示。

 private List<Product> productList; //关联商品集合
 //省略getter和setter方法
 //省略toString()方法

( 3 ) 在 com.neuedu.mapper 包中,创建订单实体映射文件 OrdersMapper.xml 和商品实体映射文件 ProductMapper.xml ,对两个映射文件进行编辑后,文件如下所示。

<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE mapper  
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace 表示命名空间  -->
<mapper namespace="com.neuedu.mapper.OrdersMapper">
<!-- 多对多嵌套查询:通过执行另外一条SQL映射语句来返回预期的特殊类型  -->
  <select id="findOrdersWithProduct" parameterType="Integer" resultMap="OrdersWithProductResult">
      select * from tb_orders where id = #{id}
  </select>
  <resultMap type="Orders" id="OrdersWithProductResult">
      <id property="id" column="id"/>
      <result property="number" column="number"/>
      <collection property="productList" column="id" ofType="Product" 
                  >select="com.neuedu.mapper.ProductMapper.findProductById">
          
      </collection>
  </resultMap>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE mapper  
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace 表示命名空间  -->
<mapper namespace="com.neuedu.mapper.ProductMapper">
  <select id="findProductById" parameterType="Integer" resultType="Product">
      select * from tb_product where id in(
          select product_id from tb_ordersitem where orders_id = #{id}
      )
  </select>
</mapper>

在文件中,使用嵌套查询的方式定义了一个 id 为 findOrdersWithPorduct 的 select 语句来查询订单及其关联的商品信息。 在<resultMap>元素中使用了 <collection>元素来映射多对多的关联关系,其中 property 属性表示订单持久化类中的商品属性, ofType 属性表示集合中的数据为 Product 类型,而 column 的属性值会作为参数执行 ProductMapper 中定义的 id 为 findProductByld 的执行语句来查询订单中的商品信息。
在文件中,定义了一个 id 为 findProductByld 的执行语句, 该执行语句中的 SQL 会根据订单 id 查询与该订单所关联的商品信息。 由于订单和商品是多对多的关联关系,所以需要通过中间表来查询商品信息。
( 4 )将新创建的映射文件 OrdersMapper.xml 和 ProductMapper.xml 的文件路径配置到核心配置文件 mybatis-config.xml 中 ,代码如下所示。

      <mapper resource="com/neuedu/mapper/OrdersMapper.xml" />
      <mapper resource="com/neuedu/mapper/ProductMapper.xml" />

( 5 )在测试类 MybatisAssociatedTest 中,编写多对多关联查询的测试方法 findOrdersTest() , 其代码如下所示。

  /**
   * 多对多
   */
  @Test
  public void findOrdersTest(){
      // 1.通过工具类生成SqlSession对象
      SqlSession sqlSession = MybatisUtils.getSession();
      // 2.查询 id 为 1 的用户信息、
      Orders orders = sqlSession.selectOne("com.neuedu.mapper.OrdersMapper.findOrdersWithProduct", 1);
      // 3.输出查询结果信息
      System.out.println(orders);
      // 4.关闭SQlSession
      sqlSession.close();
  }

使用 JUnit4 执行 findOrdersTest() 方法后,控制台的输出结果如图所示。



从图中可以看出,使用 MyBatis 嵌套查询的方式执行了两条 SOL 语句,并查询出了订单及其关联的商品信息,这就是 MyBatis 多对多的关联查询。
如果大家对多表关联查询的 SOL 语句比较熟,也可以在 OrdersMapper.xml 中使用嵌套结果的方式,其代码如下所示。



在上述执行代码中,只定义了一条查询 SOL ,通过该 SOL 即可查询出订单及其关联的商品信息。
本章小结

本章首先对开发中涉及的数据表之间以及对象之间的关联关系做了简要介绍,并由此引出了 MyBatis 框架中对关联关系的处理;然后通过案例对 MyBatis 框架处理实体对象之间的三种关联关系进行了详细讲解。通过本章的学习,大家可以了解数据表以及对象中所涉及的三种关联关系,并能够使用 MyBatis 框架对三种关联关系的查询进行处理。 MyBatis 中的关联查询操作在实际开发中非常普遍,熟练掌握这三种关联查询方式有助于提高项目的开发效率,因此大家一定要多加练习。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,701评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,649评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,037评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,994评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,018评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,796评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,481评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,370评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,868评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,014评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,153评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,832评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,494评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,039评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,437评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,131评论 2 356

推荐阅读更多精彩内容