Java SE轻量收银系统:Swing+JDBC实现桌面端收银闭环
发布时间:2026/9/12 5:01:10 锦皓数字建站

简介这是一份基于Java开发的轻量级超市收银系统实战项目资源面向Java初学者与课程设计学习者解决零售场景下商品管理、用户登录、收银台操作及会员注册等核心业务逻辑实现问题。压缩包共27个文件含9个.java源码文件如DengLu.java登录模块、Shouyintai.java收银台主界面、Goods.java商品类、14个编译后.class字节码文件、2个manifest.mf清单文件、以及.project和.classpath工程配置文件完整呈现从编码、编译到运行的全流程结构包体仅25KB便于快速导入IDE学习调试。已有264人下载学习读者可直接获取可运行的GUI收银系统源码掌握Swing界面开发、JDBC数据库连接预设DBUtil工具类、MVC分层思想及基础事务处理逻辑特别适合JavaSE阶段项目实训与期末课程设计参考。1. 这不是演示项目而是一套能跑通完整收银闭环的 Java 桌面系统你解压chaoshi.rar后看到的不是空壳 Demo也不是只有登录界面的“教学玩具”——它包含从商品录入、会员注册、收银台结算到库存扣减的全链路逻辑且所有类文件.class已编译就绪src/下对应.java源码完整可读。这意味着无需重写业务逻辑只要配好 JDBC 驱动、建好数据库表、启动主类Shouyintai.class就能在 Windows 或 Linux 桌面环境直接扫码结账。它不依赖 Spring Boot、不嵌入 Tomcat、不走 HTTP 接口而是用 AWT/Swing 构建本地 GUI用DBUtil.class封装 JDBC 连接池虽简但有效用Goods.class和Member.class承载核心领域模型。适合两类人一是 Java 初学者想拿真实业务练手比“学生管理系统”更贴近商业场景二是中小超市技术员需快速部署轻量收银终端无云服务依赖单机即可运行。它不解决高并发或分布式事务但把“一笔交易如何从扫码→查价→扣库存→打小票”这个最小闭环用纯 Java SE JDBC Swing 拆解得足够清晰。2. 从反编译源码到数据库建模还原系统数据结构与交互流程2.1 解包后关键类职责解析不是命名堆砌而是分层明确的 MVC 实践chaoshi.rar解压后呈现典型的 Java SE 桌面应用结构bin/存放编译产物src/存放源码.project和.classpath表明原工程基于 Eclipse 开发。我们先聚焦核心类的协作关系Shouyintai.java收银台主窗口类继承Frame含JTable显示购物车、JLabel实时总价、JButton触发结算。其内部匿名类Shouyintai$1.class是ActionListener监听“结算”按钮点击事件。Goods.java与Member.java实体类字段直白映射数据库列如Goods含id,name,price,stockMember含cardNo,name,discount无 Lombok 注解靠手动 getter/setter 维护。DBUtil.java关键基础设施类。静态块加载com.mysql.jdbc.Driver注意这是 MySQL Connector/J 5.x 旧版驱动类名getConnection()方法返回Connectionclose()方法显式释放资源。未使用连接池但通过static变量缓存Connection对象避免每次操作都新建连接——这是单机桌面应用的典型取舍。DengLu.java登录与Shouyinyuanzhuce.java收银员注册验证逻辑在客户端完成JPasswordField.getText().equals(123)类似硬编码密码安全性非重点但流程完整登录 → 进入收银台 → 商品扫码/搜索 → 加入购物车 → 结算 → 更新库存。提示Shangpingtianjia$close.class是Shangpingtianjia.java商品添加中内部类用于监听窗口关闭事件并执行dispose()。这种细粒度事件拆分是 Swing 编程的惯用手法而非冗余代码。2.2 数据库表结构逆向推导从 DAO 层 SQL 拼接还原 DDLDBUtil.java中无建表语句但Goods.java的insert()方法暴露了插入逻辑public int insert(Goods goods) { String sql INSERT INTO goods (name, price, stock, barcode) VALUES (?, ?, ?, ?); // ... PreparedStatement 设置参数 ... }同理Member.java的insert()方法含INSERT INTO member (cardNo, name, phone, discount) VALUES (?, ?, ?, ?)。结合DengLu.java登录查询SELECT * FROM user WHERE username ? AND password ?可确定三张核心表表名字段类型说明goodsid INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(50),price DECIMAL(10,2),stock INT,barcode VARCHAR(20)商品主表barcode为扫码依据memberid INT PRIMARY KEY AUTO_INCREMENT,cardNo VARCHAR(20) UNIQUE,name VARCHAR(30),phone VARCHAR(15),discount DECIMAL(3,2)会员表discount存储折扣率如 0.95 表示 95 折userid INT PRIMARY KEY AUTO_INCREMENT,username VARCHAR(20),password VARCHAR(50)收银员账号表密码明文存储仅作功能验证非生产标准建表 SQL 必须执行以 MySQL 8.0 为例CREATE DATABASE IF NOT EXISTS chaoshi DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci; USE chaoshi; CREATE TABLE goods ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, barcode VARCHAR(20) UNIQUE ); CREATE TABLE member ( id INT PRIMARY KEY AUTO_INCREMENT, cardNo VARCHAR(20) UNIQUE NOT NULL, name VARCHAR(30), phone VARCHAR(15), discount DECIMAL(3,2) DEFAULT 1.00 ); CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(20) NOT NULL, password VARCHAR(50) NOT NULL ); -- 插入默认收银员用户名 admin密码 123 INSERT INTO user (username, password) VALUES (admin, 123);注意DBUtil.java中getConnection()使用DriverManager.getConnection(jdbc:mysql://localhost:3306/chaoshi?useSSLfalseserverTimezoneGMT%2B8, root, 123456)。必须确保 MySQL 服务运行且 root 用户密码为123456否则启动Shouyintai会抛出SQLException: Access denied。若修改密码需同步更新DBUtil.java中的密码字符串并重新编译。2.3 启动流程与类加载路径绕过 IDE 直接运行的关键步骤系统未打包成 JAR需手动指定 classpath 运行。步骤如下准备运行环境安装 JDK 8java -version输出1.8.0_XXX因DBUtil.java使用com.mysql.jdbc.DriverMySQL 5.x 驱动与 JDK 8 兼容性最佳将mysql-connector-java-5.1.47.jar下载地址https://dev.mysql.com/downloads/connector/j/5.1.html放入chaoshi/目录确保chaoshi/bin/下存在所有.class文件含manifest.mf但此文件无Main-Class声明故不能java -jar。命令行启动收银台cd chaoshi java -cp bin:./mysql-connector-java-5.1.47.jar Shouyintai-cp参数将bin/含所有 class和 MySQL 驱动 JAR 合并为类路径Shouyintai是主类名无.class后缀JVM 自动查找bin/Shouyintai.class若报错Could not find or load main class Shouyintai检查当前目录是否为chaoshi/且bin/下确有Shouyintai.class。首次运行验证启动后弹出登录窗口输入admin/123进入收银台点击“商品管理” → “添加商品”填入名称、价格、库存、条码如123456点击保存返回收银台在“商品条码”输入框输入123456回车——商品自动加入购物车总价实时计算点击“结算”弹出确认框选择“现金支付”系统扣减goods表中对应stock并生成交易记录虽无独立 transaction 表但库存更新已执行。3. 商品扫码与库存强一致性保障事务控制与并发安全实操3.1 默认无事务用 try-catch 手动 rollback 弥补 JDBC 原生缺陷Shouyintai.java的结算逻辑位于actionPerformed()方法中关键代码片段如下// 伪代码示意实际在 Shouyintai$1.class 中 try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 关键开启事务 // 步骤1扣减库存UPDATE goods SET stock stock - ? WHERE id ? PreparedStatement ps1 conn.prepareStatement(UPDATE goods SET stock stock - ? WHERE id ?); ps1.setInt(1, quantity); ps1.setInt(2, goodsId); ps1.executeUpdate(); // 步骤2记录销售此处无 sales 表故省略 conn.commit(); // 提交事务 } catch (SQLException e) { try { conn.rollback(); // 回滚 } catch (SQLException ex) { ex.printStackTrace(); } JOptionPane.showMessageDialog(null, 库存不足或数据库错误); }但原始chaoshi.rar中的Shouyintai.java并未显式调用setAutoCommit(false)和commit()。这意味着默认autoCommittrue每条 SQL 独立提交无法保证“扣库存”与“记销售”的原子性。必须手动增强修改Shouyintai.java的结算方法以 Eclipse 为例在// 结算逻辑开始处添加Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 关闭自动提交在// 扣库存 SQL 执行后添加// 假设已有 PreparedStatement psUpdateStock 执行成功 psUpdateStock.executeUpdate(); // 此处可追加其他业务SQL如插入 sales 记录 // PreparedStatement psInsertSale conn.prepareStatement(INSERT INTO sales (...) VALUES (...)); // psInsertSale.executeUpdate(); conn.commit(); // 显式提交 JOptionPane.showMessageDialog(this, 结算成功); } catch (SQLException e) { try { if (conn ! null) conn.rollback(); // 发生异常则回滚 } catch (SQLException ex) { ex.printStackTrace(); } JOptionPane.showMessageDialog(this, 结算失败 e.getMessage()); } finally { try { if (conn ! null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } }提示DBUtil.java的close()方法仅关闭Statement和ResultSet未关闭Connection。因此在Shouyintai中必须显式conn.close()否则连接泄漏。这是桌面应用常见疏漏需补全。3.2 库存超卖防护乐观锁 vs 悲观锁的轻量级选型当多台收银机同时操作同一商品时UPDATE goods SET stock stock - 1 WHERE id 100可能导致超卖如库存剩 1两笔请求同时读到 1均扣减为 0。chaoshi原始代码无防护需加固方案一乐观锁推荐侵入小在goods表新增version INT DEFAULT 0字段查询商品时读取version更新时校验UPDATE goods SET stock stock - 1, version version 1 WHERE id ? AND version ?若executeUpdate()返回 0说明版本不匹配已被他人更新提示“库存变动请刷新重试”。方案二悲观锁简单直接在查询商品时加锁SELECT * FROM goods WHERE id ? FOR UPDATE此 SQL 会阻塞其他事务对同一行的UPDATE直到当前事务结束注意必须在同一个Connection中执行SELECT ... FOR UPDATE和后续UPDATE且事务未提交前锁不释放。实操代码悲观锁String sqlSelect SELECT * FROM goods WHERE id ? FOR UPDATE; PreparedStatement psSelect conn.prepareStatement(sqlSelect); psSelect.setInt(1, goodsId); ResultSet rs psSelect.executeQuery(); if (rs.next()) { int currentStock rs.getInt(stock); if (currentStock quantity) { throw new SQLException(库存不足); } // 继续执行 UPDATE }4. 从 Swing 到现代化 UI替换 AWT 组件与集成打印模块的实战改造4.1 用 IntelliJ IDEA 重构 GUI告别 AWT拥抱现代 Swing 组件chaoshi原始界面使用FramePanelButton等 AWT 组件布局僵硬FlowLayout、字体模糊、无高 DPI 支持。升级为JFrameJPanelJButton并启用系统外观修改主类继承关系Shouyintai.java中public class Shouyintai extends Frame→public class Shouyintai extends JFrame替换组件初始化// 原Frame frame new Frame(收银台); // 改为 JFrame frame new JFrame(超市收银系统); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); // 使用 BorderLayout 替代 FlowLayout启用系统外观Windows/macOS/Linux 自适应在main()方法开头添加try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { e.printStackTrace(); }4.2 集成热敏小票打印用 Java Print Service 输出标准 POS 小票收银系统必须支持物理打印。chaoshi无打印模块需自行添加。以下为兼容主流热敏打印机如芯烨、佳博的方案步骤 1定义小票文本模板public static String generateReceipt(ListCartItem cart, double total) { StringBuilder sb new StringBuilder(); sb.append( 超市收银系统 \n); sb.append(时间).append(new SimpleDateFormat(yyyy-MM-dd HH:mm:ss).format(new Date())).append(\n); sb.append(----------------------------\n); for (CartItem item : cart) { sb.append(String.format(%-10s %4d×%6.2f%6.2f\n, item.getName(), item.getQuantity(), item.getPrice(), item.getTotal())); } sb.append(----------------------------\n); sb.append(String.format(总计¥%8.2f\n, total)); sb.append(感谢惠顾\n); return sb.toString(); }步骤 2调用系统打印机无需驱动依赖 OS 打印服务public static void printReceipt(String receiptText) { try { DocFlavor flavor DocFlavor.BYTE_ARRAY.AUTOSENSE; PrintService service PrintServiceLookup.lookupDefaultPrintService(); if (service null) { JOptionPane.showMessageDialog(null, 未找到默认打印机请检查系统设置); return; } Doc doc new SimpleDoc(receiptText.getBytes(StandardCharsets.UTF_8), flavor, null); service.createPrintJob().print(doc, null); } catch (Exception e) { e.printStackTrace(); JOptionPane.showMessageDialog(null, 打印失败 e.getMessage()); } }调用时机在Shouyintai结算成功后JOptionPane.showMessageDialog(...)之后插入printReceipt(receiptText)。注意Windows 系统需在“设置 蓝牙和其他设备 打印机和扫描仪”中添加热敏打印机并设为默认。Linux 用户需安装 CUPS 服务并配置打印机。5. 生产环境加固密码加密、日志追踪与离线模式下的本地缓存策略5.1 密码明文存储风险与 BCrypt 加密迁移路径user表中password字段明文存储如123严重违反安全规范。迁移方案需兼顾兼容性与平滑升级新增password_hash字段VARCHAR(100)ALTER TABLE user ADD COLUMN password_hash VARCHAR(100) AFTER password;修改登录验证逻辑DengLu.java// 查询时同时获取 password 和 password_hash String sql SELECT id, username, password, password_hash FROM user WHERE username ?; // 若 password_hash 为空老用户用明文比对并生成 hash 写回数据库 // 若 password_hash 不为空用 BCrypt.checkpw(inputPassword, storedHash) 验证引入 BCrypt 依赖下载bcprov-jdk15on-168.jarBouncy Castle或更轻量的jbcrypt-0.4.jarhttps://github.com/jeremyh/jBCrypt添加至 classpath// 生成 hash 示例 String hash BCrypt.hashpw(123, BCrypt.gensalt());5.2 用 Log4j2 实现操作审计日志定位每一笔交易源头chaoshi无日志故障排查困难。添加log4j2.xml配置放在src/下?xml version1.0 encodingUTF-8? Configuration statusWARN Appenders File nameFileAppender fileNamelogs/chaoshi.log PatternLayout pattern%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n/ /File /Appenders Loggers Root levelinfo AppenderRef refFileAppender/ /Root /Loggers /Configuration在Shouyintai.java结算方法中插入日志Logger logger LogManager.getLogger(Shouyintai.class); logger.info(收银员[{}]结算订单{}件商品总金额¥{}支付方式[现金], currentUser.getUsername(), cart.size(), total);效果logs/chaoshi.log自动生成记录每次结算时间、操作员、商品数、金额便于事后审计。5.3 离线模式兜底H2 数据库嵌入式切换与同步机制当网络中断时MySQL 不可用。chaoshi需支持降级到本地 H2 数据库存储临时交易待网络恢复后同步至 MySQL添加 H2 依赖h2-2.1.214.jar修改DBUtil.java的getConnection()public static Connection getConnection() { String url isOnline() ? jdbc:mysql://localhost:3306/chaoshi?... : jdbc:h2:./data/chaoshi_offline;DB_CLOSE_DELAY-1; return DriverManager.getConnection(url, sa, ); }设计同步任务启动时检查h2中是否存在未同步的offline_sales表记录若有则批量 INSERT 到 MySQL 的sales表并清空h2表。提示H2 的jdbc:h2:./data/chaoshi_offline会在当前目录创建data/文件夹存储数据库文件无需额外服务进程真正实现“开箱即用”的离线能力。最后一行不要总结。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。