Mybatis框架之关联查询(一对多、多对多)(附Demo:用户和订单)

在上一篇博客 Mybatis框架之快速入门(别再翻了,此篇博客就够了) 介绍了Mybatis框架基本使用细节,在本篇博客将介绍一下在Mybatis如何处理实体之间的关系。

懂一点数据库原理的都知道常见的实体之间的关系有一对一一对多(多对一)、多对多三种基本关系。下面是UserOrderProduct三者之间的关系:
在这里插入图片描述
建表sql语句:

/*
 Navicat Premium Data Transfer

 Source Server         : mysql
 Source Server Type    : MySQL
 Source Server Version : 50725
 Source Host           : localhost:3306
 Source Schema         : mybatis_day_01

 Target Server Type    : MySQL
 Target Server Version : 50725
 File Encoding         : 65001

 Date: 26/01/2020 17:15:43
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for order
-- ----------------------------
DROP TABLE IF EXISTS `order`;
CREATE TABLE `order` (
  `id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL COMMENT '下单用户id',
  `sum_price` double(32,0) DEFAULT NULL COMMENT '订单号',
  `created_time` datetime DEFAULT NULL COMMENT '创建订单时间',
  `info` varchar(100) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for order_item
-- ----------------------------
DROP TABLE IF EXISTS `order_item`;
CREATE TABLE `order_item` (
  `order_id` int(11) NOT NULL COMMENT '订单id',
  `product_id` int(11) NOT NULL COMMENT '商品id',
  `purchase_num` int(11) DEFAULT NULL COMMENT '商品购买数量',
  PRIMARY KEY (`order_id`,`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for product
-- ----------------------------
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL COMMENT '商品名称',
  `price` float(10,1) NOT NULL COMMENT '商品定价',
  `info` text COMMENT '商品描述',
  `created_time` datetime NOT NULL COMMENT '生产日期',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) NOT NULL COMMENT '用户名称',
  `birthday` date DEFAULT NULL COMMENT '生日',
  `sex` char(1) DEFAULT NULL COMMENT '性别',
  `address` varchar(256) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

SET FOREIGN_KEY_CHECKS = 1;

1、实体类

①User实体类
package cn.hestyle.entity;

import java.io.Serializable;
import java.util.Date;

/**
 * description: User模型
 *
 * @author hestyle
 * @version 1.0
 * @className mybatis_day_01->User
 * @date 2020-01-25 10:58
 **/
public class User implements Serializable {
    private Integer id;
    private String username;
    private String sex;
    private Date birthday;
    private String address;

    public User() {
    }

    //getter、setter方法

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                ", address='" + address + '\'' +
                '}';
    }
}
②Product实体类
package cn.hestyle.entity;

import java.io.Serializable;
import java.util.Date;

/**
 * description: Product实体类
 *
 * @author hestyle
 * @version 1.0
 * @className mybatis_day_01->Product
 * @date 2020-01-26 15:02
 **/
public class Product implements Serializable {
    private Integer id;
    private String name;
    private Double price;
    private Date createdTime;
    private String info;

    public Product() {
    }

    public Product(String name, Double price, Date createdTime, String info) {
        this.name = name;
        this.price = price;
        this.createdTime = createdTime;
        this.info = info;
    }

    //getter、setter方法

    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", createdTime=" + createdTime +
                ", info='" + info + '\'' +
                '}';
    }
}
③OrderItem实体类
package cn.hestyle.entity;

import java.io.Serializable;

/**
 * description: OrderItem实体类
 *
 * @author hestyle
 * @version 1.0
 * @className mybatis_day_01->OrderItem
 * @date 2020-01-26 14:58
 **/
public class OrderItem implements Serializable {
    /**所属order的id*/
    private Integer orderId;
    /**商品*/
    private Product product;
    /**购买数量*/
    private Integer purchaseNum;

    public OrderItem() {
    }

    public OrderItem(Integer orderId, Product product, Integer purchaseNum) {
        this.orderId = orderId;
        this.product = product;
        this.purchaseNum = purchaseNum;
    }

    //getter、setter方法

    @Override
    public String toString() {
        return "OrderItem{" +
                "orderId=" + orderId +
                ", product=" + product +
                ", purchaseNum=" + purchaseNum +
                '}';
    }
}
④Order实体类
package cn.hestyle.entity;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * description: Order实体类
 *
 * @author hestyle
 * @version 1.0
 * @className mybatis_day_01->Order
 * @date 2020-01-26 12:09
 **/
public class Order implements Serializable {
    private Integer id;
    private Integer userId;
    private Double sumPrice;
    private Date createdTime;
    private String info;
    private List<OrderItem> orderItemList = new ArrayList<OrderItem>();

    public Order() {
    }

    //getter、setter方法
    
    @Override
    public String toString() {
        return "Order{" +
                "id=" + id +
                ", sumPrice=" + sumPrice +
                ", createdTime=" + createdTime +
                ", info='" + info + '\'' +
                '}';
    }
}

2、mappers映射文件

①、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:命名空间,它的作用就是对SQL进行分类化管理,可以理解为SQL隔离
	注意:使用mapper代理开发时,namespace有特殊且重要的作用
 -->
<mapper namespace="cn.hestyle.mapper.ProductMapper">
	<!--
    保存一个Product对象
        keyProperty="id" useGeneratedKeys="true"用于设置自动增长标识(id字段自动增长)
        [parameterType]:入参的java类型
     -->
	<insert id="save" keyProperty="id" useGeneratedKeys="true" parameterType="cn.hestyle.entity.User">
		INSERT INTO product(
			name, price, info, created_time
		) VALUES (
			#{name},#{price},#{info},#{createdTime}
		)
	</insert>

    <!--
    通过id查找Product表
        [id]:statement的id,要求在命名空间内唯一
        [parameterType]:入参的java类型
        [resultType]:查询出的单条结果集对应的java类型
     -->
    <select id="findById" parameterType="java.lang.Integer" resultType="cn.hestyle.entity.Product">
		SELECT id, name, price, info, created_time createdTime
		FROM `product`
		WHERE id = #{id}
	</select>

	<!--
    查找product表所有记录
        [id]:statement的id,要求在命名空间内唯一
        [resultType]:查询出的单条结果集对应的java类型
     -->
	<select id="findAll" resultType="cn.hestyle.entity.Product">
		SELECT id, name, price, info, created_time createdTime
		FROM `product`
	</select>
</mapper>
②、OrderItemMapper.xml

由于OrderItem实体类中含有product属性,所以需要调ProductMapper.findById方法查找到product。

<?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:命名空间,它的作用就是对SQL进行分类化管理,可以理解为SQL隔离
	注意:使用mapper代理开发时,namespace有特殊且重要的作用
 -->
<mapper namespace="cn.hestyle.mapper.OrderItemMapper">


	<!-- 批量保存orderItem,传入的数据是orderItemList -->
	<insert id="saveOrderItemList" parameterType="cn.hestyle.entity.OrderItem">
		INSERT INTO order_item(order_id, product_id, purchase_num)
		VALUES
			<!-- 使用foreach遍历orderItemList,拼接sql参数 -->
			<foreach collection="list" item="orderItem" separator=",">
				(#{orderItem.orderId},#{orderItem.product.id},#{orderItem.purchaseNum})
			</foreach>
	</insert>

	<!--
    OrderItem实体类对象封装的规则,OrderItem(orderId, product, purchaseNum)实体类、orderItem(order_id, product_id, purchaseNum)表
    需要根据orderItem.product_id去查找product表,得到相应的对象
     -->
	<resultMap id="MyOrderItem" type="cn.hestyle.entity.OrderItem">
		<result column="order_id" property="orderId"/>
		<result column="purchase_num" property="purchaseNum"/>
		<!-- order_item.product_id字段信息,调用ProductMapper.findById方法查找到product,然后映射到OrderItem.product属性 -->
		<association property="product" select="cn.hestyle.mapper.ProductMapper.findById" column="product_id"/>
	</resultMap>

	<!-- 通过orderId查找该订单的所有orderItem -->
    <select id="findByOrderId" parameterType="java.lang.Integer" resultMap="MyOrderItem">
		SELECT *
		FROM `order_item`
		WHERE order_id = #{orderId}
	</select>
</mapper>
③、OrderMapper.xml

由于Order实体类中含有orderItemList属性,所以需要调OrderItemMapper.findByOrderId方法查找到orderItemList。

<?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:命名空间,它的作用就是对SQL进行分类化管理,可以理解为SQL隔离
	注意:使用mapper代理开发时,namespace有特殊且重要的作用
 -->
<mapper namespace="cn.hestyle.mapper.OrderMapper">
	<!-- 保存一个order -->
	<insert id="save" parameterType="cn.hestyle.entity.Order">
		INSERT INTO `order`(id, user_id, sum_price, created_time, info)
		VALUES (#{id},#{userId},#{sumPrice},#{createdTime},#{info})
	</insert>
    <!--
    Order实体类各个属性与order表中的字段映射规则
     -->
	<resultMap id="MyOrder" type="cn.hestyle.entity.Order">
		<id column="id" property="id"/>
		<result column="user_id" property="userId"/>
		<result column="sum_price" property="sumPrice"/>
		<result column="created_time" property="createdTime"/>
		<result column="info" property="info"/>
		<!-- Order.orderItemList字段需要调用OrderItemMapper.findByOrderId方法,查找order_item表 -->
		<association property="orderItemList" select="cn.hestyle.mapper.OrderItemMapper.findByOrderId" column="id"/>
	</resultMap>

	<!-- 通过orderId查找order -->
    <select id="findByOrderId" parameterType="java.lang.Integer" resultMap="MyOrder">
		SELECT *
		FROM `order`
		WHERE id = #{orderId}
	</select>

	<!-- 通过userId查找order -->
	<select id="findOrdersByUserId" parameterType="java.lang.Integer" resultMap="MyOrder">
		SELECT *
		FROM `order`
		WHERE user_id = #{userId}
	</select>

	<!-- 查找所有order -->
	<select id="findAll" resultMap="MyOrder">
		SELECT *
		FROM `order`
	</select>
</mapper>

对于OrderProduct的多对多关系,需要构建OrderItem,拆开为OrderOrderItem一对多关系、ProductOrderItem多对一关系。

OrderItem实体类中设置product属性,即可以表达ProudctOrderItem的一对多关系(同一件商品可以出现在多个不同的OrderItem里,一个OrderItem只能由唯一一个Product)。

Order实体类中设置orderItemList属性,即可以表达OrderOrderItem的一对多关系。

由于Order包含orderItemOrderItem包含product,所以当我们查找Order的时候,需要通过orderId把属于该账单的所有OrderItem也查出来放到Order.orderItemList中。同样,查找OrderItem时,需要根据productId查找product放到OrderItem.product属性中。

不难看出,最为关键的就是一对多的关系处理,在映射文件中通过resultMap.association标签进行跨命名空间调用select方法。

Demo源码地址:

地址一:https://pan.baidu.com/s/1Dod-pVPyVsrjxcAqrV6YWQ 提取码: thf2
地址二:https://pan.baidu.com/s/1wCcRWyeFxwwTyRmGDWmF0A 提取码: 9t3a
地址三:https://pan.baidu.com/s/1PaXWZeszhXL8uM6gAyceRQ 提取码: d2n9

Order、OrderItem、product三者之间的关系确实有点绕,如果你还是看不太明白的话,建议在纸上画一画。

发布了976 篇原创文章 · 获赞 230 · 访问量 20万+

猜你喜欢

转载自blog.csdn.net/qq_41855420/article/details/104087425