在上一篇博客 Mybatis框架之快速入门(别再翻了,此篇博客就够了) 介绍了Mybatis框架基本使用细节,在本篇博客将介绍一下在Mybatis如何处理实体之间的关系。
懂一点数据库原理的都知道常见的实体之间的关系有一对一
、一对多
(多对一)、多对多
三种基本关系。下面是User
、Order
、Product
三者之间的关系:
建表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>
对于Order
与Product
的多对多关系,需要构建OrderItem
,拆开为Order
与OrderItem
一对多关系、Product
与OrderItem
多对一关系。
在OrderItem
实体类中设置product
属性,即可以表达Proudct
与OrderItem
的一对多关系(同一件商品可以出现在多个不同的OrderItem里,一个OrderItem只能由唯一一个Product
)。
在Order
实体类中设置orderItemList
属性,即可以表达Order
与OrderItem
的一对多关系。
由于Order
包含orderItem
、OrderItem
包含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三者之间的关系确实有点绕,如果你还是看不太明白的话,建议在纸上画一画。