news 2026/10/10 3:05:53

期末数据库课设:JDBC+JavaSwing超市管理系统实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
期末数据库课设:JDBC+JavaSwing超市管理系统实战指南

简介:这份资源是面向高校计算机相关专业学生的期末数据库课程设计参考方案,基于Java、MySQL、JDBC与JavaSwing实现一套完整的超市管理系统,适合正在准备课程设计或想练习桌面端数据库应用开发的学习者。压缩包共24个文件,约1.1MB,其中17个java源文件承载商品、库存、销售等核心业务逻辑,1个sql脚本用于建库建表,2个jar包提供FlatLaf界面美化支持,另有pom.xml与mvn-settings.xml等Maven配置及README说明,整体结构清晰、便于导入IDE运行调试。项目采用JDK12与MySQL8.0环境,编码为GBK,并在各组件构造函数中通过UIManager设置系统外观,使界面风格更统一美观。目前已有217人学习下载,读者可借此掌握JDBC连接数据库、Swing事件处理与增删改查的完整实现思路,并参考其分层结构与SQL设计完成自己的课程设计。

1. 期末数据库课设选超市管理系统:为什么JDBC+JavaSwing这套组合最不容易翻车

每年一到期末,数据库课程设计就成了不少人的心病。选题太简单怕被导师打回,选题太复杂又怕自己写不完。在众多题目里,超市管理系统算是出现频率最高的一个,而基于Java+MySQL+JDBC+JavaSwing的实现路线,几乎是零基础也能跑通、导师也挑不出大毛病的稳妥选择。原因很直接:这套技术栈把数据库操作、界面展示、业务逻辑三层分得清清楚楚,JDBC负责和MySQL对话,JavaSwing负责把结果画到窗口上,中间的业务代码只做数据搬运和校验,每一层都能单独调试。

这篇文章面向的是正在做数据库课设、需要交出一份能演示、能答辩、代码能讲清楚的同学。我会把超市管理系统的表结构设计、JDBC连接封装、Swing界面搭建、事务处理、以及答辩时最容易被追问的几个点,按实际动手顺序拆开讲。你不需要有很深的Java基础,但至少要能看懂类、方法、循环和条件判断。读完之后,你应该能独立搭出一个带登录、商品管理、库存出入库、销售收银和简单报表的系统,并且知道每一行关键代码为什么这么写。

2. 先把数据库表结构定下来:五张表撑起整个超市业务

2.1 从业务动作反推表结构,而不是先画ER图

很多人一上来就打开画图工具画ER图,画了半天发现字段对不上业务。我的习惯是先把系统里会发生哪些动作列出来:管理员登录、商品上架、商品分类、库存入库、库存出库、收银结算、查看销售记录。每个动作对应一到两张表,列完动作,表自然就出来了。

超市管理系统的核心表不需要多,五张足够:用户表、商品分类表、商品表、库存记录表、销售订单表。用户表存登录信息,商品分类表给商品归类,商品表存商品基本信息和当前库存,库存记录表记录每一次入库和出库的流水,销售订单表记录每一笔收银。这样设计的好处是,商品表里的库存字段是实时值,库存记录表是历史流水,两者对账时能互相验证。

2.2 建表SQL与字段类型选择

下面这段SQL可以直接在MySQL里执行。注意字符集用utf8mb4,不然商品名称里出现特殊符号会乱码。主键统一用int自增,外键约束先不加,课设阶段用代码保证一致性比用数据库约束更灵活,也更容易在答辩时解释清楚。

-- 创建数据库,字符集必须指定,否则中文商品名会乱码 CREATE DATABASE supermarket_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE supermarket_db; -- 用户表:区分管理员和普通收银员 CREATE TABLE t_user ( user_id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, role VARCHAR(20) DEFAULT 'cashier', -- admin 或 cashier create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 商品分类表 CREATE TABLE t_category ( category_id INT PRIMARY KEY AUTO_INCREMENT, category_name VARCHAR(50) NOT NULL ); -- 商品表:stock 字段是当前库存实时值 CREATE TABLE t_product ( product_id INT PRIMARY KEY AUTO_INCREMENT, product_name VARCHAR(100) NOT NULL, category_id INT, price DECIMAL(10,2) NOT NULL, stock INT DEFAULT 0, unit VARCHAR(10) DEFAULT '件' ); -- 库存流水表:每次入库/出库都插一条 CREATE TABLE t_stock_record ( record_id INT PRIMARY KEY AUTO_INCREMENT, product_id INT NOT NULL, change_type VARCHAR(10) NOT NULL, -- in 或 out change_qty INT NOT NULL, record_time DATETIME DEFAULT CURRENT_TIMESTAMP, operator VARCHAR(50) ); -- 销售订单表 CREATE TABLE t_order ( order_id INT PRIMARY KEY AUTO_INCREMENT, product_id INT NOT NULL, quantity INT NOT NULL, total_price DECIMAL(10,2) NOT NULL, order_time DATETIME DEFAULT CURRENT_TIMESTAMP, cashier VARCHAR(50) );

字段类型上有几个容易踩坑的地方。价格用DECIMAL而不是FLOAT,因为浮点数算钱会出现0.30000000000000004这种结果,答辩时被问到很难解释。库存用INT,不要用VARCHAR,否则后面做库存扣减时还要转类型。时间字段统一用DATETIME,默认值CURRENT_TIMESTAMP,插入时不用手动传时间。

2.3 初始化数据与自增主键的注意事项

建完表之后先插几条测试数据,方便后面调界面。注意自增主键在删除数据后不会重置,如果你反复删插测试,ID会越来越大,这是正常现象,不用去TRUNCATE,课设演示不影响。

INSERT INTO t_user (username, password, role) VALUES ('admin', '123456', 'admin'); INSERT INTO t_user (username, password, role) VALUES ('cashier01', '123456', 'cashier'); INSERT INTO t_category (category_name) VALUES ('饮料'), ('零食'), ('日用品'); INSERT INTO t_product (product_name, category_id, price, stock, unit) VALUES ('矿泉水', 1, 2.00, 100, '瓶'), ('薯片', 2, 6.50, 50, '袋'), ('洗发水', 3, 35.00, 30, '瓶');

提示:课设数据库名和表名尽量用英文,字段名也用英文,答辩时导师看到中文表名会追问编码问题,徒增麻烦。

3. JDBC连接封装:一个工具类解决所有数据库操作

3.1 为什么必须封装JDBC工具类

如果每个Swing按钮的监听器里都写一遍Class.forName、DriverManager.getConnection、PreparedStatement、ResultSet,代码会重复到无法维护,而且连接忘记关闭会导致MySQL连接数爆掉,演示到一半程序卡死。我一般会写一个DBUtil类,把获取连接、关闭资源、执行增删改、执行查询这四个动作封装成静态方法。这样业务代码里只需要调DBUtil.executeUpdate(sql, params)和DBUtil.executeQuery(sql, params),既干净又不容易漏关连接。

3.2 DBUtil完整代码与参数说明

import java.sql.*; import java.util.ArrayList; import java.util.List; public class DBUtil { // 数据库连接参数,课设阶段直接写死,方便演示 private static final String URL = "jdbc:mysql://localhost:3306/supermarket_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"; private static final String USER = "root"; private static final String PASSWORD = "你的密码"; // 加载驱动,静态代码块只执行一次 static { try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } // 获取连接 public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } // 通用增删改,返回受影响行数 public static int executeUpdate(String sql, Object... params) { try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { for (int i = 0; i < params.length; i++) { ps.setObject(i + 1, params[i]); } return ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); return 0; } } // 通用查询,返回List<Object[]>,每行一个数组 public static List<Object[]> executeQuery(String sql, Object... params) { List<Object[]> list = new ArrayList<>(); try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { for (int i = 0; i < params.length; i++) { ps.setObject(i + 1, params[i]); } try (ResultSet rs = ps.executeQuery()) { ResultSetMetaData meta = rs.getMetaData(); int colCount = meta.getColumnCount(); while (rs.next()) { Object[] row = new Object[colCount]; for (int i = 0; i < colCount; i++) { row[i] = rs.getObject(i + 1); } list.add(row); } } } catch (SQLException e) { e.printStackTrace(); } return list; } }

URL里的serverTimezone=Asia/Shanghai必须加,否则MySQL 8.x会报时区错误。useSSL=false在本地演示时加上,避免SSL握手警告。characterEncoding=utf8保证中文不乱码。executeUpdate和executeQuery都用了try-with-resources,连接和语句会自动关闭,这是Java 7之后的标准写法,比手动finally里close更安全。

3.3 事务处理:收银扣库存必须一起成功

收银这个动作涉及两张表:往t_order插一条销售记录,同时更新t_product的stock字段。如果插订单成功但扣库存失败,库存就对不上。这时候必须用事务。DBUtil里再加一个方法,专门处理需要事务的操作。

public static boolean executeTransaction(String sql1, Object[] params1, String sql2, Object[] params2) { Connection conn = null; try { conn = getConnection(); conn.setAutoCommit(false); // 关闭自动提交,开启事务 try (PreparedStatement ps1 = conn.prepareStatement(sql1); PreparedStatement ps2 = conn.prepareStatement(sql2)) { for (int i = 0; i < params1.length; i++) ps1.setObject(i + 1, params1[i]); for (int i = 0; i < params2.length; i++) ps2.setObject(i + 1, params2[i]); ps1.executeUpdate(); ps2.executeUpdate(); conn.commit(); // 两条都成功才提交 return true; } } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } e.printStackTrace(); return false; } finally { if (conn != null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }

调用时传入两条SQL和对应参数,比如第一条是INSERT INTO t_order...,第二条是UPDATE t_product SET stock = stock - ? WHERE product_id = ?。任何一条抛异常,rollback会把两条都撤销,保证数据一致。答辩时如果导师问“你怎么保证收银不会扣错库存”,把这段代码指给他看就行。

4. JavaSwing界面搭建:从登录窗口到收银主界面

4.1 登录窗口:最小可运行Swing程序

Swing的布局管理器是新手最大的拦路虎。我建议登录窗口用绝对布局(setLayout(null)),因为登录界面元素少,绝对定位最直观,不用去理解BorderLayout和GridBagLayout的约束规则。主界面再用BorderLayout加JPanel组合,这样难度是递进的。

import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.util.List; public class LoginFrame extends JFrame { private JTextField userField; private JPasswordField passField; public LoginFrame() { setTitle("超市管理系统 - 登录"); setSize(350, 220); setLocationRelativeTo(null); // 居中显示 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(null); JLabel userLabel = new JLabel("用户名:"); userLabel.setBounds(50, 40, 80, 25); add(userLabel); userField = new JTextField(); userField.setBounds(120, 40, 150, 25); add(userField); JLabel passLabel = new JLabel("密 码:"); passLabel.setBounds(50, 80, 80, 25); add(passLabel); passField = new JPasswordField(); passField.setBounds(120, 80, 150, 25); add(passField); JButton loginBtn = new JButton("登录"); loginBtn.setBounds(80, 130, 80, 30); add(loginBtn); JButton cancelBtn = new JButton("取消"); cancelBtn.setBounds(190, 130, 80, 30); add(cancelBtn); loginBtn.addActionListener(this::doLogin); cancelBtn.addActionListener(e -> System.exit(0)); } private void doLogin(ActionEvent e) { String username = userField.getText().trim(); String password = new String(passField.getPassword()).trim(); if (username.isEmpty() || password.isEmpty()) { JOptionPane.showMessageDialog(this, "用户名和密码不能为空"); return; } String sql = "SELECT user_id, role FROM t_user WHERE username = ? AND password = ?"; List<Object[]> result = DBUtil.executeQuery(sql, username, password); if (result.isEmpty()) { JOptionPane.showMessageDialog(this, "用户名或密码错误"); } else { String role = result.get(0)[1].toString(); new MainFrame(username, role).setVisible(true); dispose(); // 关闭登录窗口 } } public static void main(String[] args) { SwingUtilities.invokeLater(() -> new LoginFrame().setVisible(true)); } }

setLocationRelativeTo(null)让窗口在屏幕居中,不加的话窗口会跑到左上角。SwingUtilities.invokeLater是Swing线程规范,所有界面创建都放在事件调度线程里,避免偶发的界面卡死。登录查询用PreparedStatement参数化,不要用字符串拼接,否则输入单引号会SQL报错,这也是答辩常问的安全点。

4.2 主界面布局:菜单栏加选项卡

主界面用JFrame加JMenuBar做菜单,中间用JTabbedPane分页,每个选项卡是一个JPanel。商品管理、库存管理、收银、销售记录各占一个选项卡。这样代码结构清晰,每个面板单独写一个类,互不干扰。

import javax.swing.*; import java.awt.*; public class MainFrame extends JFrame { private String username; private String role; public MainFrame(String username, String role) { this.username = username; this.role = role; setTitle("超市管理系统 - 当前用户:" + username); setSize(900, 600); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JMenuBar menuBar = new JMenuBar(); JMenu sysMenu = new JMenu("系统"); JMenuItem logoutItem = new JMenuItem("注销"); logoutItem.addActionListener(e -> { new LoginFrame().setVisible(true); dispose(); }); sysMenu.add(logoutItem); menuBar.add(sysMenu); setJMenuBar(menuBar); JTabbedPane tabbedPane = new JTabbedPane(); tabbedPane.addTab("商品管理", new ProductPanel()); tabbedPane.addTab("库存管理", new StockPanel(username)); tabbedPane.addTab("收银", new CashierPanel(username)); tabbedPane.addTab("销售记录", new OrderPanel()); add(tabbedPane, BorderLayout.CENTER); } }

每个Panel类里用JTable展示数据,JTable必须包在JScrollPane里,否则表头不显示。表格数据用DefaultTableModel,查询到List<Object[]>之后循环addRow。按钮监听器里调DBUtil执行SQL,然后重新查询刷新表格。这套模式在四个面板里重复用,写熟一个后面就是复制改字段。

4.3 收银面板:事务与界面刷新的配合

收银面板是最能体现业务逻辑的地方。用户选择商品、输入数量、点结算,程序先查库存够不够,够的话执行事务插订单扣库存,然后弹窗提示成功并刷新商品表格。库存不够就提示“库存不足”,不执行任何数据库操作。

private void doCheckout() { int selectedRow = productTable.getSelectedRow(); if (selectedRow == -1) { JOptionPane.showMessageDialog(this, "请先选择商品"); return; } int productId = Integer.parseInt(productTable.getValueAt(selectedRow, 0).toString()); int stock = Integer.parseInt(productTable.getValueAt(selectedRow, 4).toString()); int qty; try { qty = Integer.parseInt(qtyField.getText().trim()); } catch (NumberFormatException ex) { JOptionPane.showMessageDialog(this, "数量必须是数字"); return; } if (qty <= 0 || qty > stock) { JOptionPane.showMessageDialog(this, "数量不合法或库存不足"); return; } double price = Double.parseDouble(productTable.getValueAt(selectedRow, 3).toString()); double total = price * qty; String sql1 = "INSERT INTO t_order (product_id, quantity, total_price, cashier) VALUES (?, ?, ?, ?)"; String sql2 = "UPDATE t_product SET stock = stock - ? WHERE product_id = ?"; boolean ok = DBUtil.executeTransaction(sql1, new Object[]{productId, qty, total, username}, sql2, new Object[]{qty, productId}); if (ok) { JOptionPane.showMessageDialog(this, "收银成功,金额:" + total); refreshTable(); // 重新查询商品表,库存显示会更新 } else { JOptionPane.showMessageDialog(this, "收银失败,请检查数据库"); } }

refreshTable方法里重新执行SELECT * FROM t_product,然后tableModel.setRowCount(0)清空再addRow。注意setRowCount(0)必须在addRow之前,否则数据会重复叠加。这个细节很多人第一次写会漏,导致表格越刷新行越多。

5. 避坑与排查:课设演示前必须过的五道关

5.1 中文乱码:从数据库到界面全链路排查

现象是商品名称在JTable里显示成问号或方块。原因可能在三处:数据库字符集不是utf8mb4、JDBC URL没加characterEncoding=utf8、Swing组件字体不支持中文。解决顺序是先确认数据库和表都是utf8mb4,再检查URL参数,最后在main方法开头设置UIManager.setLookAndFeel和默认字体。我一般会在程序启动时加一句UIManager.put("Label.font", new Font("微软雅黑", Font.PLAIN, 14)),把全局字体定下来。

5.2 连接失败:MySQL 8.x的驱动和时区问题

现象是启动就报Communications link failure或者Unknown database。先确认MySQL服务在运行,再确认数据库名拼写。MySQL 8.x必须用com.mysql.cj.jdbc.Driver,用旧的com.mysql.jdbc.Driver会警告。时区问题在URL里加serverTimezone=Asia/Shanghai就能解决。如果报Access denied,检查DBUtil里的用户名密码和MySQL实际账号是否一致,课设阶段用root最省事。

5.3 表格不刷新:查询结果和界面不同步

现象是数据库里数据变了,但JTable还显示旧数据。原因是Swing的表格模型没有重新加载。解决方法是每次增删改之后,重新执行查询,然后tableModel.setRowCount(0)再循环addRow。不要试图直接修改表格里的单元格来同步数据库,那样数据库和界面会脱节,答辩时说不清楚。

5.4 事务不生效:自动提交没关

现象是收银时订单插入了但库存没扣,或者反过来。原因是Connection默认autoCommit为true,每条SQL独立提交。必须在事务方法里先conn.setAutoCommit(false),执行完两条SQL再commit,异常时rollback。另外注意,事务方法里不能用DBUtil.executeUpdate,因为那个方法每次都会新建连接,事务必须用同一个Connection。

5.5 打包运行报错:缺少MySQL驱动jar

现象是在IDE里跑得好好的,导出成jar双击就报ClassNotFoundException。原因是MySQL驱动jar没有一起打包。解决方法是导出时选择Runnable JAR,把mysql-connector-java.jar一起打进去,或者用IDEA的Artifacts配置把驱动加到lib目录。课设答辩如果要求现场跑,提前在答辩机器上装好MySQL并导入SQL文件,比现场打包更稳。

6. 答辩前把这三个进阶点讲清楚,导师基本不会为难你

第一个进阶点是连接池。课设阶段用DriverManager每次新建连接没问题,但你可以主动提一句:实际项目里会用连接池(比如Druid或HikariCP)复用连接,避免频繁创建销毁的开销。不需要真去集成,但要知道这个概念,导师问“你的连接管理有什么优化空间”时能答上来。

第二个进阶点是分页查询。销售记录多了之后,SELECT * FROM t_order全查出来会卡。MySQL用LIMIT offset, size做分页,Swing里配合上一页下一页按钮。你可以只实现一个简单的LIMIT 0, 20,然后说明完整分页需要传页码参数。这个点能体现你有考虑数据量增长的情况。

第三个进阶点是密码存储。课设里密码明文存t_user表,演示没问题,但你可以提一句实际应该存MD5或BCrypt哈希值,登录时把输入的密码哈希后再比对。不需要真改代码,但答辩时主动说出来,导师会觉得你考虑过安全。

最后一个我自己的习惯:答辩前把整个系统从头到尾操作三遍,每遍记录哪里卡顿、哪里报错、哪里数据不对。第一遍查功能,第二遍查数据一致性,第三遍查异常输入(比如数量输负数、商品名输空)。三遍下来,导师现场让你演示任何功能你都能顺手点出来。数据库课设不难,难的是细节都想到。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 3:05:51

Linux syslog 编程实战:从日志丢失排查到接口正确使用

简介&#xff1a;这份资源面向Linux/Unix系统编程学习者与运维开发人员&#xff0c;聚焦syslog日志机制的C语言实现&#xff0c;帮助读者理解系统日志从产生、分级到发送至syslogd的完整链路。压缩包共2个文件&#xff0c;包含1个c源文件与1个h头文件&#xff0c;整体约5KB&…

作者头像 李华
网站建设 2026/10/10 3:04:33

ASP.NET WebForms学生成绩管理系统实战部署与源码解析

简介&#xff1a;本资源是一套完整的学生成绩管理系统毕业设计实践材料&#xff0c;面向计算机专业本科生、软件开发初学者及教育信息化项目开发者&#xff0c;解决高校或中小学教务场景中成绩录入、查询、统计分析与报表生成等核心管理需求。压缩包共544个文件&#xff0c;涵盖…

作者头像 李华
网站建设 2026/10/10 3:04:11

火车管理系统课程设计:从图建模到堆排序的完整实践

简介&#xff1a;面向数据结构课程设计的学生&#xff0c;这份火车管理系统资源提供了从源码到设计文档的完整实践方案&#xff0c;重点解决车次管理、座位分配、乘客查询等场景下的数据结构选型问题。压缩包共3个文件&#xff0c;包含C语言源程序、可直接运行的exe文件以及说明…

作者头像 李华
网站建设 2026/10/10 3:03:54

Go商城读写分离实战:Gorm+Redis构建高并发后端

简介&#xff1a;面向毕业设计与课程设计场景的电子商城后端项目&#xff0c;完整使用 Gin、GORM、Redis 与 MySQL 构建并实现读写分离。项目覆盖 JWT 鉴权、CORS 跨域、AES 对称加密等安全与中间件处理&#xff0c;同时引入 ELK 体系便于日志查看&#xff0c;集成 Jaeger 与 S…

作者头像 李华
网站建设 2026/10/10 3:03:02

编译版Chromedriver特征抹除与配套浏览器版本匹配实战指南

简介&#xff1a;这是一份面向爬虫开发者与自动化测试人员的Chromedriver资源&#xff0c;针对浏览器指纹特征易被识别、导致自动化任务受阻的问题&#xff0c;提供了已完成特征抹除处理的驱动版本&#xff0c;目前仅支持Windows 10系统。压缩包共491个文件&#xff0c;约56MB&…

作者头像 李华
网站建设 2026/10/10 3:02:14

江苏大跨度网架加工厂 玖盛发钢结构设计加工一体化

大跨度网架市场需求持续增长&#xff0c;专业加工能力成为项目落地的关键 近年来&#xff0c;随着工矿企业环保改造、学校体育场馆建设、公共文化设施升级等项目持续推进&#xff0c;大跨度网架结构的应用场景越来越广。电厂干煤棚、水泥厂料场封闭、体育馆屋面、加油站罩棚、采…

作者头像 李华