基于Spring Boot与MySQL的小型超市管理系统设计与实现

发布时间:2026/10/17 15:35:38

基于Spring Boot与MySQL的小型超市管理系统设计与实现 简介本资源为基于SpringBoot的小型超市管理系统毕业设计全套资料面向计算机专业学生及Java初学者解决零售场景下数字化管理断层、供应链信息孤岛与动态决策支持不足等问题。系统采用B/S架构集成商品分类管理、采购流程优化与销售数据分析等核心模块前后端通过HTML、CSS、JavaScript与后端交互提升易用性与响应速度。压缩包共402个文件约13.18MB以158个Java源码、58个Vue组件、30个JavaScript脚本、25个XML配置及45张JPG、36张PNG界面截图为主另含SQL建库脚本、CSS样式、JSON数据与项目说明文档结构完整便于二次开发。已有147人学习下载。读者可获得完整可运行的代码工程、数据库脚本与LW文档通过销售统计与经营报表模块理解决策支持逻辑适合作为课程设计或毕业设计参考快速掌握SpringBoot与MySQL整合开发流程。1. 一个超市老板的账本为什么值得用 Spring Boot 重写很多小型超市的日常运转靠的是一本手写台账加一台老收银机。老板白天盯货、晚上对账最怕两件事一是库存对不上二是月底盘不清。这个标题要解决的就是把「进货、销售、库存、会员、员工」这几本账从纸面搬到一套能跑起来的 Web 系统里。技术选型上Spring Boot 负责把后端接口和业务逻辑快速搭起来MySQL 负责把商品、订单、库存这些结构化数据存住前端用最朴素的页面把操作暴露给收银员和管理员。适合谁看适合正在做课程设计、毕业设计或者想给身边小超市做一套轻量管理工具的开发者。它不追求高并发追求的是「一个人能维护、一台普通电脑能跑、数据不丢」。2. 需求拆解与技术选型为什么是 Spring Boot MySQL2.1 小型超市的业务边界到底在哪先把「小型」两个字钉死。小型超市通常指单店经营、SKU 在几百到两三千之间、收银台一到两个、没有连锁调拨需求。它的核心业务只有四条线商品管理、库存管理、销售收银、基础报表。会员和员工管理属于加分项不是第一版必须做的。我一般会先把业务画成三张表的关系商品表是主数据库存表跟着商品走销售订单表在收银时产生并反向扣减库存。会员表独立存在订单表里存一个会员 ID 做关联即可。这样拆的好处是后面加促销、加退货都只是在订单表上做扩展不会把商品表搞脏。提示不要一上来就设计「供应商、采购、退货、促销、积分」全套。第一版把商品、库存、订单跑通比堆二十张空表有价值得多。2.2 为什么选 Spring Boot 而不是别的后端框架Spring Boot 在这个场景里的优势很具体起步依赖把 Web、JDBC、模板引擎一次性拉齐不用自己配 web.xml内嵌 Tomcat 让打包出来的 jar 直接java -jar就能跑省掉单独装容器的步骤application.properties里改几行就能切换数据库连接。对比一下常见选择纯 Servlet 写起来太碎每个请求都要自己处理参数和转发SSM 配置量大对只想做一个小系统的人来说心智负担重Node 或 Python 方案不是不行但很多课程设计和答辩环境默认认 Java 技术栈Spring Boot 的兼容性最好。数据库选 MySQL 的理由更直接免费、资料多、mysqldump备份一条命令搞定、图形化工具遍地都是。小型超市的数据量MySQL 单机绰绰有余。2.3 项目分层与目录结构我习惯用 controller / service / mapper / entity 四层配合 resources 下的 mapper XML 和静态页面。目录大致长这样supermarket/ ├── src/main/java/com/example/supermarket/ │ ├── controller/ # 接收请求做参数校验 │ ├── service/ # 业务逻辑事务在这里 │ ├── mapper/ # 数据库访问接口 │ └── entity/ # 与表对应的实体类 ├── src/main/resources/ │ ├── mapper/ # MyBatis 的 XML 映射文件 │ ├── static/ # 前端页面与静态资源 │ └── application.properties └── pom.xml分层的意义在于收银逻辑扣库存、生成订单必须放在 service 层并加事务不能写在 controller 里。否则一旦扣库存成功、生成订单失败账就乱了。这是小型系统最容易翻车的地方。2.4 依赖清单与版本选择思路pom.xml里我一般只留必要的几个依赖避免版本冲突dependencies !-- Web 能力内嵌 Tomcat Spring MVC -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- 数据库访问MyBatis 比 JPA 更贴近手写 SQL适合报表类查询 -- dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.1/version /dependency !-- MySQL 驱动 -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency !-- 连接池HikariCP 是 Spring Boot 默认无需额外配置 -- /dependencies参数说明MyBatis 的版本要和你用的 Spring Boot 大版本匹配2.x 系列对应 Spring Boot 2.x 和 3.x 都能用但 3.x 项目建议用更新的 starter。MySQL 驱动从 8.0 开始包名是com.mysql.cj.jdbc.Driver连接串里要带时区和字符集参数否则中文商品名会变问号。3. 数据库设计五张核心表怎么落地3.1 商品表与库存表的分工商品表存「是什么」库存表存「有多少」。分开的理由是库存会频繁变动商品信息很少变分开后库存表的更新不会锁住商品表的读。CREATE TABLE product ( id BIGINT PRIMARY KEY AUTO_INCREMENT, barcode VARCHAR(32) NOT NULL UNIQUE COMMENT 条形码收银扫码用, name VARCHAR(64) NOT NULL COMMENT 商品名, category VARCHAR(32) COMMENT 分类, price DECIMAL(10,2) NOT NULL COMMENT 售价, status TINYINT DEFAULT 1 COMMENT 1上架 0下架 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE stock ( product_id BIGINT PRIMARY KEY, quantity INT NOT NULL DEFAULT 0 COMMENT 当前库存, warn_line INT DEFAULT 10 COMMENT 预警线, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;参数说明DECIMAL(10,2)存金额不要用 float否则 0.10.2 会出现精度误差对账时能让你怀疑人生。utf8mb4是为了支持 emoji 和生僻字商品名。库存表用product_id做主键保证一个商品只有一条库存记录。3.2 订单表与订单明细表的一对多订单表存「这一单的总信息」明细表存「这一单买了哪些商品」。这是典型的一对多也是数据库多对多关系的落地方式。CREATE TABLE sale_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL UNIQUE COMMENT 订单号, member_id BIGINT COMMENT 会员ID可空, total_amount DECIMAL(10,2) NOT NULL, pay_type TINYINT COMMENT 1现金 2扫码, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE order_item ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_id BIGINT NOT NULL, product_id BIGINT NOT NULL, quantity INT NOT NULL, price DECIMAL(10,2) NOT NULL COMMENT 成交单价防止商品调价后历史订单失真, INDEX idx_order (order_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;参数说明明细表里冗余存一个price是因为商品售价会变历史订单必须保留成交时的价格。idx_order索引是为了按订单查明细时不用全表扫。订单号order_no建议用「日期随机数」生成不要用自增 ID 直接暴露给用户。3.3 会员表与员工表的最小字段集CREATE TABLE member ( id BIGINT PRIMARY KEY AUTO_INCREMENT, phone VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(32), points INT DEFAULT 0 COMMENT 积分, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE staff ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL COMMENT 存哈希不存明文, role TINYINT DEFAULT 1 COMMENT 1收银员 2管理员 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;参数说明密码字段长度给 64是为了存 SHA-256 或 BCrypt 的结果。千万不要存明文这是最基础的安全底线。员工表用role区分权限收银员只能进收银页管理员才能改商品和看报表。3.4 连接配置与初始化脚本application.properties里几个关键项spring.datasource.urljdbc:mysql://localhost:3306/supermarket?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai spring.datasource.usernameroot spring.datasource.passwordyour_password spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver mybatis.mapper-locationsclasspath:mapper/*.xml mybatis.configuration.map-underscore-to-camel-casetrue参数说明serverTimezone必须设否则连接 MySQL 8 会报时区错误。map-underscore-to-camel-casetrue让数据库的product_id自动映射到 Java 的productId省掉大量手动映射。初始化时把建表 SQL 放在schema.sql里Spring Boot 启动会自动执行方便反复重建测试环境。4. 核心功能实现从收银到库存扣减4.1 商品查询与分页接口商品列表要支持按名称模糊查、按分类筛、分页。MyBatis 的 XML 写法select idlistProducts resultTypecom.example.supermarket.entity.Product SELECT id, barcode, name, category, price, status FROM product where if testkeyword ! null and keyword ! AND name LIKE CONCAT(%, #{keyword}, %) /if if testcategory ! null and category ! AND category #{category} /if /where ORDER BY id DESC LIMIT #{offset}, #{size} /select逻辑说明where标签会自动处理第一个条件前的 AND避免拼接出WHERE AND的语法错误。CONCAT(%, #{keyword}, %)用#{}而不是${}能防止 SQL 注入。offset和size由 service 层根据页码算出来比如第 2 页每页 10 条offset 就是 10。4.2 收银下单与库存扣减的事务处理这是整个系统最不能出错的地方。收银动作包含三步生成订单、写明细、扣库存。三步必须在一个事务里任何一步失败全部回滚。Service public class SaleService { Autowired private SaleOrderMapper orderMapper; Autowired private OrderItemMapper itemMapper; Autowired private StockMapper stockMapper; Transactional(rollbackFor Exception.class) public String checkout(ListCartItem items, Long memberId) { // 1. 先校验库存避免扣减到一半才发现不够 for (CartItem item : items) { Stock stock stockMapper.selectByProductId(item.getProductId()); if (stock null || stock.getQuantity() item.getQuantity()) { throw new RuntimeException(库存不足 item.getProductId()); } } // 2. 生成订单主记录 SaleOrder order new SaleOrder(); order.setOrderNo(generateOrderNo()); order.setMemberId(memberId); order.setTotalAmount(calcTotal(items)); orderMapper.insert(order); // 3. 写明细并扣库存 for (CartItem item : items) { OrderItem oi new OrderItem(); oi.setOrderId(order.getId()); oi.setProductId(item.getProductId()); oi.setQuantity(item.getQuantity()); oi.setPrice(item.getPrice()); itemMapper.insert(oi); stockMapper.decrease(item.getProductId(), item.getQuantity()); } return order.getOrderNo(); } }逻辑说明Transactional(rollbackFor Exception.class)里的rollbackFor很关键默认只回滚运行时异常加上它之后受检异常也会回滚。先校验再扣减是为了在并发不高的小型超市场景下用最简单的方式避免超卖。如果以后并发上来了再改成UPDATE stock SET quantity quantity - #{qty} WHERE product_id #{id} AND quantity #{qty}靠数据库行锁保证原子性。4.3 库存预警与低库存查询库存预警不需要定时任务收银扣减后顺手查一下就行或者管理员进首页时查一次。SELECT p.name, s.quantity, s.warn_line FROM stock s JOIN product p ON p.id s.product_id WHERE s.quantity s.warn_line ORDER BY s.quantity ASC;逻辑说明用 JOIN 把商品名带出来前端直接展示。quantity warn_line是预警条件warn_line默认 10管理员可以按商品单独调。这个查询走的是 stock 表全扫但库存表通常只有几千行性能没问题。4.4 销售报表的按日汇总老板最想看的是「今天卖了多少钱、哪些货走得快」。SELECT DATE(create_time) AS day, COUNT(*) AS order_count, SUM(total_amount) AS total FROM sale_order WHERE create_time #{start} AND create_time #{end} GROUP BY DATE(create_time) ORDER BY day DESC;参数说明start和end用日期字符串传入end取次日零点这样能覆盖当天全部订单。DATE(create_time)会用到函数数据量大时索引失效但小型超市一天几百单完全够用。如果以后要优化可以加一个sale_date冗余字段并建索引。5. 避坑与排查那些让我返工三次的问题5.1 中文乱码从连接串到建表字符集现象商品名存进去变成问号或者页面显示乱码。原因三个地方任意一处字符集不对都会出问题——数据库连接串没带characterEncodingutf8、建表时用了latin1、前端页面没声明 UTF-8。解决连接串加useUnicodetruecharacterEncodingutf8建表统一DEFAULT CHARSETutf8mb4HTML 头部加meta charsetUTF-8。三处对齐后基本不会再犯。5.2 事务不生效方法内部调用踩的坑现象收银时库存扣了但订单没生成数据对不上。原因Transactional标注的方法被同一个类里的另一个方法直接调用Spring 的代理没拦住事务根本没开。解决把事务方法抽到独立的 service 类里或者通过注入自身代理调用。我一般直接把收银逻辑单独放一个SaleServicecontroller 只调它不绕弯。5.3 库存超卖并发下的经典问题现象两个收银台同时卖最后一件商品结果库存变成 -1。原因先查后扣的写法在并发下不安全两个线程都查到库存为 1都执行扣减。解决把扣减改成带条件的原子更新UPDATE stock SET quantity quantity - #{qty} WHERE product_id #{id} AND quantity #{qty}然后判断影响行数为 0 就抛异常回滚。这是最省事的方案不用引入分布式锁。5.4 订单号重复别用时间戳裸奔现象同一秒内两笔订单订单号撞了插入报唯一键冲突。原因直接用System.currentTimeMillis()做订单号毫秒级并发会重复。解决订单号用「yyyyMMddHHmmss 4 位随机数」或者用数据库自增 ID 拼前缀。更稳妥的是单独建一张序列表但小型系统用随机数足够。5.5 连接池耗尽忘了关流和长事务现象系统跑一段时间后报「无法获取数据库连接」。原因手写 JDBC 时忘了关 Connection或者某个事务里做了耗时操作比如发短信导致连接被长时间占用。解决统一用 MyBatis 或 JdbcTemplate它们会管理连接释放。事务方法里只做数据库操作把外部调用挪到事务外。HikariCP 默认最大连接数是 10小型超市够用但别在事务里 sleep。6. 进阶技巧让这套系统多活两年第一版跑通之后真正决定这套系统能不能长期用的是几个细节。我踩过的最大教训是一开始图省事把所有配置写死在代码里结果换台机器部署就要改代码重新打包。后来我把数据库地址、端口、备份路径全挪到application.properties用不同 profile 区分开发和生产部署时只改配置文件不动一行 Java。第二个习惯是给关键操作留日志。收银、改价、删商品这三类动作我都会在 service 里打一条带操作人和时间的日志。不是为了审计是为了出问题时能回溯。有一次库存对不上就是靠日志发现某笔订单被重复提交了两次。第三个技巧是备份。MySQL 的mysqldump配合系统的定时任务每天凌晨导出一份 SQL 文件保留最近七天。命令很简单mysqldump -u root -p supermarket /backup/supermarket_$(date %Y%m%d).sql参数说明-u和-p后面跟账号密码supermarket是库名重定向到带日期的文件。恢复时用mysql -u root -p supermarket 备份文件.sql。这个习惯看起来笨但真出事的时候它是唯一的后悔药。最后一个建议是控制功能膨胀。小型超市系统的价值在于稳定和简单不在于功能多。每次想加新模块之前先问一句老板真的会用吗如果答案模糊就先不加。我见过太多课程设计堆了十几个模块最后连收银主流程都跑不顺。把商品、库存、订单这三条线打磨到不出错比什么都强。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/17 15:35:38

年会大屏抽奖翻车后,我拆解了JS手机号批量滚动抽奖代码

简介:这份资源提供了一套基于JavaScript的手机号批量滚动抽奖实现代码,面向需要为年会、晚会或选秀类活动制作现场抽奖环节的前端开发者与活动策划人员。核心解决随机抽取且不重复、定时器驱动滚动展示、中奖人数可控等实际问题,适合具备基础…

2026/10/17 15:35:38

PyTorch实现神经风格迁移:VGG19与Gram矩阵让照片秒变梵高画作

先问一个我自己也琢磨了很久的问题:当你把一张随手拍的照片丢进某个工具,几秒钟后得到一张星空流转的“梵高版”图片时,你有没有想过,机器到底是怎么把“这张照片里有什么”和“画家的笔触怎么画”这两件事分开的?这背…

2026/10/17 15:30:38

影视聚合源码搭建避坑指南:从解压到播放的完整实践

简介:神马影视8.8源码最新优化版面向视频网站搭建者与后台运维学习者,提供一套经过性能与稳定性改进的在线影视系统源码,适合具备一定PHP与数据库基础、希望研究视频平台后台架构的技术人员参考。压缩包共1个文件,为单个sql数据库…

2026/10/17 16:40:45

MCP工具调用连接优化:从TCP握手到协议复用的全链路实践

1. 项目概述:MCP 工具调用不是“点一下就通”,而是一场精密的协议协同 你有没有遇到过这样的情况:写好一个调用外部工具的函数,本地测试一切正常,一上生产环境就卡在“连接中”——超时、重试、日志里反复刷着 connec…

2026/10/17 16:40:45

棉纺场景烟雾火焰检测实战:YOLOv26训练与部署全解析

1. 棉纺场景下的火情检测,难点和机会在哪里这套系统是某个棉纺厂技改项目里的真实需求,不是实验室里跑个Demo交差那种。厂里的安全主管反复强调过一句话:真正要防的不是已经烧起来的明火,而是棉包内部阴燃、清花机底部飘出的那几缕…

2026/10/17 16:40:45

带标注的大块煤识别数据集:支持YOLO/COCO/VOC,助力选煤厂智能分拣

在选煤厂或者矿井地面筛分站,传送带上高速通过的煤流里往往混着矸石,工人需要从中把大块煤挑出来。这活儿看着简单,但皮带转速快、粉尘大、夜班光线差,人工手选不仅效率上不去,漏检和误检一直是压在成本里的两块石头。…

2026/10/17 16:40:45

专科生AI论文写作全攻略:8款工具实操指南

我知道很多人一看到“专科生写毕业论文”这几个字就开始头疼。选题没方向、文献不会找、框架搭不起来、写出来的东西自己都读不下去,最后还要过查重——这一套流程下来,能把人折腾掉一层皮。所以当“AI论文写作软件”这类工具出来之后,我身边…

2026/10/17 16:40:45

Java集合框架与时空复杂度:从大O到HashMap的底层原理

1. 为什么数据结构的第一课,绕不开集合框架 我接触过不少准备面试的开发者,十个里有七八个会把“集合和时空复杂度”当成一个走流程的入门章节:HashMap查一下复杂度是O(1),ArrayList增删是O(n),背完就觉得自己会了。真…

2026/10/17 16:35:45

GaussDB 100在EulerOS上的安装实战:从环境准备到自检指南

简介:这是华为云 GaussDB 100 数据库基于 EulerOS 20 SP8 64 位架构的安装压缩包,面向需要部署企业级分布式数据库的运维、开发和架构师,适用于高并发事务、OLAP 分析与云原生数据管理场景。包内共 6 个文件,其中 4 个 Python 脚本…

2026/10/16 5:33:43

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/16 4:03:48

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/17 14:20:58

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/17 0:04:32

PlanarSLAM:面向曼哈顿世界的点线面联合SLAM系统

简介:本资源是面向机器人视觉与SLAM研究者的C开源RGB-D SLAM系统实现,专为结构化室内场景(如办公室、走廊)设计,通过融合点、线、平面三类几何特征并引入“曼哈顿世界”假设,显著提升位姿跟踪鲁棒性与稠密地…

2026/10/17 0:04:32

TDH Workflow调度实战:从DAG依赖驱动到任务编排与故障排查

1. 从一个调度脚本说起:TDH Workflow到底在解决什么问题第一次接触TDH的Workflow,是在一个数据清洗项目里。当时团队每天要跑几十个ETL任务,任务之间有严格的先后依赖——A表清洗完才能跑B表的关联,B表跑完才能触发C表的聚合。最开…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑