
简介Ebuy易买网商城项目是一套基于MySQL数据库、采用Java与JSP技术开发的电商平台完整源码面向Java Web初学者与课程设计、毕业设计开发者帮助其理解电商系统的前后台架构与实现细节。压缩包共1182个文件约23.7MB涵盖js、html、css等前端资源png、jpg、gif等图片素材以及java、class、jar、jsp等后端与视图层文件另含sql脚本、db数据文件与xml、properties配置结构完整便于直接部署运行。项目包含商品管理、用户登录注册、购物车、订单处理及后台权限管理等模块数据库设计涉及商品表、用户表、订单表等实体关系并涉及预编译语句防注入、密码加密存储等安全思路。目前已有718人学习下载适合作为Java Web综合实践与电商业务逻辑学习的参考案例。1. Ebuy易买网商城项目一套 MySQL 前台后台库到底该怎么落地很多做 JavaWeb 课程设计或接私活的朋友拿到「Ebuy易买网商城项目」的第一反应是去搜源码包结果下回来一堆.sql文件导入之后前台能打开、后台登录报错或者商品列表是空的。问题基本不在代码而在数据库这一层没吃透。Ebuy易买网这类商城项目的 MySQL 数据库本质上是把「前台交易」和「后台管理」两套业务流拆成两组表再用外键和状态字段把它们缝在一起。前台关心的是商品展示、购物车、订单后台关心的是商品维护、订单处理、用户管理。这两套逻辑共用同一份商品表和订单表只是读写权限和字段可见性不同。这篇文章不讲空泛的数据库概念而是把 Ebuy易买网商城项目里前台库和后台库怎么建、怎么连、怎么查、怎么排错按一线实操的顺序拆开讲。适合正在做数据库课程设计、或者第一次接手 JavaWeb 商城项目、需要把 MySQL 增删改查真正跑通的人。2. 前台库与后台库的表结构拆解从商品表到订单状态机2.1 前台和后台到底是不是两个库先纠正一个常见误解。Ebuy易买网商城项目里说的「前台后台」在数据库层面通常不是两个独立的 database而是同一个库里的两组表通过角色和业务场景区分。前台用到的核心表包括product商品、category分类、cart购物车、orders订单、order_item订单明细、user前台会员。后台用到的核心表包括admin管理员、product复用、orders复用、category复用、notice公告、log操作日志。为什么复用而不是拆开因为商品和订单是同一份数据。如果前台一个product表、后台一个product表库存扣减和价格修改就会立刻出现数据不一致。常见做法是商品表、订单表、分类表只建一份前台只读、后台读写用户表和管理员表分开建因为登录入口和权限模型完全不同。下面这张表是我在多个商城项目里验证过的核心表清单字段名可以直接抄表名归属关键字段说明user前台id,username,password,phone,address前台会员密码存 MD5admin后台id,username,password,role管理员role 区分超管和运营category共用id,name,parent_id,sort支持二级分类parent_id 为 0 是一级product共用id,name,price,stock,category_id,statusstatus1 上架0 下架cart前台id,user_id,product_id,num同一用户同一商品只存一条orders共用id,user_id,total,status,create_timestatus 是订单状态机核心order_item共用id,order_id,product_id,price,num下单时快照价格不随商品改价2.2 订单状态机前台和后台的交接点订单表是整个项目里最容易翻车的地方。前台用户下单、支付、取消后台管理员发货、退款、关闭这些动作全部落在orders.status这一个字段上。如果状态定义不清晰就会出现「前台显示已支付、后台显示待付款」这种玄学问题。我一般用这套状态值简单且够用0待付款用户刚下单库存已锁定1已付款支付回调成功等待发货2已发货后台填写物流单号3已完成用户确认收货4已取消用户主动取消或超时未付5退款中用户申请退款6已退款后台同意退款前台只展示0到3和4后台可以操作1到6。状态流转必须用 SQL 的UPDATE ... WHERE status 当前值来保证原子性不能先查再改。-- 后台发货只有已付款(1)的订单才能改成已发货(2) UPDATE orders SET status 2, ship_time NOW(), logistics_no SF123456789 WHERE id 1001 AND status 1; -- 检查受影响行数如果是 0 说明状态已经被别人改过 SELECT ROW_COUNT() AS affected;这段 SQL 的逻辑是把订单 ID 为 1001 且当前状态为 1 的记录改成状态 2同时写入发货时间和物流单号。WHERE后面的status 1是关键它防止了并发场景下两个管理员同时发货导致的状态覆盖。执行后必须检查ROW_COUNT()如果返回 0说明订单状态已经变了前端要提示「订单状态已变更请刷新」。参数上ship_time用NOW()取服务器时间logistics_no是字符串长度建议varchar(50)。2.3 商品表的 status 和 stock 怎么配合商品表有两个字段直接影响前台能不能买status和stock。status0是下架前台列表查不到stock0是售罄前台能查到但加购按钮要置灰。后台改商品时这两个字段要分开操作不要用一个字段同时表达上下架和库存。-- 前台商品列表只查上架且有库存的 SELECT id, name, price, stock, category_id FROM product WHERE status 1 AND stock 0 ORDER BY id DESC LIMIT 0, 20; -- 后台商品列表全部展示用 status 标记上下架 SELECT id, name, price, stock, status, category_id FROM product ORDER BY status ASC, id DESC LIMIT 0, 20;前台查询加了stock 0后台查询不加这是最直观的区别。分页用LIMIT offset, sizeoffset 从 0 开始。如果商品量超过 10 万LIMIT深分页会变慢那时候再考虑用WHERE id 上一页最大 id的方式优化但课程设计级别完全够用。3. 用 MySQL 把 Ebuy 前台后台跑起来建库、导数据、连项目3.1 建库和字符集别让中文变成问号第一步永远是建库。Ebuy易买网这类项目数据库名一般叫ebuy或ebuy_shop字符集必须用utf8mb4排序规则用utf8mb4_general_ci。用utf8的话emoji 和部分生僻字会插入失败前台商品名带特殊符号就翻车。-- 建库字符集用 utf8mb4 CREATE DATABASE IF NOT EXISTS ebuy DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; USE ebuy; -- 建商品表注意 price 用 decimal 不用 float CREATE TABLE product ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, name VARCHAR(200) NOT NULL COMMENT 商品名称, price DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 售价, stock INT NOT NULL DEFAULT 0 COMMENT 库存, category_id INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 分类ID, status TINYINT NOT NULL DEFAULT 1 COMMENT 1上架 0下架, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_category (category_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品表;price用DECIMAL(10,2)而不是FLOAT因为浮点数算钱会出现0.1 0.2 0.30000000000000004这种问题订单金额对不上就是血泪教训。stock用INT默认值 0配合mysql设置默认值为0这个热搜词注意DEFAULT 0要写在字段定义里不能靠代码兜底。索引idx_category和idx_status是为了前台按分类筛选和后台按状态筛选时走索引数据量小的时候感觉不到上万条之后差别很明显。3.2 导入 SQL 文件的正确姿势拿到 Ebuy 项目的.sql文件后不要直接双击用图形工具导入先看文件头有没有CREATE DATABASE和USE。如果没有要先手动建库再导入。# 命令行导入先建库再导 mysql -u root -p -e CREATE DATABASE IF NOT EXISTS ebuy DEFAULT CHARSET utf8mb4; mysql -u root -p ebuy ebuy.sql # 如果 SQL 文件里有 USE ebuy; 就直接导 mysql -u root -p ebuy.sql命令行导入比图形工具稳因为图形工具遇到大文件容易超时。导入完成后用SHOW TABLES;确认表数量再用SELECT COUNT(*) FROM product;看商品数据有没有进去。如果商品表是空的说明 SQL 文件里只有建表语句没有INSERT需要单独找数据文件。注意导入前确认 SQL 文件编码是 UTF-8用记事本另存为 ANSI 再导入中文会变乱码。Linux 下用file ebuy.sql看编码。3.3 Java 项目连接 MySQL 的配置Ebuy 项目如果是 JavaWeb 的数据库连接一般在db.properties或application.yml里。MySQL 8 的驱动类是com.mysql.cj.jdbc.DriverURL 要带时区和 SSL 参数否则会报mysql ssl连接错误。# db.properties jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/ebuy?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue jdbc.usernameroot jdbc.password你的密码useSSLfalse是本地开发关掉 SSL生产环境要开。serverTimezoneAsia/Shanghai解决时区差 8 小时的问题不加的话订单创建时间会不对。allowPublicKeyRetrievaltrue是 MySQL 8 用caching_sha2_password认证时需要加的不加会报Public Key Retrieval is not allowed。这几个参数是 JavaWeb 项目连 MySQL 8 的标配少一个都可能连不上。连接池方面课程设计用 Druid 或 HikariCP 都行。Druid 的配置里initialSize设 5maxActive设 20validationQuery用SELECT 1。连接池不是越大越好maxActive超过数据库max_connections反而会导致连接被拒。4. 前台后台增删改查的 SQL 写法与参数边界4.1 前台商品查询和购物车合并前台最核心的查询是商品列表和商品详情。商品详情要带分类名用LEFT JOIN关联分类表。-- 商品详情带分类名 SELECT p.id, p.name, p.price, p.stock, p.status, c.name AS category_name FROM product p LEFT JOIN category c ON p.category_id c.id WHERE p.id 1001 AND p.status 1;LEFT JOIN保证即使分类被删了商品还能查出来category_name为 NULL。如果用INNER JOIN分类删了商品就消失前台会莫名其妙少商品。购物车合并是前台另一个高频操作。同一个用户反复加同一商品不能插多条要用INSERT ... ON DUPLICATE KEY UPDATE。-- 购物车存在则数量累加不存在则插入 INSERT INTO cart (user_id, product_id, num) VALUES (1, 1001, 2) ON DUPLICATE KEY UPDATE num num 2;这要求cart表在(user_id, product_id)上建唯一索引否则ON DUPLICATE KEY不生效。ALTER TABLE cart ADD UNIQUE KEY uk_user_product (user_id, product_id);没有这个唯一索引购物车会出现同一商品多条记录结算时数量翻倍这是新手最常见的翻车点之一。4.2 后台订单列表和状态筛选后台订单列表要支持按状态、按时间、按用户筛选。动态条件用 MyBatis 的if标签拼但 SQL 层面要保证每个条件都能走索引。-- 后台订单列表状态和时间范围筛选 SELECT o.id, o.total, o.status, o.create_time, u.username FROM orders o LEFT JOIN user u ON o.user_id u.id WHERE 11 AND o.status 1 AND o.create_time 2025-01-01 00:00:00 AND o.create_time 2025-02-01 00:00:00 ORDER BY o.create_time DESC LIMIT 0, 20;WHERE 11是为了后面动态拼AND方便但生产环境建议用 MyBatis 的where标签避免全表扫描的误解。create_time上要建索引否则按时间筛选会慢。ALTER TABLE orders ADD KEY idx_status_time (status, create_time);联合索引(status, create_time)比单独建两个索引更有效因为后台查询通常是「先按状态筛再按时间排序」。4.3 库存扣减别用先查再改库存扣减是商城项目最经典的并发问题。错误写法是先SELECT stock FROM product WHERE id1001然后在代码里减 1再UPDATE。两个用户同时下单都查到库存 1都减 1最后库存变成 -1。正确写法是用一条 SQL 完成判断和扣减-- 扣库存库存充足才扣返回受影响行数 UPDATE product SET stock stock - 2 WHERE id 1001 AND stock 2; -- 受影响行数为 0 说明库存不足回滚订单 SELECT ROW_COUNT() AS affected;WHERE stock 2是原子判断MySQL 的 InnoDB 行锁保证同一行不会被并发修改。执行后检查ROW_COUNT()为 0 就抛异常回滚事务。这个写法比SELECT ... FOR UPDATE更轻量适合课程设计和小型项目。注意扣库存和插订单必须在同一个事务里否则扣了库存订单没插进去库存就白扣了。5. 避坑排查Ebuy 数据库连不上、数据不对、状态错乱5.1 报错error 2002 (hy000): cant connect to local mysql server through socket现象命令行连 MySQL 报这个错图形工具却能连。原因MySQL 客户端默认走 socket 文件连接但 socket 路径不对或者 MySQL 服务没启动。解决先systemctl status mysql或service mysql status看服务状态。如果服务正常用mysql -h 127.0.0.1 -u root -p强制走 TCP 连接绕过 socket。Linux 下 socket 文件一般在/var/lib/mysql/mysql.sock可以在/etc/my.cnf里确认socket配置。5.2 前台商品列表为空后台能看到商品现象后台商品管理有数据前台首页一个商品都不显示。原因前台查询加了status 1 AND stock 0但商品数据里status是 0 或者stock是 0。解决先查SELECT id, name, status, stock FROM product LIMIT 10;看实际值。如果是导入的测试数据批量改成上架有库存UPDATE product SET status 1, stock 100 WHERE stock 0;。如果是后台新增商品时没选上架检查后台表单的status默认值。5.3 订单金额和商品总价对不上现象订单表total是 100但order_item里明细加起来是 98。原因下单时用了商品当前价格算总价但order_item存的是另一个时间点的价格或者运费、优惠没算进去。解决下单逻辑必须在一个事务里先查商品价格算总价插orders再插order_itemorder_item.price用查询时的价格快照。不要在插完订单后再去查价格中间商品改价就会对不上。检查order_item的price字段类型是不是DECIMAL(10,2)用FLOAT也会导致几分钱的误差。5.4 后台登录提示密码错误但数据库里密码是对的现象admin表里密码是123456后台登录就是失败。原因代码里对密码做了 MD5 加密后比对但数据库里存的是明文或者 MD5 大小写不一致。解决确认代码里登录逻辑是SELECT * FROM admin WHERE username ? AND password MD5(?)还是先查用户名再在 Java 里比对 MD5。两种写法都行但要一致。数据库里密码建议存 32 位小写 MD5用SELECT MD5(123456);生成后手动更新。如果用的是 BCrypt那数据库里存的是$2a$开头的哈希不能用 MD5 比对。5.5 中文商品名导入后变成问号现象SQL 文件导入后商品名全是???。原因导入时客户端字符集不是utf8mb4或者 SQL 文件本身编码是 GBK。解决导入前执行SET NAMES utf8mb4;再用source ebuy.sql导入。如果是 Windows 命令行先chcp 65001切到 UTF-8。已经导入的乱码数据只能重新导入没法直接修复。6. 用存储过程和主从复制把 Ebuy 数据库再推一步课程设计做完增删改查只是及格线。如果想让 Ebuy 易买网商城项目的数据库部分真正拿得出手有两个方向可以加一是用存储过程封装后台的月度统计二是用主从复制做读写分离的雏形。先看存储过程。后台经常要查「某个月每个分类的销售额」用一条 SQL 写出来很长封装成存储过程调用更干净。DELIMITER // CREATE PROCEDURE stat_category_sales(IN start_date DATE, IN end_date DATE) BEGIN SELECT c.name AS category_name, COUNT(DISTINCT o.id) AS order_count, SUM(oi.price * oi.num) AS total_sales FROM orders o JOIN order_item oi ON o.id oi.order_id JOIN product p ON oi.product_id p.id JOIN category c ON p.category_id c.id WHERE o.status IN (1, 2, 3) AND o.create_time start_date AND o.create_time DATE_ADD(end_date, INTERVAL 1 DAY) GROUP BY c.id, c.name ORDER BY total_sales DESC; END // DELIMITER ; -- 调用查 2025 年 1 月的分类销售 CALL stat_category_sales(2025-01-01, 2025-01-31);DELIMITER //是为了让 MySQL 把;当成存储过程内部语句而不是结束符。IN参数是两个日期DATE_ADD(end_date, INTERVAL 1 DAY)是为了把结束日期包含进去因为create_time是DATETIME直接 end_date会漏掉当天。status IN (1,2,3)只统计已付款、已发货、已完成的订单待付款和已取消不算销售额。这个存储过程在后台报表页直接CALL就行比在 Java 里拼 SQL 更稳。再说主从复制。Ebuy 项目如果前台读多写少可以配一主一从前台查询走从库下单走主库。MySQL 主从复制的核心是 binlog。-- 主库创建复制账号 CREATE USER repl% IDENTIFIED BY Repl_2025; GRANT REPLICATION SLAVE ON *.* TO repl%; FLUSH PRIVILEGES; -- 主库看 binlog 位置 SHOW MASTER STATUS;记下File和Position然后在从库执行CHANGE MASTER TO MASTER_HOST主库IP, MASTER_USERrepl, MASTER_PASSWORDRepl_2025, MASTER_LOG_FILEmysql-bin.000001, MASTER_LOG_POS154; START SLAVE; SHOW SLAVE STATUS\GSlave_IO_Running和Slave_SQL_Running两个都是Yes才算成功。如果Slave_SQL_Running是No看Last_Error常见的是主从数据不一致导致主键冲突。课程设计里配主从复制能加分但要注意从库是只读的前台写操作还是走主库。最后说一个我自己的习惯每次改完数据库结构先mysqldump备份一份再动手。mysqldump -u root -p --single-transaction --routines --triggers ebuy ebuy_backup_$(date %Y%m%d).sql--single-transaction保证 InnoDB 表备份时一致性--routines把存储过程也导出来--triggers导触发器。没有这个备份改错一个字段类型就可能要把整个项目重导一遍。这个习惯帮我省过至少三次通宵重做的时间。希望帮到你。本文还有配套的精品资源点击获取