学科分类
目录
SSM框架

一对多

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

图1 用户和订单的关联关系

那么使用MyBatis是怎么处理这种一对多关联关系的呢?在本书第7章所讲解的<resultMap>元素中,包含了一个<collection>子元素,MyBatis就是通过该元素来处理一对多关联关系的。<collection>子元素的属性大部分与<association>元素相同,但其还包含一个特殊属性——ofType。ofType属性与javaType属性对应,它用于指定实体对象中集合类属性所包含的元素类型。

<collection >元素的使用也非常简单,同样可以参考如下两种示例进行配置,具体代码如下:

<!--方式一:嵌套查询  -->
<collection property="ordersList" column="id" 
ofType="com.itheima.po.Orders"
select=" com.itheima.mapper.OrdersMapper.selectOrders" />
<!--方式二:嵌套结果 -->
<collection property="ordersList" ofType="com.itheima.po.Orders">
    <id property="id" column="orders_id" />
    <result property="number" column="number" />
</collection>  

在了解了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', '1');
INSERT INTO tb_orders VALUES ('3', '1000013', '2');

完成上述操作后,数据库tb_user表和tb_orders表中的数据如图2所示。

图2 tb_user和tb_orders表

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

文件1 Orders.java

 1    package com.itheima.po;
 2    /**
 3     * 订单持久化类
 4     */
 5    public class Orders {
 6        private Integer id;    //订单id
 7        private String number;//订单编号
 8        public Integer getId() {
 9            return id;
 10        }
 11        public void setId(Integer id) {
 12            this.id = id;
 13        }
 14        public String getNumber() {
 15            return number;
 16        }
 17        public void setNumber(String number) {
 18            this.number = number;
 19        }
 20        @Override
 21        public String toString() {
 22            return "Orders [id=" + id + ", number=" + number + "]";
 23        }
 24    }

文件2 User.java

 1    package com.itheima.po;
 2    import java.util.List;
 3    /**
 4     * 用户持久化类
 5     */
 6    public class User {
 7        private Integer id;                 // 用户编号
 8        private String username;           // 用户姓名
 9        private String address;            // 用户地址
 10        private List<Orders> ordersList; //用户关联的订单
 11        public Integer getId() {
 12            return id;
 13        }
 14        public void setId(Integer id) {
 15            this.id = id;
 16        }
 17        public String getUsername() {
 18            return username;
 19        }
 20        public void setUsername(String username) {
 21            this.username = username;
 22        }
 23        public String getAddress() {
 24            return address;
 25        }
 26        public void setAddress(String address) {
 27            this.address = address;
 28        }
 29        public List<Orders> getOrdersList() {
 30            return ordersList;
 31        }
 32        public void setOrdersList(List<Orders> ordersList) {
 33            this.ordersList = ordersList;
 34        }
 35        @Override
 36        public String toString() {
 37            return "User [id=" + id + ", username=" + username + ", address="
 38                    + address + ", ordersList=" + ordersList + "]";
 39        }
 40    }

在上述两个文件中,分别定义了各自的属性以及对应的getter/setter方法,同时为了方便查看输出结果,还重写了toString()方法。

(3)在com.itheima.mapper包中,创建用户实体映射文件UserMapper.xml,并在文件中编写一对多关联映射查询的配置,如文件3所示。

文件3 UserMapper.xml

 1    <?xml version="1.0" encoding="UTF-8"?>
 2    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 3        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 4    <!-- namespace表示命名空间 -->
 5    <mapper namespace="com.itheima.mapper.UserMapper">
 6        <!-- 一对多:查看某一用户及其关联的订单信息 
 7              注意:当关联查询出的列名相同,则需要使用别名区分 -->
 8        <select id="findUserWithOrders" parameterType="Integer" 
 9                               resultMap="UserWithOrdersResult">
 10            SELECT u.*,o.id as orders_id,o.number 
 11            from tb_user u,tb_orders o 
 12            WHERE u.id=o.user_id 
 13             and u.id=#{id}
 14        </select>
 15        <resultMap type="User" id="UserWithOrdersResult">
 16            <id property="id" column="id"/>
 17            <result property="username" column="username"/>
 18            <result property="address" column="address"/>
 19            <!-- 一对多关联映射:collection 
 20                ofType表示属性集合中元素的类型,List<Orders>属性即Orders类 -->
 21            <collection property="ordersList" ofType="Orders">
 22                <id property="id" column="orders_id"/>
 23                <result property="number" column="number"/>
 24            </collection>
 25        </resultMap>
 26    </mapper>

在文件3中,使用了MyBatis嵌套结果的方式定义了一个根据用户id查询用户及其关联的订单信息的select语句。因为返回的用户对象中,包含Orders集合对象属性,所以需要手动编写结果映射信息。

(4)将映射文件UserMapper.xml的路径配置到核心配置文件mybatis-config.xml中,其代码如下所示。

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

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

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

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

图3 运行结果

从图3可以看出,使用MyBatis嵌套结果的方式查询出了用户及其关联的订单集合信息。这就是MyBatis一对多的关联查询。

需要注意的是,上述案例从用户的角度出发,用户与订单之间是一对多的关联关系,但如果从单个订单的角度出发,一个订单只能属于一个用户,即一对一的关联关系。读者可根据9.2小节内容实现单个订单与用户之间的一对一关联关系,由于篇幅有限,这里不再重复赘述。

点击此处
隐藏目录