简介:这是一份基于JavaWeb的超市管理系统毕业设计资源,完整包含项目源码与数据库脚本,面向计算机、通信、人工智能、自动化等专业的学生、教师及从业者,适用于毕业设计、课程设计、期末大作业及自学进阶。项目源于个人毕设,答辩评审达98分,代码经过调试测试可正常运行,具备良好的学习借鉴价值,也便于在基础模块上二次开发实现个性化功能。压缩包共64个文件,主要有28个Java类文件承担业务逻辑,21个JSP页面构建前端交互,10个XML文件管理配置,另含SQL数据库脚本、MD说明文档、properties配置及图片素材等,整体582KB,目录结构清晰易检索。目前已有141人学习,对于需要快速搭建超市管理系统或深入理解JavaWeb开发流程的读者,不失为一套紧凑、实用的参考资料。
1. 选题选到头秃,不如先看看这套 JavaWeb 超市管理系统能给你什么
每年毕设季,JavaWeb 方向的项目几乎人手一个。选电商系统怕被老师说没新意,选图书管理又嫌太单薄。超市管理系统是一个很微妙的折中选择:它的业务量级刚好卡在"再简单就没东西写、再复杂就不是一个人能写完"的区间,既能覆盖商品管理、进货、销售、库存这些通用业务域,又能在答辩时讲出清晰的表关联和事务逻辑,不会因为业务太抽象而说不清楚。
标题里直接点明了交付物是项目源码和数据库脚本,意味着你拿到的是一个完整的、能直接导入 IDE 跑起来的工程,而不是只有业务概念图或者零散代码片段。这套方案的典型技术栈由 JSP + Servlet + JDBC(或 MyBatis)+ MySQL 组成,特点是结构直观,每一层都能单独讲明白。本文接下来的内容,会从业务表怎么拆、源码怎么导入、脚本怎么初始化,到答辩前怎么加亮点讲透。无论你是两手空空开始搭环境,还是已经能写一点 Java 但卡在项目整合,这套流程都能直接照着做。
2. 从数据库脚本看超市系统怎么拆表:先看懂设计再动手
2.1 超市管理系统的核心业务闭环:进、销、存、收银
拿到一套源码先别急着运行,第一步应该打开数据库脚本看表结构。超市管理系统看起来模块多,本质上就是一条"采购入库 → 库存台账 → 前台销售 → 销售出库 → 库存扣减"的链路,外加供应商、用户权限、订单报表这些支撑模块。一套毕业设计级别的脚本,很少会超过十五张表,因为再多表,学生自己做不出来,老师也不信是独立完成的。
识别一套超市系统是否"内行",就看它的销售单怎么设计。常见的错误做法是把商品名称、价格、数量直接存在订单表里,这样每张订单只能卖一件商品。正确做法是拆成销售单主表(记录订单号、收银员、总金额、下单时间)和销售单明细表(记录商品 ID、单价、数量、小计),两张表通过订单号关联。这也是超市收银台场景的基本事实:一次扫描多件商品,出一个小票,这就是一主多从的表结构。
商品管理的核心是分类。不要小看商品分类表,很多同学嫌麻烦,直接在商品表里存一个"category_name"字符串字段,后果是你没办法做分类统计,改分类名称还要批量 update。正规做法是独立的商品分类表,只存分类 ID 和分类名,商品表里存 category_id,再做外键或者逻辑关联。这样的设计支持你后面实现"按分类查看库存""分类销售占比图"等功能,这些都是答辩加分点。
2.2 核心数据表清单与设计意图对照
下面这份表清单不是空想,而是从"超市管理系统"这个需求反推出来的标准表集合。你在打开数据库脚本后,对照这份清单能迅速判断这套源码有没有删减功能。
| 表名 | 核心字段 | 设计意图 |
|---|---|---|
| t_user | id, username, password, real_name, role | 区分管理员和收银员,权限校验入口 |
| t_category | id, category_name, description | 商品分类,支撑统计报表 |
| t_product | id, category_id, product_name, price, stock | 商品主表,库存字段冗余在此,保证查询速度 |
| t_supplier | id, supplier_name, contact, phone | 供应商资料,支撑采购单 |
| t_purchase | id, purchase_no, supplier_id, total, create_time | 采购单主表,记录一次进货 |
| t_purchase_item | id, purchase_id, product_id, price, quantity | 采购单明细,入库时批量操作商品库存 |
| t_sale | id, sale_no, user_id, total, sale_time | 销售单主表,收银台一次结账对应一条 |
| t_sale_item | id, sale_id, product_id, price, quantity | 销售单明细,逐件扣减库存 |
| t_stock_log | id, product_id, change_type, change_qty, create_time | 库存流水表,可选但很加分 |
值得注意的地方是库存字段。很多东西系统喜欢把库存设计成"读库存表,然后 join 商品表",这导致每一次商品列表加载都要做两表关联。这套设计在商品表里直接冗余一个 stock 字段,写入销量时同步扣减,只在库存变动时写一条流水日志。两个表能完成任务,就不要拆三个表,这是毕业设计合理的工作量边界。
2.3 从脚本里抄一段建表语句:注意看 InnoDB 和 utf8mb4
数据库脚本的价值就在于可以直接执行,但直接执行不等于一定会成功。下面这段建表脚本是一个典型的"商品分类表 + 商品表"结构,你可以对照自己的脚本看有没有遗漏。
CREATE TABLE `t_category` ( `id` int(11) NOT NULL AUTO_INCREMENT, `category_name` varchar(50) NOT NULL COMMENT '分类名称', `description` varchar(255) DEFAULT NULL COMMENT '分类描述', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品分类表'; CREATE TABLE `t_product` ( `id` int(11) NOT NULL AUTO_INCREMENT, `category_id` int(11) NOT NULL COMMENT '商品分类ID', `product_name` varchar(100) NOT NULL COMMENT '商品名称', `price` decimal(10,2) NOT NULL COMMENT '售价', `stock` int(11) NOT NULL DEFAULT '0' COMMENT '当前库存', `create_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`), KEY `idx_category_id` (`category_id`), CONSTRAINT `fk_product_category` FOREIGN KEY (`category_id`) REFERENCES `t_category` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';这段脚本里有两个细节值得注意。第一是 ENGINE=InnoDB,超市管理系统涉及销售单和库存扣减,必须要有事务支持,一个销售行为要同时插入主表和明细表、扣减商品库存,任何一个步骤失败都应该整体回滚,用 MyISAM 做不到这一点。第二是 CHARSET=utf8mb4,如果你的脚本还是 utf8,后面往商品表里塞"咖啡"这类带生僻字或表情符号的数据,会直接报 Incorrect string value,这是老脚本的常见病。外键 fk_product_category 表示商品表依赖分类表,这解释了为什么导入脚本时报外键错误——你必须先建主表再建从表,或者先执行 SET FOREIGN_KEY_CHECKS=0。
3. 用 IDEA 把源码跑起来:导入、配置、启动三步走
3.1 idea运行javaweb项目配置:用 IDEA 打开源码的两种典型方式
毕业设计项目源码的交付形式就两种:Maven 工程或者普通 Web 工程。你现在能拿到的源码大多数是 Maven 结构,因为 Maven 管理依赖方便,只用把 pom.xml 一导入,所有的 Jar 包自动下载。少部分老式项目是 WebContent 目录结构,手动把 lib 包放进 WEB-INF 下。
用 IDEA 打开 Maven 工程的做法是:File → New → Project from Existing Sources,然后选中源码根目录下的 pom.xml 文件,IDEA 会识别为 Maven 工程。这里有个细节:让 IDEA 打开的是 pom.xml 而不是整个文件夹,这样可以避免把 .idea 配置缓存一起导入导致环境错乱。导入后,IDEA 会自动读取 pom.xml 并下载依赖,打开 Maven 侧边栏看到 Dependencies 没有报红,就说明依赖就绪。
如果源码是非 Maven 老工程,做法是 File → New → Project from Existing Sources,选根目录后一路 Next,直到向导提示识别到 Web 模块,这时把 Web 模块勾上。这种工程的全部 Jar 包在 WEB-INF/lib 目录下,你不需要额外下载任何依赖。启动前打开 Project Structure → Artifacts,确认 Web 应用归档的 lib 目录下已经包含了所有 Jar 包。这一步经常被忽略,导致 Tomcat 启动后找不到 Servlet 类报 NoClassDefFoundError。
3.2 改数据库连接配置:不同框架改不同的文件
源码能不能运行起来,数据库连接配置是关键。老式 JSP + Servlet + JDBC 项目的连接配置写在 src 目录下的 db.properties 或 jdbc.properties 文件里;用 MyBatis 的工程则在 mybatis-config.xml 或 spring-mybatis.xml 里配了数据源。无论哪种形式,你都要把数据库地址、账号、密码改成自己的本地环境。以最常见的 properties 文件为例:
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/supermarket_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=123456注意这里的 driver 用的 com.mysql.cj.jdbc.Driver。早期教程喜欢的 com.mysql.jdbc.Driver 在 MySQL 8.0 之后已经移除了,新版本驱动必须用 cj 这个包名。URL 后面的 serverTimezone=Asia/Shanghai 也必须有,否则系统当前时间和数据库时区对不上,保存销售单时会报 SQLException 说日期转换异常。useUnicode=true 和 characterEncoding=utf8 是防中文乱码的标准配置,不能省略。
改完配置先不要急着启动,用数据库客户端工具检查一下这个 URL 里的库名是否存在。如果脚本是超市系统.sql,你需要先在 Navicat 或命令行里创建对应的数据库,然后导入脚本。如果你直接复制了别人工程里的配置,结果他用的库名是 test,你的数据库里没有 test 这个库,启动时会在建立数据源连接这一步报错,页面直接 500。
3.3 配置 Tomcat 并启动:IDEA 里跑 Web 工程的步骤清单
在 IDEA 里运行 JavaWeb 项目,核心动作是配置一个本地 Tomcat 服务器。打开 Run → Edit Configurations,点加号找到 Tomcat Server → Local。如果下拉列表里没有这个选项,说明你还没有添加 Tomcat,需要先到 Settings → Build, Execution, Deployment → Application Servers 里把自己本地的 Tomcat 解压目录指定进去。这一步要求你已经下载了 Tomcat 8.5 或 9.0 并解压到本地目录,IDEA 不会自动帮你下载 Tomcat。
配置页面的 Deploy 标签页里,Application server 选 Tomcat,下方 Context Path 建议设置为 /,或者保持源码里已有的路径。这里的 Context Path 就是后续访问 URL 中的路径部分。如果你设置成 /supermarket,访问地址就是 http://localhost:8080/supermarket/;设置成 / 则直接访问 http://localhost:8080/。很多同学启动成功后访问页面 404,就是因为这里的虚拟路径跟实际访问路径没对上。
配置完成后点击右上角的 Debug 或 Run 按钮。第一次启动建议用 Debug 模式,因为控制台会打印完整的异常堆栈,排错信息量更大。看到控制台出现 "Connected to server" 和 "Starting ProtocolHandler" 字样,说明 Tomcat 已经启动成功。启动过程中最快的失败点是端口占用,解决方法下文避坑章节专门讲。
4. 把数据库脚本变成你的数据:导入、验证、初始化三部曲
4.1 数据导入的两个入口:命令行执行和 GUI 工具执行
数据库脚本的导入是整个项目跑通的前提。你拿到的 .sql 文件里通常会包含建库语句、建表语句和预设数据三部分。打开这个文件,第一眼重点看开头有没有 CREATE DATABASE 语句。如果有,导入后会自动创建数据库;如果没有,你就要先手动创建空库,再选中它导入。
命令行导入方式是基础功,打开终端执行如下命令:
mysql -u root -p < supermarket.sql执行后系统会提示输入 root 密码,输入密码回车后没有任何输出就是成功。这里有个容易被忽略的点:命令里的 < 是重定向符号,表示把文件内容作为 mysql 客户端的输入,而不是传给某个具体数据库。所以这个命令要求 supermarket.sql 文件本身包含建库语句,否则数据会导进默认的数据库,导致表创建在其他库下面。
图形化工具导入更直观。用 Navicat 的话,右键连接下的某个数据库,选"运行 SQL 文件",然后在弹出的窗口里选择脚本路径。这里要留心脚本开头有没有 USE database_name; 语句。如果有 USE,脚本会自动切换到你指定的库;如果没有,Navicat 会默认在当前选中的数据库里执行,一旦选错库,表就全建错地方了。
4.2 脚本导入报错的三个高频原因:外键顺序、utf8mb4 和 SQL 模式
导入脚本报错是概率极高的事,先别急着怀疑脚本坏。第一个常见报错是 Can't create table 某某表 (errno: 150),这基本就是外键顺序问题。脚本里有外键约束的表必须先建主表、再建从表,如果脚本里表的排列顺序不满足这个条件,导入就会中断。解决办法是在脚本开头加上这段:
SET FOREIGN_KEY_CHECKS=0;这条命令临时关闭外键检查,导入结束后再设置回 1。它的副作用是如果脚本中途某张表创建失败,后续表依然能继续创建,因此导入完成后要检查日志确认全部成功,而不是只看结尾有没有报红。
第二个常见报错是 Illegal mix of collations 或者 Unknown character set。这是因为脚本文档规定的字符集 utf8mb4 和数据库默认字符集不一致导致的。解决办法有两种:一是给库设置全局默认,执行 ALTER DATABASE 你的库名 CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; 二是打开脚本文件另存为 UTF-8 编码后重新导入。这里最隐蔽的坑是 Windows 记事本打开脚本文件再保存,文件会被加上 BOM 头,导致 MySQL 解析到第一行出现乱码或语法错误。推荐用 VS Code 或 Notepad++ 打开脚本,另存时选择"UTF-8 无 BOM"。
第三个报错是 SQL 语法错误,提示某个字段类型不对,这跟 MySQL 版本有关。老脚本用的 varchar 定义在 5.7 没问题,但随脚本附带的 MySQL 8.0 将 sql_mode 默认设置为严格模式,老的日期格式如 '0000-00-00' 会直接报错。这类问题的通用解法是临时把 sql_mode 改宽松:
SET sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO';刀下留情的做法是先执行这条语句再导入脚本。导入结束后重启 MySQL 服务会恢复默认模式。
4.3 初始化数据要不要全保留:管理员密码和库存数据怎么改
脚本导入后,系统自带的管理员账号密码通常是 admin / admin 或者 admin / 123456,这些默认值在脚本的 INSERT 语句里明文写着。毕业后你如果真要基于这套系统做二次开发,第一件事就是改掉默认密码。有两种改法:一种是在数据库表里直接用 UPDATE 语句改字段值,另一种是去找用户表插入数据的 SQL 位置,把密码字段替换成 MD5 加密后的字符串。
密码存储方式也要看清楚。如果 t_user 表里密码字段直接保存明文,说明这套源码的逻辑是登录时明文比对;如果保存的是一串 32 位字母数字,就是标准 MD5 加密。毕设答辩时老师很可能问"密码怎么存储的",你需要能准确回答。如果脚本中用户表插入的密码是明文,而代码里的登录逻辑却用 MD5 加密比对,那就会导致永远登录不进去。翻车情况大多是这两种格式混用了,排查时优先看注册或登录的 Service 层代码。
库存数据的初始值不需要全部清空重造。脚本自带的商品数据往往包含"可乐、薯片、纸巾"这类供演示的商品,以及对应的分类、供应商、模拟销量。建议保留它们直到系统跑通,因为插入销售单、查询库存这类功能需要有数据支撑才能看到效果。等到你的系统适配了新的商品清单,再清空业务表重新录入即可,用户表别清空,你的账号还在里面。
5. JavaWeb 超市管理系统避坑指南:5 个翻车高发点排查和解决
5.1 Tomcat 启动报端口 8080 被占用,换个端口有没有副作用
现象是启动 Tomcat 时控制台抛 java.net.BindException: Address already in use: JVM_Bind。原因不外乎两种情况:本机已经跑了一个 Tomcat 或其他服务占用了 8080 端口;或者上一次 IDEA 异常退出,残留的 Java 进程仍然占着端口。解决方法是打开 conf/server.xml,找到 Connector port="8080" 改为 8081 或者 9090。注意改完之后,访问地址的端口要同步改。另一个更省事的排查方式是在终端执行 netstat -ano | findstr 8080,看到 PID 后进任务管理器结束对应进程。
换端口有没有副作用?如果你的代码里写了绝对路径带端口跳转,改端口后会出现页面跳转 404。JSP 或者 Servlet 里如果使用 request.getContextPath() 拼接 URL 就没问题,因为它是相对当前应用上下文的,不依赖端口。如果你在 JavaScript 里写死了 localhost:8080,那就需要全局搜索替换。
5.2 页面能打开但全是 404,先查模块发布还是路径映射问题
现象是 Tomcat 成功启动,输入地址后返回 404,甚至 IDEA 控制台显示页面资源找不到。原因大多是部署的 Application Context 跟实际访问路径不一致,或者 Servlet 上的 @WebServlet("/xxx") 注解路径和前端表单 action 指向的路径不一致。解决分两步:第一步确认 IDEA 中 Deploy 页签 Application context 是 /supermarket 还是 /,然后访问对应 URL;第二步打开前端 JSP 文件,搜索 action= 和 url= 开头,把路径和注解逐一对照。
如果你用的是 Servlet 3.0 之后的注解方式配置,另外一个隐藏点就是注解扫描的包路径。用 web.xml 配置的监听程序写在 web.xml 里,用注解配置的必须保证 Java 类上标注了 @WebServlet("/login") 且该类在 Spring 扫描路径下。老项目的 web.xml 里配置了 servlet-mapping 的,检查 web.xml 文件是否损坏或被误删,这个文件丢了页面一定 404。
5.3 登录成功后页面中文全部乱码,是 POST 乱码还是页面响应乱码
现象是销售单和商品列表的中文都显示为问号或乱码。原因多半是请求和响应的编码不一致,POST 请求提交到 Servlet 时 Spring 或 Servlet 容器用默认 ISO-8859-1 解码,中文必然乱码。解决方法是给 Servlet 或者过滤器里加如下代码:
request.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8");正确的做法是写一个 EncodingFilter,实现 javax.servlet.Filter 接口,在 doFilter 方法里对请求和响应统一设置编码,然后放行。超市管理系统涉及的商品名、供应商名全部是中文,这类乱码问题如果只改局部请求是不够的,一个销售单提交的路径可能穿过前置过滤器和多个 Servlet,所以全局过滤器是根治方案。还有一个细节,页面上中文正常但 JSP 脚本片段里输出的中文乱码,那就是 JSP 页面本身的 file encoding 不是 UTF-8,用 VS Code 打开另存为 UTF-8 解决。
5.4 数据库连不上:驱动 Jar 包缺失还是新驱动路径改名
现象是启动时控制台报 ClassNotFoundException: com.mysql.jdbc.Driver。原因和 3.2 节提到的一致,老项目导入的 Jar 包是最老的 mysql-connector-java-5.x 版本。MySQL 8.0 之后推荐驱动类名是 com.mysql.cj.jdbc.Driver,如果你使用的是新版 MySQL 连接驱动,甚至连 jar 包名都变成了 mysql-connector-j。解决办法是把数据库连接配置里的 driver 改成新名称,然后在 Maven 依赖里确认连接驱动的版本号,或直接把驱动 Jar 包放进 WEB-INF/lib。判定依赖是否加载成功的方法是在项目里按住 Ctrl 点击类名,如果能定位到 jar 包里的 class 文件就是加载成功,否则就是没引入成功。
5.5 源码运行需要 JDK 版本不匹配,怎么用 IDEA 切换 JDK
现象是编译报错提示 diamond operator 或 lambda 表达式不可用,在旧版 JDK 环境下编译新代码会遇到。超市管理系统这类 JavaWeb 项目如果用了 JDK 8 的语法特性,则要求本机至少装 JDK 8 以上版本。解决办法是 Project Structure 中设置 Project SDK 和 Project language level 保持一致,并在 Settings → Java Compiler 里确认 Target bytecode version 也是对应版本。Tomcat 本身也有版本要求,Tomcat 9 对应 JDK 8 及以上,Tomcat 10 对应 JDK 11 及以上,如果项目还在用 javax.servlet 包,建议原本用 Tomcat 8.5 或 9.0,不要直接上 Tomcat 10,否则 Servlet API 从 javax 改成 jakarta,代码全部报红。这个坑非常隐蔽,网上不少新教程默认下载 Tomcat 10,结果老项目直接编译失败。
6. 答辩前给系统加一个亮点功能:基于 Filter 的登录拦截与操作日志
到了这个阶段,系统能跑通,功能完整,但是要让自己在答辩时更有底气,最好在现有源码上再加一个小功能。这里推荐一个改动面小但技术上能讲的亮点:用 Filter 实现的双重拦截——未登录用户不能访问管理页面,同时记录每次登录时间和操作路径。
public class LoginFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; HttpSession session = req.getSession(false); String uri = req.getRequestURI(); if (uri.endsWith("login.jsp") || uri.endsWith("LoginServlet") || uri.endsWith(".css") || uri.endsWith(".js")) { chain.doFilter(request, response); return; } if (session == null || session.getAttribute("user") == null) { resp.sendRedirect(req.getContextPath() + "/login.jsp"); return; } chain.doFilter(request, response); } }这段 Filter 的逻辑分三段。第一段放行少数不需要登录即可访问的路径,包括登录页本身、登录处理请求和静态资源。第二段检查会话里的登录标记,如果为空就重新定向回登录页,这是典型的"未登录拦截"。第三段放行,让请求继续传到后面的 Servlet。过滤器的关键参数在 web.xml 中配置映射路径,一般配置为 /* 拦截全部请求,再配合白名单思路放行登录页。
这个功能的加分点在于你能讲清楚三个问题:为什么用 Filter 而不是在每个 Servlet 里重复判断?因为 Servlet 覆盖的范围有限,新加一个 Servlet 就少一层防护,Filter 是在请求到达 Servlet 之前做统一处理。Session 是怎么判断登录状态的?登录成功时把 user 对象放进 session,Filter 读取这个标记。静态资源为什么也要放行?如果 CSS 被拦截,登录页会加载不出样式,体验很差。
再加一层保险,可以在 Filter 里追加一个操作日志:从 session 里读取出当前用户名,连同请求 URI 和时间戳写入数据库一张操作日志表。这个功能本质上是 AOP 思路的雏形,代码量只有十几行,但答辩时能讲到"统一横切关注点""前后端分离下的认证思路"这些词,价值密度一下子就上来了。
我的个人习惯是做完这类改动后在本地把配置调两次,一次故意不登录直接访问后台页面,验证被弹回登录页;一次登录后访问页面,再查看日志表里有没有新增记录。这套验证流程看起来笨,但能保证演示时不翻车。真到了答辩那天,老师更关心的是你对系统运行逻辑的理解程度,而不是功能有多花哨。通过自己动手改源码、加过滤、造数据、调参数的过程积累的每个细节,才是最抗追问的东西。希望这篇笔记能帮你用最少的翻车次数,把系统跑起来,也在答辩现场多几分底气。
本文还有配套的精品资源,点击获取