【Uni-App+SSM 宠物项目实战】Day5:数据库设计

103 阅读9分钟

一、前言

前面我们完成了后端框架初始化和前端 Uni-App 项目搭建,今天进入 “数据存储核心”——数据库设计

宠物服务平台的所有数据(如用户信息、宠物档案、商家服务、订单记录)都需要存在 MySQL 中,而合理的表结构设计能避免后续 “数据冗余”“查询效率低” 等问题。今天我们不仅会执行项目自带的db.sql脚本创建基础表,还会补充订单表、购物车表、商家表等核心业务表,并重点讲解 “表关联逻辑”(比如订单表为什么要关联用户 ID),为后续前后端数据交互(如提交订单、查询商家服务)打好数据基础。

二、今日教学目标

  1. 掌握 MySQL 中核心业务表的创建(订单表、购物车表、商家表),理解每个字段的设计意义;
  1. 理解 “外键关联” 的作用(如订单表user_id关联用户表id),避免数据逻辑混乱;
  1. 能通过 SQL 语句测试表结构(查询、插入数据),验证表关联的正确性。

三、前置准备

  1. 已完成 Day1 环境搭建:MySQL 服务正常运行(可通过services.msc查看 “MySQL” 服务状态为 “正在运行”);
  1. 具备基础 SQL 语法:能看懂CREATE TABLE(建表)、SELECT(查询)、INSERT(插入)语句;
  1. 数据库工具:已安装 MySQL Workbench(或 Navicat、DataGrip),用于执行 SQL 脚本和查看表结构;
  1. 项目关键文件路径:db.sql脚本路径为C:\Users\Downloads\my-pet-custom\mypet\db.sql(项目自带基础表结构)。

四、代码实现(核心:表结构设计与关联)

1. 第一步:执行项目默认db.sql脚本(创建基础表)

项目自带的db.sql已包含用户表(users)、宠物信息表(chongwuxinxi)等基础表,先执行脚本初始化基础结构:

(1)打开db.sql并执行

  1. 打开 MySQL Workbench,登录 root 账号(密码为 Day1 设置的123456);
  1. 点击菜单栏File → Open SQL Script,选择db.sql文件(路径见前置准备);
  1. 点击 “执行” 按钮(闪电图标),等待脚本执行完成(控制台显示 “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 方法,进一步简化后端开发)。