资讯详情

Java+SQL Server超市管理系统源码解析:从环境搭建到二次开发

📅 2026/10/30 5:27:51 | 华诺云谱 👁 阅读
Java+SQL Server超市管理系统源码解析:从环境搭建到二次开发
简介这份资源面向Java初学者与课程设计开发者提供一套基于Java与SQL Server的超市管理系统完整实现可用于毕业设计、课程实训或后台管理系统的入门参考。系统覆盖商品管理、库存控制、销售记录、会员管理与报表生成等模块技术栈涉及JDBC连接数据库、Servlet与JSP构建MVC结构、Spring管理Bean与事务、MyBatis封装持久层操作数据库侧包含表结构设计、索引优化、存储过程与触发器等实践要点。压缩包为rar格式大小约2.16MB上游未提供文件明细具体文件构成以实际下载为准。目前已有492人学习下载说明该案例在同类教学资源中具有一定参考价值。读者可从中了解Java Web项目与SQL Server配合的典型分层方式对照业务模块理解数据表设计与SQL优化思路并借鉴系统开发从需求分析到部署上线的完整流程适合作为动手实践与查漏补缺的参考素材。1. 从一份能跑起来的超市管理系统源码说起接手一套别人写了一半的超市管理系统最怕的不是业务复杂而是打开压缩包发现只有一堆.java文件数据库脚本缺失、依赖版本对不上、连登录入口在哪都要靠猜。这次拆的这份 Java SQL Server 超市管理系统源码恰好是少数能直接跑通的一套商品管理、库存出入库、收银结算、会员积分、销售统计几个核心模块都在前端是 Swing 桌面端后端用 JDBC 直连 SQL Server没有堆一堆用不上的框架。它适合两类人——课程设计要交差、想找个结构完整又不臃肿的参考实现的新手以及需要一套能改造成小型门店收银工具的老手。下面按「先跑通、再拆结构、最后改业务」的顺序把这份资源从环境搭建到二次开发讲透坑点一个不落。2. 环境搭建与数据库初始化SQL Server 连接串和建库脚本怎么配拿到源码第一步不是急着点运行而是把 SQL Server 这一侧先立住。这套系统对数据库的依赖很重商品、库存、订单、会员四张主表加若干关联表任何一张建错字段类型后面 JDBC 查询就会在ResultSet.getXXX上翻车。我一般按「装库 → 建库 → 导脚本 → 验连接」四步走每步都能单独验证出问题好定位。2.1 SQL Server 版本选择与实例配置源码里的连接串默认走的是localhost:1433说明作者用的是默认实例加 TCP 端口。实际部署时有两个常见选择Express 版够用但默认不开 TCP/IP需要手动在配置管理器里启用Developer 版功能全、免费用于开发装完默认实例名就是机器名。我倾向后者省掉改实例名的麻烦。装完之后必须确认三件事缺一个后面就连不上SQL Server Browser 服务是否启动命名实例才需要默认实例可跳过TCP/IP 协议是否启用端口是否为 1433是否开启了「SQL Server 和 Windows 身份验证模式」纯 Windows 认证模式下 JDBC 用账号密码登录会直接被拒第三点是最容易漏的。很多人装库时一路下一步默认就是仅 Windows 认证结果 Java 端报Login failed for user sa还以为是密码错了。改法是在 SSMS 里右键实例 → 属性 → 安全性 → 改成混合模式然后重启服务再启用 sa 账号并设密码。2.2 建库与脚本导入的实操步骤源码包里的 SQL 脚本一般叫db_supermarket.sql或类似名字里面包含建库、建表、插初始数据三部分。导入时不要用图形界面的「导入数据」向导那个对含GO分隔符的脚本支持很差容易只执行了第一段。正确做法是新建查询窗口把脚本内容贴进去整体执行或者用命令行# 用 sqlcmd 执行建库脚本-S 指定实例-U/-P 指定账号密码 sqlcmd -S localhost -E -i db_supermarket.sql # 如果走 SQL 认证 sqlcmd -S localhost -U sa -P YourPassword -i db_supermarket.sql-E表示用当前 Windows 身份登录本地开发最省事-i后面跟脚本路径。执行完用下面这条确认表都建出来了-- 切到目标库列出所有用户表 USE supermarket; GO SELECT name FROM sys.tables ORDER BY name;正常应该能看到商品表、库存表、订单主表、订单明细表、会员表、用户表这几张。如果只有一两张多半是脚本里的GO被某个客户端吞了换 sqlcmd 重跑一遍即可。2.3 JDBC 连接串的四个关键参数Java 侧连 SQL Server 用的是微软官方驱动mssql-jdbc连接串长这样// 典型连接串各参数含义见下方说明 String url jdbc:sqlserver://localhost:1433; databaseNamesupermarket; encryptfalse; trustServerCertificatetrue; loginTimeout5;; Connection conn DriverManager.getConnection(url, sa, YourPassword);四个参数值得单独说。databaseName必须和建库脚本里的库名完全一致大小写不敏感但拼错就是Cannot open database。encryptfalse是给老版本驱动留的兼容项新版驱动默认要求加密本地没配证书时会报 SSL 握手失败关掉最省事。trustServerCertificatetrue配合上一条使用避免自签证书校验。loginTimeout5是我自己加的默认超时太长连不上时界面会卡死十几秒设成 5 秒能快速暴露配置错误。驱动 jar 包要放进项目的lib目录并加到 classpathMaven 项目则加依赖dependency groupIdcom.microsoft.sqlserver/groupId artifactIdmssql-jdbc/artifactId version12.4.2.jre11/version /dependency版本号按你的 JDK 选jre8和jre11后缀别搞混用错会在启动时报UnsupportedClassVersionError。3. 源码结构拆解从登录入口到收银结算的调用链环境通了之后别急着改业务先把调用链摸清楚。这套系统的分层不算严格但胜在直白ui包放 Swing 窗体dao包放 JDBC 操作model包放实体类util包放数据库连接和工具方法。理解这条链后面加字段、改逻辑才知道动哪里。3.1 包结构与启动类定位打开源码先找带main方法的类通常在com.xxx.supermarket根包下名字类似Main或LoginFrame。启动流程是加载登录窗体 → 校验用户表 → 进主界面 → 按角色加载菜单。用户表里一般有role字段区分管理员和收银员管理员能看到商品管理和统计收银员只能进收银界面。这个权限控制是硬编码在菜单初始化里的不是配置化的改角色要动代码。DAO 层的写法是典型的「一个实体一个类」比如GoodsDao里有queryAll、insert、updateStock这些方法每个方法内部自己拿连接、拼 SQL、关资源。没有连接池每次操作新建Connection小数据量下没问题并发一高就明显卡。这一点后面避坑章节会细说。3.2 商品与库存模块的数据流商品模块的核心是「商品表 库存表」两张表联动。新增商品时先插商品表拿到自增主键再往库存表插一条初始库存记录两步在同一个事务里。源码里这段事务处理写得比较糙用的是手动setAutoCommit(false)加 try-catch 回滚没有用 try-with-resources异常路径下连接可能没关干净。我一般会重写成这样// 新增商品并初始化库存放在同一事务里保证一致性 public boolean addGoods(Goods g, int initStock) { String sql1 INSERT INTO goods(name, price, category) VALUES(?,?,?); String sql2 INSERT INTO stock(goods_id, quantity) VALUES(?,?); try (Connection conn DBUtil.getConnection()) { conn.setAutoCommit(false); // 关闭自动提交开启事务 try (PreparedStatement ps1 conn.prepareStatement(sql1, Statement.RETURN_GENERATED_KEYS)) { ps1.setString(1, g.getName()); ps1.setBigDecimal(2, g.getPrice()); ps1.setString(3, g.getCategory()); ps1.executeUpdate(); ResultSet keys ps1.getGeneratedKeys(); // 取回自增主键 keys.next(); int goodsId keys.getInt(1); try (PreparedStatement ps2 conn.prepareStatement(sql2)) { ps2.setInt(1, goodsId); ps2.setInt(2, initStock); ps2.executeUpdate(); } conn.commit(); // 两步都成功才提交 return true; } catch (SQLException e) { conn.rollback(); // 任一步失败整体回滚 throw e; } } catch (SQLException e) { e.printStackTrace(); return false; } }RETURN_GENERATED_KEYS是关键参数没有它拿不到刚插入商品的自增 ID库存表就没法关联。setBigDecimal对应价格字段别用setDouble金额用浮点会出精度问题这是收银系统的老坑。3.3 收银结算的事务边界收银是整个系统里唯一必须严格保证原子性的操作扣库存、写订单主表、写订单明细、加会员积分四步要么全成要么全败。源码里这段逻辑分散在CashierFrame的事件处理方法里SQL 直接写在界面类中事务边界不清晰。改造时我建议把这段抽到OrderService里用显式事务包住// 结算核心扣库存 写订单 加积分一个事务内完成 public boolean checkout(ListCartItem items, int memberId, BigDecimal total) { String deduct UPDATE stock SET quantity quantity - ? WHERE goods_id ? AND quantity ?; String order INSERT INTO orders(member_id, total, create_time) VALUES(?,?,GETDATE()); String detail INSERT INTO order_detail(order_id, goods_id, qty, price) VALUES(?,?,?,?); String point UPDATE member SET points points ? WHERE id ?; try (Connection conn DBUtil.getConnection()) { conn.setAutoCommit(false); try { int orderId; try (PreparedStatement ps conn.prepareStatement(order, Statement.RETURN_GENERATED_KEYS)) { ps.setInt(1, memberId); ps.setBigDecimal(2, total); ps.executeUpdate(); ResultSet rs ps.getGeneratedKeys(); rs.next(); orderId rs.getInt(1); } for (CartItem it : items) { // 扣库存时带 quantity ? 条件防止超卖 try (PreparedStatement ps conn.prepareStatement(deduct)) { ps.setInt(1, it.getQty()); ps.setInt(2, it.getGoodsId()); ps.setInt(3, it.getQty()); if (ps.executeUpdate() 0) { // 影响行数为 0 说明库存不足 conn.rollback(); return false; } } try (PreparedStatement ps conn.prepareStatement(detail)) { ps.setInt(1, orderId); ps.setInt(2, it.getGoodsId()); ps.setInt(3, it.getQty()); ps.setBigDecimal(4, it.getPrice()); ps.executeUpdate(); } } try (PreparedStatement ps conn.prepareStatement(point)) { ps.setInt(1, total.intValue()); // 简化1 元 1 分 ps.setInt(2, memberId); ps.executeUpdate(); } conn.commit(); return true; } catch (SQLException e) { conn.rollback(); throw e; } } catch (SQLException e) { e.printStackTrace(); return false; } }UPDATE ... WHERE quantity ?这个写法是防超卖的核心靠数据库行锁保证并发下不会扣成负数比先查再扣的两步操作可靠得多。GETDATE()是 SQL Server 的内置函数取服务器当前时间比 Java 端传时间更不容易受客户端时钟影响。4. 避坑与排查五个让系统跑不起来的真实问题这套源码能跑但「能跑」和「跑得稳」之间隔着不少坑。下面五条是我实际部署和改造时踩过的按现象、原因、解决三段写照着排查能省不少时间。4.1 中文乱码商品名存进去变成问号现象新增商品时中文名称在界面显示正常存进数据库再查出来变成???。原因有两层一是 SQL Server 的varchar字段不认 Unicode中文必须用nvarchar二是 JDBC 连接串没指定字符集驱动按平台默认编码处理。解决建表脚本里所有存文本的字段改成nvarchar连接串加sendStringParametersAsUnicodetrue新版驱动默认就是 true老驱动要显式加。改完记得把已有数据清掉重插乱码数据改字段类型也救不回来。4.2 驱动版本与 JDK 不匹配导致启动即崩现象双击启动脚本控制台一闪而过日志里是UnsupportedClassVersionError。原因mssql-jdbc的 jar 包后缀标了jre8和jre11用 JDK 8 跑jre11的包就会报这个错。解决先java -version确认 JDK 版本再选对应后缀的驱动。顺带一提JDK 8 下用jre8后缀的 12.x 版本驱动没问题但别用太老的 6.x那个版本对 TLS 支持有缺陷连新版 SQL Server 会握手失败。4.3 连接未关闭导致连接数耗尽现象系统用一段时间后所有操作都卡住重启才好。原因DAO 层大量Connection、PreparedStatement、ResultSet没有在 finally 里关闭异常路径下更是直接泄漏。SQL Server Express 默认连接数有限泄漏几十个就满了。解决全部改成 try-with-resources 写法资源在括号里声明方法结束自动关。改造时重点查catch块里有没有漏关那是泄漏高发区。4.4 金额用 double 导致对账差几分钱现象收银小票合计和数据库订单总额对不上差 0.01。原因价格字段在 Java 端用了double累加时浮点误差累积。解决实体类价格字段改BigDecimal数据库字段用decimal(10,2)所有加减用BigDecimal.add、multiply别用和*。BigDecimal构造要用字符串或valueOf别用new BigDecimal(0.1)那个一样有误差。4.5 自增主键取回失败导致库存关联为空现象新增商品成功但库存表里goods_id是 0 或 null。原因prepareStatement时没传Statement.RETURN_GENERATED_KEYSgetGeneratedKeys()返回空结果集代码里rs.next()没判断就直接getInt拿到默认值 0。解决按 3.2 节的写法补上参数并在rs.next()前加判断取不到主键就抛异常回滚别让脏数据落库。5. 二次开发与验证把系统改成能用的门店工具跑通只是起点真正有价值的是按自己场景改。这套系统的扩展点集中在三处商品分类、会员规则、统计报表。改之前先想清楚验证方法不然改完不知道对不对。5.1 加一个商品分类维度的销售统计原系统的销售统计只按时间汇总加分类维度需要动 SQL 和界面两处。SQL 侧用JOIN把订单明细、商品、分类串起来-- 按分类统计销售额日期范围用参数占位 SELECT c.name AS category, SUM(d.qty * d.price) AS amount, COUNT(DISTINCT o.id) AS order_count FROM order_detail d JOIN orders o ON o.id d.order_id JOIN goods g ON g.id d.goods_id JOIN category c ON c.id g.category_id WHERE o.create_time BETWEEN ? AND ? GROUP BY c.name ORDER BY amount DESC;SUM(d.qty * d.price)用的是明细里的成交价而不是商品当前价这点很重要商品调价后历史订单金额不能跟着变。界面侧在统计窗体加一个JTable把ResultSet逐行填进去即可注意列名和 SQL 别名对齐。5.2 用一组固定用例验证改造没改坏每次改完业务我都会跑一遍固定用例比肉眼点界面靠谱。核心用例就四条正常结算后库存减对、库存不足时结算被拒且订单不落库、会员积分按规则累加、统计金额和订单总额一致。前三条用代码直接调 Service 层验证第四条用 SQL 对账-- 对账订单主表总额应等于明细汇总 SELECT o.id, o.total, SUM(d.qty * d.price) AS calc FROM orders o JOIN order_detail d ON d.order_id o.id GROUP BY o.id, o.total HAVING o.total SUM(d.qty * d.price);这条查出来有行就说明有对不上的订单通常是事务没包全或者金额精度问题。我一般把它做成一个定时跑的校验出问题早发现。5.3 一个我坚持了很多年的习惯这套系统我前后改过三版最深的教训来自第一版当时图快直接在界面类里写 SQL事务边界全靠记忆结果一次并发测试把库存扣成了负数查了两天才定位到是两笔结算交叉执行。从那以后我每次动收银相关的代码都强制先把事务边界画出来确认四步操作在同一个Connection的同一个事务里再动手写。改完必跑 5.2 节那组用例一条不过就不提交。这套笨办法救过我很多次也希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑