MyBatis的关联映射,resultMap元素之collection子元素,实现多对多关联关系(节选自:Java EE企业级应用开发教程)

MyBatis映射文件中的<resultMap>元素中,包含一个<collection>子元素,MyBatis通过它来处理多对多关联关系。

<collection>子元素的大部分属性与<association>子元素相同,但其还包含一个特殊属性——ofType。ofType属性与javaType属性对应,它用于指定实体对象中集合类属性所包含的元素类型。

本文是MyBatis的关联映射,resultMap元素之collection子元素,实现一对多关联关系(节选自:Java EE企业级应用开发教程)两篇文章的延续,如有配置上的问题,请参考上两篇文章。

情景:在实际项目开发中,多对多的关联关系是非常觉的。以订单和商品为例,一个订单可以包含多种商品,而一种商品又可以属于多个订单,订单和商品就属于多对多的关联关系。

一、创建数据结构及插入数据(MySQL),注意:请先选择数据库

# 创建一个名称为tb_product的表
CREATE TABLE tb_product(
    id INT(32) PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(32),
    price DOUBLE
);

# 插入3条数据
INSERT INTO tb_product VALUES ('1','Java基础入门','44.5');
INSERT INTO tb_product VALUES ('2','Java Web程序开发入门','38.5');
INSERT INTO tb_product VALUES ('3','SSM框架整合实战','50');

# 创建一个名称为tb_ordersitem 的中间表
CREATE TABLE tb_ordersitem(
    id INT(32) PRIMARY KEY AUTO_INCREMENT,
    orders_id INT(32),
    product_id INT(32),
    FOREIGN KEY(orders_id) REFERENCES tb_orders(id),
    FOREIGN KEY(product_id) REFERENCES tb_product(id)
    );

# 插入5条数据
INSERT INTO tb_ordersitem VALUES ('1','1','1');
INSERT INTO tb_ordersitem VALUES ('2','1','3');
INSERT INTO tb_ordersitem VALUES ('3','3','1');
INSERT INTO tb_ordersitem VALUES ('4','3','2');
INSERT INTO tb_ordersitem VALUES ('5','3','3');

二、创建实体类Product,并修改Orders实体类 

package com.itheima.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 [> name 
                           + ", price=" + price + "]";
    }
}
package com.itheima.po;

import java.util.List;

/**
 * 订单持久化类
 */
public class Orders {
    private Integer id;    //订单id
    private String number;//订单编号
    //关联商品集合信息
    private List<Product> productList;

    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;
    }
    public List<Product> getProductList() {
        return productList;
    }
    public void setProductList(List<Product> productList) {
        this.productList = productList;
    }
    @Override
    public String toString() {
        return "Orders [>;
    }
}

三、创建映射文件ProductMapper.xml、OrdersMapper.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.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>
<?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.OrdersMapper">
    <!-- 多对多嵌套查询:通过执行另外一条SQL映射语句来返回预期的特殊类型 -->
    <select id="findOrdersWithPorduct" 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.itheima.mapper.ProductMapper.findProductById">
        </collection>
    </resultMap>
    
    <!-- 多对多嵌套结果查询:查询某订单及其关联的商品详情 -->
    <select id="findOrdersWithPorduct2" parameterType="Integer" 
             resultMap="OrdersWithPorductResult2">
        select o.*,p.id as pid,p.name,p.price
        from tb_orders o,tb_product p,tb_ordersitem  oi
        WHERE oi.orders_id=o.id 
        and oi.product_id=p.id 
        and o.id=#{id}
    </select>
    <!-- 自定义手动映射类型 -->
    <resultMap type="Orders" id="OrdersWithPorductResult2">
        <id property="id" column="id" />
        <result property="number" column="number" />
        <!-- 多对多关联映射:collection -->
        <collection property="productList" ofType="Product">
            <id property="id" column="pid" />
            <result property="name" column="name" />
            <result property="price" column="price" />
        </collection>
    </resultMap>
</mapper>

四、修改MyBatis配置文件(mybatis-config.xml),加入如下内容: 

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

五、修改测试程序MybatisAssociatedTest.java,加入如下内容:

可以将findOrdersWithPorduct修改为findOrdersWithPorduct2,其输出仍然是一样的。

    /**
     * 多对多
     */
    @Test
    public void findOrdersTest(){
        // 1、通过工具类生成SqlSession对象
        SqlSession session = MybatisUtils.getSession();
        // 2、查询id为1的订单中的商品信息
        Orders orders = session.selectOne("com.itheima.mapper."
                               + "OrdersMapper.findOrdersWithPorduct", 3);
        // 3、输出查询结果信息
        System.out.println(orders);
        // 4、关闭SqlSession
        session.close();
    }

六、运行结果 

DEBUG [main] - ==>  Preparing: select * from tb_orders WHERE id=? 
DEBUG [main] - ==> Parameters: 3(Integer)
DEBUG [main] - <==      Total: 1
DEBUG [main] - ==>  Preparing: SELECT * from tb_product where id IN( SELECT product_id FROM tb_ordersitem WHERE orders_id = ? ) 
DEBUG [main] - ==> Parameters: 3(Integer)
DEBUG [main] - <==      Total: 3
Orders [id=3, number=1000013, productList=[Product [id=1, name=Java基础入门, price=44.5], Product [id=2, name=Java Web程序开发入门, price=38.5], Product [id=3, name=SSM框架整合实战, price=50.0]]]