☰
Java+MySQL+JDBC+JavaSwing实现超市管理系统课程设计实战指南
2026/10/29 1:30:25 网站建设 项目流程

简介:一份面向高校期末数据库课程设计的超市管理系统源码,基于Java、MySQL、JDBC与JavaSwing构建,适合需要完成C/S架构课程项目或复习JDBC开发的在校学生参考。代码覆盖商品进货、销售与库存管理等日常业务,SQL脚本可直接建库,便于理解数据库表关系、JDBC操作流程以及Swing界面事件处理。压缩包共24个文件,主体是17个Java源码,另有SQL数据库脚本、2个XML工程配置(含Maven相关设置)、2个FlatLaf界面美化JAR包和README说明文档,整包仅1.1MB,目录层级清晰,适合对照源码逐模块学习。项目基于JDK12与MySQL8.0,已在资源中注明编码为GBK,导入IDE时需注意防止中文乱码;附带的系统风格UI美化代码,可直接调用以改善界面观感。目前已有217人学习下载,整体体量小巧,适合课程设计答辩前查漏补缺,也可作为Java桌面应用开发的练手模板。

1. 期末数据库课程设计为什么都爱用这套Java+MySQL+JDBC+JavaSwing组合

很多期末数据库课程设计题目都会落在“某管理系统”上,超市管理系统就是其中最经典的一个。因为超市业务足够直观:商品、员工、供应商、销售记录,天然对应数据库里的增删改查。而技术选型上,Java+MySQL+JDBC+JavaSwing几乎成了课程设计的默认套餐:Java是教学主流语言,MySQL负责数据落地,JDBC是两者之间的桥梁,JavaSwing则用来画桌面窗口。这意味着不需要搭Web框架,不需要配置Tomcat,只要一台电脑和IDE就能跑起来给老师演示。

但真正动手才发现,光拿到一套“源码+数据库”不等于能顺利启动。mysql安装教程里的细节、JDBC驱动版本、连接URL参数、字符集、Swing界面刷新,任何一个环节出错都会卡住半天。这篇文章我按照做课设的顺序,把表设计、JDBC封装、Swing窗体、常见坑位一遍讲清楚。适合正准备做课程设计的学生,也适合想用最简技术栈理解CRUD桌面应用的从业者。

2. 超市管理系统的表结构设计:先想清楚功能边界再动手建库

2.1 功能模块怎么拆?从课程设计的验收标准反推

做课设最容易犯的错是动不动就设计十几张表,把自己绕进多表关联和存储过程里。超市管理系统在课程设计这个体量下,最稳妥的做法是守住一条主线:商品信息维护、员工登录、供应商管理、销售记录。这不叫偷懒,而是课程设计的验收标准通常只要求“完整走通增删改查”,所以你只要让表之间存在清晰的主外键关系,让每个界面都能展示和修改数据,就已经达标了。

我一般建议先画一张简单的功能清单,再决定表。可以把功能分为两类:一类是必须做,另一类是可选加分。必须做的是登录、商品增删改查、商品列表查询、员工管理;可选加分的是供应商维护、销售录入、库存联动、按条件搜索。对大多数课设,四张表就够用:employee(员工)、supplier(供应商)、product(商品)、sale(销售记录)。Sale表可以拆成sale_order和sale_item,但那样商品多字段查询要join两张表,复杂度上升不少。如果你还剩两周时间,我不建议拆;如果还剩一个月,可以拆。

下面的表格列出四张表各自承担的责任,以及它们之间怎么关联。这样验收时老师问起来,你能说清楚每一张表的意义。

表名核心字段与谁关联说明
employeeemp_no, password, rolesale.operator_id登录用户及操作员
suppliersupplier_name, contactproduct.supplier_id商品供应商,可选
productbarcode, price, stocksale.product_id商品主数据
salesale_no, quantity, total_priceproduct_id, operator_id每次销售记录

这样的设计还有一个好处:建表语句可以直接在mysql命令行或者MySQL Workbench里执行,导出成.sql文件就能交作业。

2.2 建库建表SQL:字段类型、字符集、外键约束一次到位

下面是一份可以直接执行的建表脚本。我特意用UTF-8字符集、DECIMAL类型存金额、给外键加上ON DELETE SET NULL等约束,因为这些细节在课设答辩里都是能拿出来说的加分点。

CREATE DATABASE IF NOT EXISTS supermarket DEFAULT CHARACTER SET utf8mb4; USE supermarket; CREATE TABLE employee ( emp_id INT AUTO_INCREMENT PRIMARY KEY, emp_no VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(50) NOT NULL, password VARCHAR(100) NOT NULL, role VARCHAR(20) DEFAULT 'cashier', created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB; CREATE TABLE supplier ( supplier_id INT AUTO_INCREMENT PRIMARY KEY, supplier_name VARCHAR(100) NOT NULL, contact VARCHAR(50), phone VARCHAR(20) ) ENGINE=InnoDB; CREATE TABLE product ( product_id INT AUTO_INCREMENT PRIMARY KEY, barcode VARCHAR(30) UNIQUE, product_name VARCHAR(100) NOT NULL, category VARCHAR(50), price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, supplier_id INT, CONSTRAINT fk_product_supplier FOREIGN KEY (supplier_id) REFERENCES supplier(supplier_id) ON DELETE SET NULL ) ENGINE=InnoDB; CREATE TABLE sale ( sale_id INT AUTO_INCREMENT PRIMARY KEY, sale_no VARCHAR(30) NOT NULL, product_id INT NOT NULL, quantity INT NOT NULL, total_price DECIMAL(10,2) NOT NULL, operator_id INT, sale_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_sale_product FOREIGN KEY (product_id) REFERENCES product(product_id), CONSTRAINT fk_sale_employee FOREIGN KEY (operator_id) REFERENCES employee(emp_id) ) ENGINE=InnoDB;

这里有几个选择值得说明。第一,金额用DECIMAL(10,2)而不是FLOAT或DOUBLE,因为浮点数在计算总价、找零时会产生精度误差,课设虽然不多,但老师如果让你连续添加商品你会发现价格对不上。第二,外键都要指定InnoDB引擎,因为MySQL默认的MyISAM不支持外键约束。第三,password字段长度给到100,是为了将来即使存哈希值也够用;如果你直接在课设里放明文,长度50就够了,但我不推荐。

关于外键的删除策略,我开始在产品表和供应商表之间用了ON DELETE SET NULL,意思是删除一个供应商后,商品表的supplier_id会被置为空,商品本身还在。如果你用默认的RESTRICT,删除供应商时会因为被商品引用而报错,很多课设就是在这里卡住。对于销售明细,sale表外键故意不加级联删除,因为销售记录是操作流水,不应该因为删了商品就消失。如果老师要求“删除商品后也要清除销售记录”,那就改成ON DELETE CASCADE,但这要小心,实际业务中流水都要留痕。

2.3 初始化数据:课程设计要交的“源码+数据库”里数据该放多少

建完表就该插入演示数据。很多同学从网上下载的源码自带一个很大的.sql文件,几十张表上千条数据,反而不好向老师解释。我们自己做的项目,初始化数据按“能演示一次完整流程”的量来放就可以:3到5个供应商、20到30个商品、3到5个员工、10条左右销售记录。这样商品列表下拉框不会空,销售明细也看得见。

INSERT INTO supplier (supplier_name, contact, phone) VALUES ('华东食品批发', '王强', '13800000001'), ('本地日用品商行', '刘敏', '13800000002'); INSERT INTO product (barcode, product_name, category, price, stock, supplier_id) VALUES ('6901020304010', '矿泉水 550ml', '饮料', 2.00, 120, 1), ('6901020304011', '方便面 红烧牛肉', '食品', 4.50, 80, 1), ('6901020304012', '抽纸 3层', '日用品', 6.90, 50, 2); INSERT INTO employee (emp_no, name, password, role) VALUES ('admin', '系统管理员', '123456', 'admin'), ('cashier1', '收银员小李', '123456', 'cashier');

插入数据时要注意外键顺序,先插supplier再插product,否则会违反外键约束。如果你希望数据库文件里直接带数据,建议用mysqldump导出:

mysqldump -uroot -p supermarket > supermarket.sql

这样导出的.sql文件既包含建表语句也包含INSERT数据,老师用mysql命令一键导入就能复现。注意导出的时候加--default-character-set=utf8mb4,否则遇到中文可能乱码。这个初始化数据的动作本身就是一次很好的数据库增删改查练习,老师问起“数据从哪里来”时,你可以说你手工构造了一套完整的测试样本。

3. 用JDBC连接MySQL:驱动、URL、DBUtil封装和常见参数

3.1 先确认环境:mysql安装教程里的版本与驱动匹配

连接MySQL前,你首先要确认本机环境。很多课设翻车不是代码问题,而是mysql安装配置和Java版本对不上。常见组合是:MySQL 5.7.44 + Java 8 + mysql-connector-java 5.1.49,或者MySQL 8.0 + Java 8/11 + mysql-connector-j 8.0.x。前者在Windows10上安装比较省事,后者是当前主流。

我用一个表格列出推荐组合,方便你照抄:

本机MySQL版本JDBC驱动包Driver类是否需要在URL加serverTimezone
5.7.xmysql-connector-java-5.1.49.jarcom.mysql.jdbc.Driver可不加
8.0.xmysql-connector-j-8.0.33.jarcom.mysql.cj.jdbc.Driver需要

这里有个坑:MySQL 8.0默认使用caching_sha2_password认证,老驱动连不上,所以驱动必须选8.x版本。另外驱动包下载后要手动导入IDE的Build Path,Eclipse里右键项目 -> Properties -> Java Build Path -> Add JARs。IDEA则是Project Structure -> Libraries里加。别只把jar文件放到项目目录就以为加载了,运行到Class.forName会直接报ClassNotFoundException。

3.2 JDBC连接MySQL的标准步骤:加载驱动、建立连接、释放资源

JDBC连接的标准动作就是三步:加载驱动、获取连接、执行SQL后释放。下面这段代码是最基本的连接测试,也是后面所有功能的骨架。

import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class JdbcDemo { public static void main(String[] args) { try { Class.forName("com.mysql.cj.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/supermarket" + "?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"; Connection conn = DriverManager.getConnection(url, "root", "123456"); System.out.println("连接成功"); conn.close(); } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); } } }

参数说明:class.forName()在Java 8及以后其实不是必须的,但保留它是为了让教学代码兼容老环境,也能提前发现驱动包是否缺。URL里的localhost:3306指本机MySQL端口,如果你安装mysql时改了端口,这里必须跟着改。useSSL=false是为了避免MySQL 8默认开启SSL后握手失败。serverTimezone=Asia/Shanghai是因为JDBC驱动需要知道时区,否则会报“The server time zone value”错误。characterEncoding=utf8是防止中文乱码的关键,少了它中文会变成问号。

连接不是用一次就没,但每次用完都要关闭。建议用try-with-resources,可以自动关闭,写起来也简洁。如果你还在用Java 7以下,就必须在finally里手动关闭,否则连接池会被占满。课设虽然只有一个用户在跑,但老师会看你的代码风格。

提示:连接串里的 root 密码要改成你自己的。如果你用 MySQL Workbench 测试过连接,知道本机端口不是 3306 时,记得同步修改。

3.3 封装一个DBUtil:让增删改查少写三行重复代码

把连接逻辑抽到一个工具类里,是JDBC课设必须有的封装。下面这个DBUtil类足够满足课程设计,不需要引入第三方连接池。

import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class DBUtil { private static final String URL = "jdbc:mysql://localhost:3306/supermarket" + "?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"; private static final String USER = "root"; private static final String PASSWORD = "123456"; static { try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void executeUpdate(String sql, Object... params) throws SQLException { try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { for (int i = 0; i < params.length; i++) { ps.setObject(i + 1, params[i]); } ps.executeUpdate(); } } public static ResultSet executeQuery(String sql, Object... params) throws SQLException { Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql); for (int i = 0; i < params.length; i++) { ps.setObject(i + 1, params[i]); } return ps.executeQuery(); } }

注意上面的executeQuery返回ResultSet时,我没有关闭Connection和PreparedStatement,因为ResultSet还需要用。这意味着调用方用完ResultSet要自己关闭,或者使用一个思路是传Connection进去,由调用方统一管理。课设中这是最常见的两种模式,选择哪种不重要,关键是别泄漏连接。如果你要更稳妥,可以不用executeQuery工具方法,而在业务代码里完整写try-with-resources。我给出的executeUpdate适合INSERT、UPDATE、DELETE这种不需要返回数据的操作,参数通过可变参数Object...传入,比直接拼字符串安全得多,能防止SQL注入。

3.4 有必要搞连接池吗?课程设计阶段我建议不碰

很多同学看到网上资料提到连接池,就觉得高大上,想在课设里用Druid或者HikariCP。我的建议是别在课程设计里用。原因有二:第一,课设是演示为主,单个用户操作同一张表,连接池带来的性能提升根本看不出来,反而增加配置文件、依赖包,老师还可能质疑你是否理解JDBC本质;第二,数据库连接池通常要配连接数、空闲超时、最大等待时间参数,这些概念超出了数据库课程设计的范围。

真正该做的是把上面的DBUtil封装好,然后把数据库增删改查操作都走PreparedStatement。等以后做分布式项目,再学Spring Boot整合Druid或HikariCP也不迟。这就像写java基础题,排序再花哨,冒泡排序写对的人才能讲清楚时间复杂度,连接池也是这个道理。

4. JavaSwing界面搭起来:登录窗体、主窗体、商品表格和增删改查事件

4.1 用JFrame搭主窗体:布局和组件选型

JavaSwing做桌面窗口,核心是JFrame加布局管理器。课程设计不用研究复杂的自定义布局,BorderLayout配合JTabbedPane足够。下面是一个主窗体骨架:

import javax.swing.*; import java.awt.*; public class MainFrame extends JFrame { private JTable productTable; private DefaultTableModel productModel; public MainFrame() { setTitle("超市管理系统"); setSize(900, 600); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); JTabbedPane tabbedPane = new JTabbedPane(); tabbedPane.addTab("商品管理", buildProductPanel()); tabbedPane.addTab("员工管理", new JPanel()); tabbedPane.addTab("销售记录", new JPanel()); add(tabbedPane, BorderLayout.CENTER); // 菜单栏 JMenuBar menuBar = new JMenuBar(); JMenu fileMenu = new JMenu("系统"); JMenuItem exitItem = new JMenuItem("退出"); exitItem.addActionListener(e -> System.exit(0)); fileMenu.add(exitItem); menuBar.add(fileMenu); setJMenuBar(menuBar); } private JPanel buildProductPanel() { JPanel panel = new JPanel(new BorderLayout()); productTable = new JTable(); productModel = new DefaultTableModel(new String[]{"ID", "条形码", "名称", "价格", "库存"}, 0); productTable.setModel(productModel); panel.add(new JScrollPane(productTable), BorderLayout.CENTER); return panel; } }

重点说一下Swing线程规则。Swing是单线程模型,所有界面更新都要在事件分派线程(EDT)里执行。你在main里直接new MainFrame().setVisible(true)是可以的,因为main线程调用Swing组件的构造方法不涉及并发;但如果你在一个工作线程里查询数据库,然后直接调用model.addRow,界面可能卡死或刷新异常。常见做法是使用SwingUtilities.invokeLater包住耗时任务执行完后的界面更新。

4.2 用JTable展示商品列表:把ResultSet转成TableModel

JTable是JavaSwing里最常用的数据表格组件。它不直接接受ResultSet,中间需要经过TableModel适配。我的做法是维护一个DefaultTableModel,每次从数据库查出数据后,先清空原模型再把新数据一行行加进去。这样也让“修改后刷新表格”变成一件很简单的事。

private void loadProductList() { String sql = "SELECT product_id, barcode, product_name, category, price, stock FROM product ORDER BY product_id"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery()) { productModel.setRowCount(0); // 清空旧的展示 while (rs.next()) { productModel.addRow(new Object[]{ rs.getInt("product_id"), rs.getString("barcode"), rs.getString("product_name"), rs.getString("category"), rs.getBigDecimal("price"), rs.getInt("stock") }); } } catch (SQLException e) { e.printStackTrace(); JOptionPane.showMessageDialog(this, "加载商品列表失败"); } }

这里有一个性能无关但影响体验的细节:ORDER BY product_id。如果不排,MySQL返回结果顺序不保证,你添加一条数据后刷新列表,新数据可能不在表格最后一行,看起来像没插入成功,导致老师以为你的增删改查有问题。加上主键排序后,表格顺序稳定,演示效果好很多。

注意:在监听器里调用 loadProductList 之前,如果正在处理长时间查询,最好用 SwingWorker 或 invokeLater 来做,否则点按钮时窗口会无响应。

4.3 增删改查绑定按钮:校验、SQL参数、刷新缺一不可

商品管理的增删改查一般做成一个表单弹窗或右侧面板。以添加商品为例,按钮事件里至少要做三件事:非空校验、用PreparedStatement参数传递、刷新列表。下面代码演示了添加商品:

btnAdd.addActionListener(e -> { String name = nameField.getText().trim(); String priceStr = priceField.getText().trim(); String stockStr = stockField.getText().trim(); if (name.isEmpty() || priceStr.isEmpty() || stockStr.isEmpty()) { JOptionPane.showMessageDialog(this, "名称、价格、库存不能为空"); return; } try { BigDecimal price = new BigDecimal(priceStr); int stock = Integer.parseInt(stockStr); String sql = "INSERT INTO product(product_name, price, stock) VALUES(?, ?, ?)"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, name); ps.setBigDecimal(2, price); ps.setInt(3, stock); ps.executeUpdate(); } loadProductList(); } catch (NumberFormatException ex) { JOptionPane.showMessageDialog(this, "价格或库存格式不正确"); } catch (SQLException ex) { ex.printStackTrace(); JOptionPane.showMessageDialog(this, "添加失败:" + ex.getMessage()); } });

我不会在添加时直接把JTable的当前行清空,而是调用loadProductList重新从数据库读取,这样能保证界面和数据库内容一致。因为数据库里可能有默认值、外键产生的新内容,人工同步容易遗漏。所有更新删除操作做完后,都应该重新拉一次列表。

删除操作要注意外键约束,如果商品已经被销售记录引用,直接DELETE会报错。我的建议是在删除前先检查是否有关联销售记录,或者给用户提示“是否同时删除相关销售记录”。修改操作类似,用UPDATE语句并带上原主键条件,不要把所有同名商品的库存都改了。课程设计里最容易犯的低级错误,是在UPDATE和DELETE的WHERE条件里漏了主键,导致批量修改。所以每次写UPDATE,第一反应就是要看WHERE后面有没有主键。

4.4 登录界面与密码校验的正确姿势

登录窗体验证逻辑一般是在“登录”按钮的ActionListener里,根据输入的员工号和密码去employee表查询。但要注意一个典型的并发问题:如果登录按钮被连续点两下,会同时发起两个查询,导致两次窗体打开。通常给按钮加一个boolean loading标志,或者在进入校验时btnLogin.setEnabled(false),查询结束finally里恢复。

另外,很多课设代码里用用户名+密码拼SQL字符串去查库,这是最容易被老师挑出毛病的写法。用PreparedStatement代替字符串拼接,既是习惯问题,也是安全问题。下面是登录校验片段:

String sql = "SELECT * FROM employee WHERE emp_no = ? AND password = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, empNoText.getText().trim()); ps.setString(2, new String(passwordField.getPassword())); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { // 登录成功 } else { JOptionPane.showMessageDialog(this, "工号或密码错误"); } } }

这里使用passwordField.getPassword()而不是getText(),因为JPasswordField的getText()已过时,而且密码用char数组可以在使用后清空。当然,课程设计里密码明文存储已经被很多老师接受,但如果你用哈希存储(比如SHA-256),在答辩里会是亮点。不过哈希需要处理加盐,建议作为加分项而不是必选。

5. 课程设计避坑:JDBC连接、Swing刷新、数据提交常见的5个翻车点

5.1 现象:JDBC连接MySQL时报“SSL connection error”或“Public Key Retrieval not allowed”

这是MySQL 8.0连接时报得最频繁的错误之一。原因在于MySQL 8.0默认开启useSSL,而客户端没有配置证书,或连接驱动版本不支持caching_sha2_password认证。解决方法是连接URL中加上useSSL=false,对于MySQL 8.0.4以后版本,可能还要加allowPublicKeyRetrieval=true。我的经验是,本地课程设计连接串统一写成:

jdbc:mysql://localhost:3306/supermarket?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8

有些同学从网上复制连接串,只写了serverTimezone=UTC,在国内时区跑会显示时间差8小时,如果你设计里需要展示销售时间,最好显式写成Asia/Shanghai。

5.2 现象:插入数据库的中文变成了问号

这是字符集问题,而且常常出现在你已经做了准备的情况下。原因可能有三层:数据库或表的字符集不是utf8mb4;JDBC URL没带characterEncoding;导入.sql文件时客户端默认字符集不对。解决方法是先检查建库语句是否包含DEFAULT CHARACTER SET utf8mb4,再检查URL连接串,最后在导入数据时设置客户端字符集。我一般用MySQL Workbench导入时,会在连接设置里把Character Set选为utf8mb4,而不是靠系统默认。如果用了命令行导入,就写成:

mysql --default-character-set=utf8mb4 -uroot -p supermarket < supermarket.sql

检查字符集可以执行SHOW CREATE TABLE product;确认表上写的CHARSET。

5.3 现象:在JTable里修改数据后,界面不刷新或刷新了但显示的是旧数据

原因常常是直接用JTable的默认Model,或者把数据库连接放进JTable的渲染里,导致更新数据时没有同步到UI线程。解决方法是使用DefaultTableModel,并在每次增删改后调用table.setModel(model)或者model.setRowCount(0)后再addRow。这里要重点提醒:如果在监听器里循环addRow,最好把JTable的更新用SwingUtilities.invokeLater包起来,避免在EDT之外改Model。不然有时会看到表格只刷新一部分,甚至偶尔抛ConcurrentModificationException。

5.4 现象:删除一个供应商或商品时,程序抛外键约束异常

数据库增删改查最容易翻车的就是外键。发生这种现象是因为你引用了主表的行,而删除策略是RESTRICT。解决有两条路:一是建表时给外键加上ON DELETE SET NULL或者ON DELETE CASCADE,比如product引用supplier,你希望删除供应商后商品还在;二是删除前先在Java代码里检查关联记录是否存在。我建议两条都做:建表时约束管住底层的完整性,Java代码里提示用户“该供应商被N个商品引用,删除后这些商品的供应商会置空”。这样老师操作时不会看到冷冰冰的SQL异常,而是友好的对话框。

5.5 现象:批量插入或修改时,一部分成功一部分失败,数据变成了半截状态

这是事务问题。JDBC默认是自动提交,每条SQL执行完就commit,所以当你先插入一条员工,再插入多条销售记录,中间某条SQL失败时,前面的插入已经生效。课设系统一般不会出大问题,但如果你要演示“销售单+多条明细”这样需要原子性的业务,就必须自己管理事务。常见的写法是:

Connection conn = DBUtil.getConnection(); try { conn.setAutoCommit(false); PreparedStatement ps1 = conn.prepareStatement("INSERT ..."); ps1.executeUpdate(); PreparedStatement ps2 = conn.prepareStatement("UPDATE product SET stock = stock - ? WHERE product_id = ?"); ps2.executeUpdate(); conn.commit(); } catch (SQLException e) { conn.rollback(); e.printStackTrace(); } finally { conn.setAutoCommit(true); conn.close(); }

核心是setAutoCommit(false),把多条语句包进一个事务,出错时rollback。注意事务结束后要恢复自动提交,并把连接关闭,否则下一次从连接池拿到的连接会带着上次的事务状态。课程设计里有这样一个“库存扣减+销售记录”的联动操作,答辩时老师会另眼相看。

6. 给课设加点分:模糊搜索和一键导出销售记录,答辩更省力

课程设计最后阶段,我习惯加两个小而实用的功能:一个是商品模糊搜索,另一个是把销售记录导出成CSV文件。它们不需要引入第三方框架,但能让评委看到你对数据库和流处理的理解。

模糊搜索很简单,就是SQL里的LIKE配合用户输入的关键字。这里要注意,LIKE里的百分号要放在参数里,而不是拼在SQL字符串里,避免SQL注入。搜索按钮的关键代码如下:

String keyword = searchField.getText().trim(); String sql = "SELECT product_id, barcode, product_name, price, stock FROM product WHERE product_name LIKE ? ORDER BY product_id"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, "%" + keyword + "%"); try (ResultSet rs = ps.executeQuery()) { productModel.setRowCount(0); while (rs.next()) { productModel.addRow(new Object[]{ /* 读取结果 */ }); } } }

导出销售记录到CSV则是另一个能满足“成果展示”的功能。因为在桌面应用里集成Excel导出通常要引poi,对课设来说太重。CSV文件用Excel打开即可,核心代码如下:

try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement("SELECT sale_no, product_name, quantity, total_price, sale_time FROM v_sale_detail"); ResultSet rs = ps.executeQuery(); FileWriter writer = new FileWriter("sale_records.csv")) { writer.write("销售单号,商品名称,数量,总价,时间\n"); while (rs.next()) { writer.write(String.format("%s,%s,%d,%.2f,%s\n", rs.getString("sale_no"), rs.getString("product_name"), rs.getInt("quantity"), rs.getBigDecimal("total_price"), rs.getTimestamp("sale_time"))); } }

如果sale表只有product_id,没有商品名称,你导出前要先做一次JOIN或者创建视图v_sale_detail。所以在表设计时,我建议保留一个view,导出和展示都从视图查,这样业务代码更清爽。这也是一个能在验收时解释的点:我不是把所有数据堆在一张表里,而是让视图承担了联表逻辑。

按我自己的课设经验,代码写到这个程度,答辩时只要把“设计四张表→JDBC封装→Swing绑定→事务扣库存”这条线讲清楚,老师通常不会为难你。别去追求花哨的布局和动效,因为JavaSwing本身是为桌面工具设计的,压得住场比什么都重要。希望这份笔记能让你少熬几个夜,也祝你的课程设计一次过。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询