Java超市购物系统课设全解:从数据库脚本到JDBC避坑实战 简介面向Java初学者与课程设计人群的超市购物系统完整工程包涵盖后端业务逻辑、数据库脚本与配套说明文档。系统以Java为主要开发语言采用MVC分层结构实现商品管理、购物车、订单结算、入库出库、销售统计等核心模块数据库部分包含商品表、用户表、订单表、库存表等典型关系型设计配合索引与ER关系说明便于理解从建表到联调的全过程。压缩包共165个文件以31个java源文件、105个class编译文件、5个jar依赖库及数据库文件ldf/mdf为主另有png界面截图、doc设计文档、xls数据表和html说明页整体约2.48MB结构紧凑适合本地运行调试。随包还提供需求分析、接口定义、测试记录等资料可直接用于课程设计或毕业设计参考。目前已有349人学习下载适合正在做JavaWeb项目或需要完整数据库示例的开发者学习使用。1. java超市购物系统先看数据库脚本再决定要不要改代码很多 Java 课设项目拿到手第一件事就是打开源码硬啃啃完发现跑不起来最后定位到是数据库没导入。这套 java 超市购物系统把 db 建库脚本、doc 文档和 src 源码分成三块最该先看的是 db 和 doc。它解决的核心问题是一套带数据库和文档的完整系统怎么在本地跑通对交课设、练 JDBC/MySQL 实战的人正好对症。项目本身是典型超市业务闭环前台注册登录、浏览商品、加购物车、下单后台管商品、库存、订单和用户。购物车和订单都落了表文档带字段说明和操作手册是最接近课堂作业规范的样子没缺表也不复杂。所以别急着改界面先把库跑起来再动手改需求。下面按功能分层、表设计、部署路径往下走坑集中到第5章。2. 系统功能与代码结构三层结构、七个核心类、一个入口2.1 功能清单前台用户和后台管理不走同一条分支这套系统按角色分成两条业务线也是答辩被问最多的第一题。前台用户线从注册登录、浏览商品、关键词搜索、按价格排序到加购物车、下单、查看自己的订单是一条完整的消费链路后台管理线管理员登录后做商品增删改查、库存调整、订单状态更新和用户管理。购物车在很多课设里都做成临时对象或内存集合但这里把购物车单独落了一张表好处是你可以直接查数据库演示“这个用户加了什么商品”坏处是并发写的时候容易脏数据——课设规模下不是问题但如果后面想改成 Web 版这个点需要重新设计。商品模块分类维护、上架下架、库存更新、按价格排序正好复习 java 排序购物车模块行项增删改查、清空、金额合计订单模块生成订单、订单明细、状态流转用户模块注册、登录、我的订单列表先看功能清单再进源码是判断代码结构最省时间的办法。因为每一个模块在代码里都应当能找到对应的类比如商品模块对应 ProductDao购物车对应 CartDao订单对应 OrderDao。如果找得到说明这份源码分层基本合格可以放心改如果所有逻辑都堆在 JFrame 的事件监听里那一进源码就会看到几百行 SQL 混着界面代码这种项目基本只能重写。2.2 三层结构为什么 CRUD 也要拆 ui/service/dao几乎每份课设源码都说自己用了三层架构实际一打开很多就是 JFrame 方法里直接写 JDBC。这套资源的分层相对标准ui 层放窗口和事件监听service 层放库存校验、金额计算这类业务规则dao 层放数据库增删改查。拆开的意义不是让包多起来而是当老师问“如果从 Swing 换 Web哪些类能留、哪些要重写”时你能按层答出来dao 层和数据模型可以留ui 层全部替换service 层基本不动。类名层级负责内容DBUtilutilConnection 的获取、复用与关闭LoginFrame / RegisterFrameui登录注册窗口与校验入口MainFrameui用户主界面商品浏览、购物车、订单入口AdminFrameui后台管理主界面ProductDaodao商品增删改查、按分类查询、库存更新CartDaodao购物车行项的增删改查OrderDaodao订单主表与明细写入、库存扣减拿到源码后先对照这张表找类能全找到说明这份代码可以放心改有一两个对不上也不是坏事至少你知道哪里需要补。判断分层是否合格还有一个笨办法看这个类里有没有 import java.sql。dao 层出现 sql 包是正常ui 层的类里出现 Statement、ResultSet 这类东西就是分层失守的信号。2.3 定位入口IDEA 打开后的第一步解压后先看顶层目录不急着运行。重点确认三块源码根目录、db 数据库脚本、doc 文档目录。然后在 IDEA 里 File → Open 选中源码目录如果项目里没有 pom.xml 也没有 build.gradle直接当普通项目打开。# 解压后先看顶层结构Windows 用 dirmacOS/Linux 用 ls # 预期看到类似目录 # src/ db/ doc/ lib/接下来在 src 下定位 Main 入口通常长这样// 入口Main.java public class Main { public static void main(String[] args) { // 把窗口创建放到事件分发线程避免跨线程操作界面 java.awt.EventQueue.invokeLater(() - { // 先出登录框登录成功后按角色跳到用户端或管理端 new LoginFrame().setVisible(true); }); } }这段代码的入口逻辑只有两件事用 EventQueue.invokeLater 保证窗口在事件分发线程上创建然后弹出 LoginFrame。登录框里会根据账号角色决定跳 MainFrame 还是 AdminFrame这个分支在 LoginFrame 的登录方法里有兴趣可以断点跟一遍。IDEA 里直接右键 Main → Run 能跑但第一次大概率报找不到驱动这不怪你是 lib 没挂上解法在第 4.4 和第 5 章。想快速验证环境最好的做法是在 LoginFrame 构造方法里打断点点 Debug 而不是 Run能走到断点说明 JDK 和编译没问题。如果打开后发现是 Web 工程入口在 web.xml 或 application.properties不是 Main这个识别细节放在第 6 章。到这里骨架已经摸清下一步钻进数据库脚本把表结构和这套分层对应起来。3. 数据库设计7 张表撑起“商品-购物车-订单”闭环3.1 建库与核心表字符集和金额字段是两个决定性选择db 目录下的 shop.sql 已经把建表和数据初始化写好了。拆开看业务一共 7 张表t_admin、t_user、t_category、t_product、t_cart、t_order、t_order_item。购物车单独成表是这份资源的特色所以讲数据库设计时把 t_cart 也算进去。先看建库语句-- 建库字符集直接指定 utf8mb4否则中文和特殊符号都可能出问题 CREATE DATABASE IF NOT EXISTS shop DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE shop; CREATE TABLE t_user ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(50) NOT NULL, phone VARCHAR(20), reg_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB;建库语句有两个关键选择。第一是字符集用 utf8mb4 而不是 utf8库里要存商品名、用户地址这类中文utf8 在 MySQL 里实际是 utf8mb3遇到特殊符号会报错或乱码。第二是主键用 INT 自增课设量级完全够用文档里如果写的是 BIGINT 也不影响只是预留空间更大。t_user 里 username 建了唯一索引注册时重复用户名会被数据库拦下来代码里再提示一次双重校验。商品表的结构更值得看CREATE TABLE t_product ( id INT AUTO_INCREMENT PRIMARY KEY, cate_id INT NOT NULL, pname VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, image VARCHAR(255), status TINYINT DEFAULT 1 ) ENGINEInnoDB;商品表两个细节容易被忽略。price 用 DECIMAL(10,2) 而不是 float/double浮点数在金额计算上有精度误差数据库 SQL 里金额字段的标准答案就是 DECIMALstatus 字段是软删除开关值为 1 上架、0 下架后续删除商品走 UPDATE 而不是 DELETE就是为了不被订单明细的外键挡住。这两个点也是文档数据字典里反复强调的内容答辩时主动讲出来是加分项。3.2 订单与明细为什么一条订单要拆两张表订单设计是这份资源里最该看的部分。一条订单里有多个商品如果只放一张表要么每个商品一行导致订单头重复要么把商品塞进一个文本字段变成黑匣子。正确做法是拆两张t_order 存订单头t_order_item 存明细行。CREATE TABLE t_order ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, total_amount DECIMAL(10,2) NOT NULL, status VARCHAR(20) DEFAULT 待付款, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_order_user FOREIGN KEY (user_id) REFERENCES t_user(id) ) ENGINEInnoDB; CREATE TABLE t_order_item ( id INT AUTO_INCREMENT PRIMARY KEY, order_id INT NOT NULL, product_id INT NOT NULL, qty INT NOT NULL, price DECIMAL(10,2) NOT NULL, CONSTRAINT fk_item_order FOREIGN KEY (order_id) REFERENCES t_order(id), CONSTRAINT fk_item_product FOREIGN KEY (product_id) REFERENCES t_product(id) ) ENGINEInnoDB;这段脚本把外键也建了。外键的作用是数据库层面保证 t_order_item 不会挂到不存在的订单上代价是物理删除商品时会被约束拦住所以商品下架只能改 status不能 DELETE。注意 t_order_item.price 是冗余存储商品价格可能变订单明细必须保留下单时价格这是数据一致性里很常见的讨论点文档里如果提到“下单快照”这个概念指的就是这个字段。外键、快照、软删除三个词串起来就是答辩时订单设计的完整话术。3.3 初始化数据管理员账号和测试商品从哪来脚本末尾一般会带初始数据至少一个管理员、几个分类和一批测试商品。建议先原样导入跑通不要第一步就改密码。下面是通用初始化示例具体内容以 db/shop.sql 为准-- 初始化数据密码是明文的说明这套资源定位是教学演示 INSERT INTO t_admin (username, password) VALUES (admin, admin123); INSERT INTO t_category (id, cate_name) VALUES (1, 饮料), (2, 零食), (3, 日用品); INSERT INTO t_product (cate_id, pname, price, stock) VALUES (1, 农夫山泉, 2.00, 100), (2, 乐事薯片, 8.50, 50), (3, 清风抽纸, 4.50, 80);初始化数据里有三个约定俗成的点。管理员密码如果是明文说明它是教学和课设演示用的商用必须改为加盐哈希插入商品的顺序不能乱因为 t_product.cate_id 指向 t_category.id先有分类才能有商品stock 库存直接用整数字段下单扣减时用 UPDATE t_product SET stock stock - ? 这种原子操作别先 SELECT 出来再减回去并发场景会超卖。看完这三段 SQL第 5 章里一半的坑已经提前排掉了字符集、金额精度、外键、软删除。接下来进入正题把项目跑起来。4. 部署与运行JDK、MySQL、IDEA 三件套的配置顺序与参数含义4.1 版本选型JDK 8 配 MySQL 5.7/8.0 是不出错的组合跑课设最怕环境比代码先翻车。拆这份资源时用的是 JDK 8 搭配 MySQL 8.0配合 mysql-connector-java 8.x 驱动全程没有兼容问题。JDK 9 以上也能跑 Swing但模块化和 classpath 行为有变化没必要在课设上给自己加难度MySQL 5.7 配 8.x 驱动也能用反过来旧驱动连 MySQL 8 会踩第 5 章认证协议的坑。三个依赖装好后先确认版本。# 三个依赖装好后先确认版本别等报错才回来看 java -version mysql --version # 预期看到类似输出版本不必完全一致 # java version 1.8.0_202 # mysql Ver 8.0.30java -version 报错说明 JAVA_HOME 没配好这是 java 环境变量配置最常见的问题mysql --version 能显示信息只代表客户端存在服务是否在跑要实际连一次数据库才能确认。4.2 导入数据库source 之前先确认字符集和路径数据库脚本的导入顺序决定后面排查成本。进入 MySQL 客户端后先建库再 source 脚本接着立刻验证表数量和数据条数。mysql -u root -p # 登录后依次执行 source D:/workspace/shop/db/shop.sql; # 验证是否导入成功 USE shop; SHOW TABLES; SELECT COUNT(*) FROM t_product;source 的路径要用正斜杠或双反斜杠路径里尽量不要有中文目录名否则部分 Windows 客户端会解析失败。SHOW TABLES 应该看到 7 张表SELECT COUNT(*) 应该返回初始化数据的数量不是 0。如果第二步报错把报错行号对一下问题通常在第一句建库语句上。提示如果脚本是旧版本可能只有建表没有建库语句那就先手动执行 CREATE DATABASE shop CHARACTER SET utf8mb4; 再 USE shop; 最后 source。4.3 改连接参数DBUtil 里的三个参数决定生死导入成功后真正和连数据库相关的地方只有一处src/util/DBUtil.java。这份源码的连接串一般长这样// src/util/DBUtil.java public class DBUtil { // useSSL、serverTimezone、characterEncoding 三个参数缺一不可 private static final String URL jdbc:mysql://localhost:3306/shop ?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8; private static final String USER root; private static final String PASSWORD 123456; static { try { // 驱动名 8.x 统一用 cj 版本 Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new RuntimeException(找不到驱动确认 lib 目录里有 mysql-connector-java 8.x 的 jar, e); } } }这段代码至少要看懂三个参数。useSSLfalse 关掉 SSL 警告本地开发连接没必要加密serverTimezoneAsia/Shanghai 让 JDBC 知道时区省得时间字段对不上或直接报 serverTimezone 错误characterEncodingutf8 保证中文不乱码。USER 和 PASSWORD 改成你本机的密码为空也照实写。驱动名 8.x 是 com.mysql.cj.jdbc.Driver5.7 时代是 com.mysql.jdbc.Driver资源包里如果是老写法8.x 驱动也能兼容但建议统一成 cj 版。4.4 把驱动 jar 挂进 classpath再 Run Main源码本身没有 Maven连接 MySQL 的驱动是一个 jar 包放在 lib 目录下。IDEA 默认不会把 lib 自动加进依赖这一步漏掉就是第 5 章第一条坑。操作方式是File → Project Structure → Libraries → → Java → 选中 lib 目录 → OK。如果不喜欢界面点命令行也能跑。# Windows 用分号分隔 classpathmacOS/Linux 用冒号 javac -encoding UTF-8 -cp lib/mysql-connector-java-8.x.jar;src src/Main.java java -cp lib/mysql-connector-java-8.x.jar;src Main-encoding UTF-8 控制编译时的字符集不写它在中文 Windows 上经常编出一堆警告classpath 里同时带上了 srcJava 会从源码目录里找 Main 类。到这里正常应该看到登录窗口弹出。如果没等到窗口直接跳到第 5 章按顺序查。课设用 DriverManager 没问题生产上没人这么干连接池才是标配第 6 章讲怎么往这个方向改。5. 避坑排查启动即翻车这 5 个问题占了课设八成报错这几条是我拆课设常被问的血泪经验报错高度集中在这五个点上。每条按现象、原因、解决三步说清楚对号入座就行。5.1 驱动 ClassNotFoundExceptionlib 目录形同虚设现象运行 Main 一秒不到控制台直接抛 java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver或者报 SQLException: No suitable driver found。原因驱动 jar 文件确实在 lib 目录里但 IDEA 没把它加入项目依赖命令行运行时也没把 jar 写进 -cp。驱动类没有被类加载器看到Class.forName 自然找不到。解决IDEA 里按 4.4 的路径加依赖Project Structure → Modules → Dependencies → → JARs or directories → 选中 lib 目录。加完确认 Dependencies 列表里有这个 jar然后重启 Run。命令行跑则检查 -cp 里路径是否正确Windows 的分号别写成冒号。这一步做完大多数“启动即翻车”直接消失。5.2 中文乱码变问号连接串和库的字符集必须三级一致现象界面上输入中文没问题写入 MySQL 后变成一串问号或者从库里查出来直接乱码。原因字符集断在三个地方任一环连接串没有 characterEncodingutf8、建库用的是 utf8 而不是 utf8mb4、IDEA 控制台或源码文件本身是 GBK 编码。三层里只要有一层不对中文就保不住。解决先改连接串按 4.3 的 URL 来再确认建库脚本utf8mb4 优先最后把 IDEA 的 File Encoding 统一设成 UTF-8。已经建好的库可以用 SQL 修正ALTER DATABASE shop CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; ALTER TABLE t_product CONVERT TO CHARACTER SET utf8mb4;ALTER TABLE 那条只改表字段的字符集也要跟着变CONVERT TO 会把已有数据一并转换这是 mysql 数据库修改结构时最常用的补救手段。改完重启程序重新插入一条中文数据验证。5.3 MySQL 8 认证协议导致连接失败现象驱动、classpath、密码都对但连接时抛 Access denied for user rootlocalhost或者 MySQL 8 报 Public Key Retrieval is not allowed。原因MySQL 8.0 默认的身份认证插件是 caching_sha2_password5.x 时代的驱动不认识另外 allowPublicKeyRetrieval 参数默认 false使用 RSA 加密传输时会拒绝获取公钥。解决两种任选。第一种URL 追加 allowPublicKeyRetrievaltrue第二种把账号认证改回 native执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;第二种方案会改动 MySQL 账号认证方式对本地课设环境没有影响但如果在团队共用数据库上操作先问管理员。改完一定要执行 FLUSH PRIVILEGES 让它立即生效。这个问题只出现在 MySQL 8.0 上5.7 不会撞见。5.4 时区报错时间字段差 8 小时或直接拒绝连接现象连接时报 The server time zone value is unrecognized或者订单表里的时间比本地时间差 8 小时。原因JDBC 驱动在读取时间时会问 MySQL 的全局时区MySQL 默认是系统时区很多 Windows 环境解析不了中文时区名就直接抛异常能连上的Server 和 JVM 时区不一致就会出现 8 小时偏差。解决URL 里加 serverTimezoneAsia/Shanghai 是最直接的解法。如果还有偏差可以在 MySQL 端统一SET GLOBAL time_zone 08:00;连接串参数不只影响时间字段也决定 timestamp 的读写解释所以这条参数必须和业务地区一致。部署到国外服务器时把 Asia/Shanghai 换成对应时区就行。5.5 列名对不上源码和脚本不是同一版本现象程序跑到某个查询就报 java.sql.SQLSyntaxErrorException: Unknown column xxx in field list或者明明导了库查询结果却是 0 行。原因课设资源在流传过程中经历过多次改名最常见的是 stock 改 inventory、cate_id 改 category_id 这类不一致。源码是按旧表写的而你导入了新表脚本或者反过来代码里的字段在表里根本不存在。解决以 db/shop.sql 为准。先在客户端看真实列名DESC t_product; 再全局搜源码里出现的同名 SQL 字段逐个对齐。改代码字段还是改表结构哪个改动小就改哪个涉及库存这种有数据的列优先改代码。改列名之前先给表结构拍个照这就是后悔药。坑位最直接检查点一句话解法驱动找不到Project Structure → Dependencies把 lib 挂进 classpath中文乱码URL 参数、库字符集utf8mb4 characterEncodingutf8认证失败MySQL 8 旧驱动加 allowPublicKeyRetrievaltrue 或改认证插件时区报错serverTimezoneURL 加 serverTimezoneAsia/Shanghai列名对不上DESC 表名以 db/shop.sql 字段为准改代码如果你卡住的位置不在上面五条里把堆栈第一行放到 IDEA 控制台搜类名。课设 90% 的报错到最后都是配置和 classpath 问题真正源码 bug 反而少。最怕的是不看日志就开始一行行翻代码翻完更乱。6. 进阶用法从“能跑”到“能答辩”先给下单流程补一个事务课设能跑不算完答辩老师最常戳的软肋是下单扣库存时如果中间抛异常数据还能对得上吗。如果你发现 OrderDao 里每条 UPDATE 都是自动提交的恭喜你这里是全项目最值得改的二十行。把下单方法改成事务版// OrderDao.java 中的下单核心方法 public boolean createOrder(Order order, ListOrderItem items) { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 1. 关闭自动提交合并为一个事务 insertOrder(conn, order); // 2. 插入订单头 insertItems(conn, items); // 3. 插入所有明细 reduceStock(conn, items); // 4. 扣减库存 conn.commit(); // 5. 全部成功才提交 return true; } catch (Exception e) { if (conn ! null) { conn.rollback(); // 6. 任何一步异常整体回滚 } throw new RuntimeException(下单失败库存已回滚, e); } finally { DBUtil.close(conn); } }这段代码的逻辑完全对得上第 3 章的订单表设计订单头和明细在同一个事务里写入库存扣减也放进来commit 之前谁出问题都 rollback不会出现订单记了库存没减这种半截数据。setAutoCommit(false) 是 JDBC 提供的开关Connection 在 DriverManager 模式下每次都是新连接事务边界就从这里开始。很多 java 面试题会问 JDBC 如何保证数据一致性与其背答案不如把这段代码放进你的项目里能讲的点比增删改查多得多。后续如果觉得 DriverManager 的连接管理太啰嗦可以往数据库连接池方向改Druid 或 HikariCP 选一个把 DBUtil 换成 DataSource 获取连接业务层代码不用动。另一个提升答辩观感的小技巧是把 doc 里的数据字典表格字段改成英文名、和数据库完全对齐再在文档前两页放一张 ER 图。答辩老师翻文档时间很短图比字管用。如果你发现拿到的其实是 Web 版入口在 web.xml 或 application.properties数据库脚本照样能复用事务这段代码同样适用——先跑通再谈界面。从那以后我每次拿到课设源码第一件事永远是先把数据库脚本完整跑一遍再打开代码核对字段最后才碰界面和功能。这个顺序让我少踩了很多坑希望帮到你。本文还有配套的精品资源点击获取