MyBatis的关联映射

学习目标

● 了解数据表之间以及对象之间的三种关联关系

● 熟悉关联关系中的嵌套查询和嵌套结果

● 掌握一对一、一对多和多对多关联映射的使用

1.1关联关系概述

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

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

· 一对一:在任意一方引入对方主键作为外键。

· 一对多:在“多”的一方,添加“一”的一方的主键作为外键。

· 多对多:产生中间关系表,引入两张表的主键作为外键,两个主键成为联合主键或使用新的字段作为主键。

 

1.2一对一

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

那么使用MyBatis是怎么处理这种一对一关联关系的呢?

在<resultMap>元素中,包含了一个<association>子元素,

MyBatis就是通过该元素来处理一对一关联关系的。在<association>元素中,

通常可以配置以下属性。

· property:指定映射到的实体类对象属性,与表字段一一对应。

· column:指定表中对应的字段。

· javaType:指定映射到实体对象属性的类型。

· select:指定引入嵌套查询的子SQL语句,该属性用于关联映射中的嵌套查询。

· fetchType:指定在关联查询时是否启用延迟加载。fetchType属性有lazy和eager两个属性值,默认值为lazy(即默认关联映射延迟加载)。

 

为了更好的说明一对一的关系我们先创建两个数据表作为演示使用

(1)创建数据表。在mybatis数据库中分别创建名为tb_idcard和tb_person的数据表,同时预先插入两条数据。其执行的SQL语句如下所示。

#创建一个名称为tb_idcard的表
create table tb_idcard(
    id int primary key auto_increment,
    code varchar(18)
);

#插入两条数据
insert into tb_idcard (code) values ("1234567890")
insert into tb_idcard (code) values ("0987654321")

#创建一个名称为tb_person的表
create table tb_person(
  id int primary key auto_increment,
  name varchar(32),
  age int,
  sex varchar(8),
  card_id int unique,
  foreign key (card_id) references tb_idcard(id)
);

#插入两条数据
insert into tb_person (name,age,sex,card_id) values ('Rose',29,'',1);
insert into tb_person (name,age,sex,card_id) values ('tom',27,'',2);

2)在项目的com.itheima.po包下创建持久化类IdCard和Person,编辑后的代码,如文件

 

/*
* 证件持久化类
*/
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 + "]";
}
}

 

 

 

 

 

/*
 * 个人持久化类
 */
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 + "]";
}
}

 

(3)在com.itheima.mapper包中,创建证件映射文件IdCardMapper.xml和个人映射文件PersonMapper.xml,并在两个映射文件中编写一对一关联映射查询的配置信息,如文件

 

IdCardMapper.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.itheima.mapper.IdCardMapper">
<!-- 根据id查询证件信息 -->
<select id="findCodeById" parameterType="Integer" resultType="com.itheima.po.IdCard">
SELECT * from tb_idcard where id=#{id}
</select>
</mapper>

 

 

 

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.itheima.mapper.PersonMapper">
<!-- 嵌套查询:通过执行另外一条SQL映射语句来返回预期的特殊类型 -->
<select id="findPersonById" parameterType="Integer"
resultMap="IdCardWithPersonResult">
SELECT * from tb_person where id=#{id}
</select>
<resultMap type="com.itheima.po.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="com.itheima.po.IdCard"
select="com.itheima.mapper.IdCardMapper.findCodeById" />
</resultMap>
</mapper>

 

 

 

 

(4)在核心配置文件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>
<!-- 1.配置环境,默认的环境id为mysql -->
 <environments default="mysql">
    <!-- 1.2配置id为mysql的数据库环境 -->
    <environment id="mysql">
       <!-- 使用JDBC的事务管理 -->
       <transactionManager type="JDBC"/>
       <!-- 数据库连接池 -->
       <dataSource type="POOLED">
          <!-- 数据库驱动 -->
           <property name="driver" value="com.mysql.jdbc.Driver"></property>
          <!-- 连接数据库 -->
          <property name="url" value="jdbc:mysql://localhost:3306/test"></property>
          <!-- 连接数据库的用户名 -->
          <property name="username" value="root"></property>
          <!-- 连接数据库的密码 -->
         <property name="password" value="root"></property>
       </dataSource>
    </environment>
 </environments>
 <!-- 2.配置Mapper的位置 -->
 <mappers>
   
   <mapper resource="com/itheima/mapper/PersonMapper.xml"></mapper>
   <mapper resource="com/itheima/mapper/IdCardMapper.xml"></mapper>
 </mappers>
</configuration>

 

 

 

 

(5)在com.itheima.test包中,创建测试类MybatisAssociatedTest,并在类中编写测试方法findPersonByIdTest(),如文件

 

/*
 * 嵌套查询
 */
    public void findPersonByIdTest() throws Exception {
      //1.通过工具类生成SqlSession对象
      SqlSession sqlSession=MybatisUtils.getSession();
      Person person=new Person();
      //2.使用MyBatis嵌套查询的方式查询id为的人的信息
      try {
          person=sqlSession.selectOne("com.itheima.mapper"
                     +".PersonMapper.findPersonById",1);
      } catch (Exception e) {
        // TODO: handle exception
         e.printStackTrace();
      }
      //3.输出查询结果信息
      System.out.println(person.toString());
      //4.关闭SqlSession
      sqlSession.close();
    }

 

 

 

使用MyBatis嵌套查询的方式查询出了个人及其关联的身份证信息,这就是MyBatis中的一对一关联查询

虽然使用嵌套查询的方式比较简单,但是从图中可以看出,MyBatis嵌套查询的方式要执行多条SQL语句,

这对于大型数据集合和列表展示不是很好,因为这样可能会导致成百上千条关联的SQL语句被执行,从而极大地消耗数据库性能并且会降低查询效率。

这并不是开发人员所期望的。为此,我们可以使用MyBatis提供的嵌套结果方式,来进行关联查询。在PersonMapper.xml中,

使用MyBatis嵌套结果的方式进行个人及其关联的证件信息查询,所添加的代码如下所示。

<?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.itheima.mapper.PersonMapper">
    <!-- 嵌套查询:通过执行另外一条SQL映射语句来返回预期的特殊类型 -->
    <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="com.itheima.po.Person" id="IdCardWithPersonResult2">
         <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" javaType="com.itheima.po.IdCard">
            <id property="id" column="card_id"></id>
            <result property="code" column="code"/>
         </association>
     </resultMap>
</mapper>

 多学一招:

MyBatis延迟加载的配置在使用MyBatis嵌套查询方式进行MyBatis关联查询映射时,

使用MyBatis的延迟加载在一定程度上可以降低运行消耗并提高查询效率。

MyBatis默认没有开启延迟加载,需要在核心配置文件mybatis-config.xml中的<settings>元素内进行配置,具体配置方式如下。

<!-- 打开延迟加载的开关 -->
 <setting name="lazyLoadingEnabled" value="true"/>
 <!-- 将积极加载改为消息加载,急按需加载 -->
 <setting name="aggressiveLazyLoading" value="false"/>
 </settings>

 

 

1.3一队多

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

 

 

那么使用MyBatis是怎么处理这种一对多关联关系的呢?在本书第7章所讲解的<resultMap>元素中,

包含了一个<collection>子元素,MyBatis就是通过该元素来处理一对多关联关系的。

<collection>子元素的属性大部分与<association>元素相同,但其还包含一个特殊属性——ofType。

ofType属性与javaType属性对应,它用于指定实体对象中集合类属性所包含的元素类型。

 

 

在了解了MyBatis处理一对多关联关系的元素和方式后,接下来以用户和订单之间的这种一对多关联关系为例,详细讲解如何在MyBatis中处理一对多关联关系,具体步骤如下。

 

(1)在mybatis数据库中,创建两个数据表,分别为tb_user和tb_orders,同时在表中预先插入几条数据,执行的SQL语句如下所示。

 

#创建一个名称为tb_user的表
create table tb_user(
  id int(32) primary key auto_increment,
  username varchar(32),
  address varchar(256)
);

#插入3条数据
insert into tb_user values ('1','詹姆斯','克利夫兰');
insert into tb_user values ('2','科比','洛杉矶');
insert into tb_user values ('3','保罗','洛杉矶');

#创建一个名称叫做tb_orders的表
create table tb_orders(
  id int(32) primary key auto_increment,
  number varchar(32) not null,
  user_id int(32) not null,
  foreign key(user_id) references tb_user(id)
);

#插入3条数据
insert into tb_orders values ('1','1000011','1');
insert into tb_orders values ('2','1000012','2');
insert into tb_orders values ('3','1000013','3');

 

(2)在com.itheima.po包中,创建持久化类Orders和User,并在两个类中定义相关属性和方法

 

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 + "]";
}
}

 

 

 

/*
 * 订单持久化类
 */
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 + "]";
}
   
}

 

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">
<mapper namespace="com.itheima.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="com.itheima.po.User" id="UserWithOrdersResult">
   <id property="id" column="id"></id>
   <result property="username" column="username"/>
   <result property="address" column="address"/>
   <!-- 一对多关联映射:collection
   ofType表示属性集合中元素的类型,List<Orders>属性即Orders类 -->
   <collection property="ordersList" ofType="com.itheima.po.Orders">
     <id property="id" column="orders_id"></id>
     <result property="number" column="number"/>
   </collection>
</resultMap>
</mapper>

 测试类:

 /*
    * 一对多    
    */
    public void findUserTest() {
        //1.通过工具类生成SqlSession对象
        SqlSession sqlSession=MybatisUtils.getSession();
        //2.查询id为1的用户信息
        User user=sqlSession.selectOne("com.itheima.mapper"
                +".UserMapper.findUserWithOrders",1);
        //3.输出信息
        System.out.println(user.toString());
        //4.关闭sqlSession
        sqlSession.close();
    }
posted @ 2020-01-10 04:43  薄眠抛却陈年事。  阅读(438)  评论(0编辑  收藏  举报