一、前言
前面我们完成了后端框架初始化和前端 Uni-App 项目搭建,今天进入 “数据存储核心”——数据库设计。
宠物服务平台的所有数据(如用户信息、宠物档案、商家服务、订单记录)都需要存在 MySQL 中,而合理的表结构设计能避免后续 “数据冗余”“查询效率低” 等问题。今天我们不仅会执行项目自带的db.sql脚本创建基础表,还会补充订单表、购物车表、商家表等核心业务表,并重点讲解 “表关联逻辑”(比如订单表为什么要关联用户 ID),为后续前后端数据交互(如提交订单、查询商家服务)打好数据基础。
二、今日教学目标
- 掌握 MySQL 中核心业务表的创建(订单表、购物车表、商家表),理解每个字段的设计意义;
- 理解 “外键关联” 的作用(如订单表user_id关联用户表id),避免数据逻辑混乱;
- 能通过 SQL 语句测试表结构(查询、插入数据),验证表关联的正确性。
三、前置准备
- 已完成 Day1 环境搭建:MySQL 服务正常运行(可通过services.msc查看 “MySQL” 服务状态为 “正在运行”);
- 具备基础 SQL 语法:能看懂CREATE TABLE(建表)、SELECT(查询)、INSERT(插入)语句;
- 数据库工具:已安装 MySQL Workbench(或 Navicat、DataGrip),用于执行 SQL 脚本和查看表结构;
- 项目关键文件路径:db.sql脚本路径为C:\Users\Downloads\my-pet-custom\mypet\db.sql(项目自带基础表结构)。
四、代码实现(核心:表结构设计与关联)
1. 第一步:执行项目默认db.sql脚本(创建基础表)
项目自带的db.sql已包含用户表(users)、宠物信息表(chongwuxinxi)等基础表,先执行脚本初始化基础结构:
(1)打开db.sql并执行
- 打开 MySQL Workbench,登录 root 账号(密码为 Day1 设置的123456);
- 点击菜单栏File → Open SQL Script,选择db.sql文件(路径见前置准备);
- 点击 “执行” 按钮(闪电图标),等待脚本执行完成(控制台显示 “0 errors” 即成功)。
(2)基础表说明(db.sql已包含)
执行后会生成以下核心基础表,后续业务表需基于这些表做关联:
- users:用户表(存储用户 ID、用户名、密码等,主键id);
- chongwuxinxi:宠物信息表(存储宠物 ID、品种、年龄等,主键id);
- chongwufenlei:宠物分类表(存储分类 ID、分类名称如 “犬类”“猫类”,主键id)。
2. 第二步:新增核心业务表(补充订单 / 购物车 / 商家模块)
db.sql可能未包含订单、购物车、商家等高频业务表,需手动创建,每个表的 SQL 语句均附带字段说明:
(1)商家表(shangjia):存储宠物店 / 服务商家信息
-- 商家表:记录商家基本信息,后续用户预约服务需关联此表
CREATE TABLE shangjia (
id BIGINT PRIMARY KEY AUTO_INCREMENT, -- 商家ID(主键,自增)
name VARCHAR(100) NOT NULL, -- 商家名称(如“宠爱之家宠物店”,非空)
address VARCHAR(200) NOT NULL, -- 商家地址(用于用户导航,非空)
phone VARCHAR(20) NOT NULL, -- 商家电话(用户联系,非空)
logo VARCHAR(255), -- 商家logo图片路径(可选)
create_time DATETIME DEFAULT CURRENT_TIMESTAMP -- 商家创建时间(默认当前时间)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- 用InnoDB引擎(支持事务和外键)
(2)购物车表(cart):存储用户待购买的宠物用品
-- 购物车表:关联用户和商品,记录待结算商品
CREATE TABLE cart (
id BIGINT PRIMARY KEY AUTO_INCREMENT, -- 购物车ID(主键,自增)
user_id BIGINT NOT NULL, -- 关联用户表的id(哪个用户的购物车)
product_id BIGINT NOT NULL, -- 关联宠物用品表的id(待购买商品)
quantity INT NOT NULL DEFAULT 1, -- 商品数量(默认1件,非空)
create_time DATETIME DEFAULT CURRENT_TIMESTAMP, -- 添加时间
-- 外键约束:user_id必须存在于users表的id中,确保数据合法
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE -- 若用户被删除,其购物车数据也删除
ON UPDATE CASCADE -- 若用户id更新,购物车的user_id同步更新
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
(3)订单表(orders):存储用户提交的服务 / 用品订单(重点讲解关联)
订单表是核心业务表,需同时关联用户表和商家表,确保 “哪个用户在哪个商家下的订单” 逻辑清晰:
-- 订单表:记录用户订单信息,核心是关联用户和商家
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT, -- 订单ID(主键,自增)
order_no VARCHAR(50) NOT NULL UNIQUE, -- 订单编号(唯一,如“PET20240520001”,用于查询)
user_id BIGINT NOT NULL, -- 关联用户表id(订单归属的用户)
shangjia_id BIGINT NOT NULL, -- 关联商家表id(订单对应的商家)
total_amount DECIMAL(10,2) NOT NULL, -- 订单总金额(如99.00元,非空)
status VARCHAR(20) NOT NULL DEFAULT '待付款', -- 订单状态(待付款/已付款/已取消)
pay_time DATETIME, -- 付款时间(可选,未付款则为NULL)
create_time DATETIME DEFAULT CURRENT_TIMESTAMP, -- 订单创建时间
-- 外键约束1:user_id关联用户表,确保订单归属的用户存在
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE RESTRICT -- 若用户存在订单,禁止删除该用户(避免数据丢失)
ON UPDATE CASCADE,
-- 外键约束2:shangjia_id关联商家表,确保订单对应的商家存在
FOREIGN KEY (shangjia_id) REFERENCES shangjia(id)
ON DELETE RESTRICT
ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3. 第三步:重点讲解 “表关联逻辑”(为什么需要外键?)
很多初学者会疑惑 “为什么订单表要加user_id和shangjia_id,直接存用户名和商家名不行吗?”,这里用两个核心原因解释:
(1)避免数据冗余
- 若订单表存 “用户名” 而非user_id:当用户修改用户名(如从 “张三” 改成 “张大三”),所有关联该用户的订单都需要同步修改用户名,操作繁琐且容易遗漏;
- 存user_id:只需修改users表的username字段,订单表通过user_id关联查询时,自动获取最新用户名,无需修改订单数据。
(2)确保数据合法性
- 若没有外键约束:可能插入 “user_id=999” 的订单,但users表中根本没有id=999的用户,导致 “无主订单”,后续查询时会出现数据错乱;
- 加外键约束:插入订单时,MySQL 会自动检查user_id是否在users表中存在,不存在则报错(如 “外键约束失败”),避免非法数据。
五、效果验证(测试表结构与关联)
1. 验证表是否创建成功
执行以下 SQL 语句,查询所有表,确认新增的shangjia、cart、orders表存在:
-- 查询当前数据库(mypet)下的所有表
SHOW TABLES;
预期结果:列表中包含users、chongwuxinxi、shangjia、cart、orders等表。
2. 插入测试数据(验证外键关联)
按 “先插主表(users/shangjia)→ 再插关联表(orders)” 的顺序插入数据,避免外键约束报错:
(1)先给users表插一条用户数据(若已存在则跳过)
INSERT INTO users (username, password, phone)
VALUES ('pet_user1', 'e10adc3949ba59abbe56e057f20f883e', '13800138001');
-- 说明:password是“123456”的MD5加密值,与项目登录逻辑一致
(2)给shangjia表插一条商家数据
INSERT INTO shangjia (name, address, phone)
VALUES ('宠爱之家宠物店', '北京市朝阳区建国路88号', '13900139001');
(3)给orders表插一条订单数据(关联上述用户和商家)
INSERT INTO orders (order_no, user_id, shangjia_id, total_amount, status)
VALUES ('PET20240520001', 1, 1, 199.00, '已付款');
-- 说明:user_id=1对应上述插的用户,shangjia_id=1对应上述插的商家
(4)查询订单数据(验证关联是否生效)
执行关联查询,通过user_id和shangjia_id获取用户名和商家名:
-- 关联订单表、用户表、商家表,查询订单详情
SELECT
o.order_no AS 订单编号,
u.username AS 用户名,
s.name AS 商家名称,
o.total_amount AS 总金额,
o.status AS 订单状态
FROM orders o
LEFT JOIN users u ON o.user_id = u.id -- 订单表关联用户表
LEFT JOIN shangjia s ON o.shangjia_id = s.id; -- 订单表关联商家表
预期结果:显示一条订单数据,包含 “PET20240520001”“pet_user1”“宠爱之家宠物店”“199.00”“已付款”。
六、常见问题与解决方案
| 问题描述 | 可能原因 | 解决方案 |
|---|---|---|
| 执行CREATE TABLE时提示 “外键约束失败(1215)” | 1. 关联的主表(如users)不存在;2. 主表的主键字段类型与外键字段类型不匹配(如主表id是INT,外键user_id是BIGINT) | 1. 先确保users、shangjia等主表已创建;2. 检查字段类型:外键字段类型必须与主表主键类型完全一致(如主表id是BIGINT,外键user_id也必须是BIGINT) |
| 插入订单时提示 “无法添加或更新子行:外键约束失败(1452)” | 插入的user_id或shangjia_id在主表中不存在(如插入user_id=999,但users表没有id=999的用户) | 1. 先执行SELECT * FROM users;确认user_id存在;2. 先插入主表数据(如先插用户、商家),再插订单数据 |
| 执行db.sql时提示 “表已存在(1050)” | 之前已执行过db.sql,表重复创建 | 1. 先删除已存在的表(执行DROP TABLE IF EXISTS 表名;,如DROP TABLE IF EXISTS users;);2. 或在db.sql的CREATE TABLE前加IF NOT EXISTS(如CREATE TABLE IF NOT EXISTS users (...)) |
七、工具类 / 框架特性拓展
外键与 MyBatis-Plus 的关联查询
今天我们用 MySQL 外键确保数据合法性,后续开发中,MyBatis-Plus(MP)会简化 “关联查询” 操作:
- 无需手动写LEFT JOIN SQL:MP 的Wrapper条件构造器可快速实现关联查询(如查询 “用户 1 的所有订单”);
- 支持 “一对一”“一对多” 映射:比如UserEntity中可定义List orders,MP 会自动将用户关联的所有订单映射到该列表中,无需手动处理关联逻辑。
后续 Day7 “用户订单查询” 功能会详细讲解 MP 关联查询的实现。
八、结语
今天我们完成了核心业务表的设计和关联测试 —— 你在插入订单数据时,是否遇到了 “外键约束失败” 的问题?或者在关联查询时,是否成功获取到了用户名和商家名?
如果遇到 SQL 报错,欢迎在评论区贴出你的 SQL 语句和错误日志(关键部分即可),我们一起分析原因;如果成功实现关联查询,也可以分享你的查询结果,让大家一起验证表结构设计的正确性!
小马绿泡泡:Niuma4G
下期预告
Day6:MP 实体类与 Mapper 生成(基于今天设计的表结构,用 MP 逆向工程生成实体类和 Mapper 接口,无需手动编写 CRUD 方法,进一步简化后端开发)。