Java+MySQL+JDBC+JavaSwing实现超市管理系统课程设计实战指南
简介一份面向高校期末数据库课程设计的超市管理系统源码基于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. 期末数据库课程设计为什么都爱用这套JavaMySQLJDBCJavaSwing组合很多期末数据库课程设计题目都会落在“某管理系统”上超市管理系统就是其中最经典的一个。因为超市业务足够直观商品、员工、供应商、销售记录天然对应数据库里的增删改查。而技术选型上JavaMySQLJDBCJavaSwing几乎成了课程设计的默认套餐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 ) ENGINEInnoDB; CREATE TABLE supplier ( supplier_id INT AUTO_INCREMENT PRIMARY KEY, supplier_name VARCHAR(100) NOT NULL, contact VARCHAR(50), phone VARCHAR(20) ) ENGINEInnoDB; 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 ) ENGINEInnoDB; 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) ) ENGINEInnoDB;这里有几个选择值得说明。第一金额用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-setutf8mb4否则遇到中文可能乱码。这个初始化数据的动作本身就是一次很好的数据库增删改查练习老师问起“数据从哪里来”时你可以说你手工构造了一套完整的测试样本。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加serverTimezone5.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 PathEclipse里右键项目 - 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 ?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8; 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时改了端口这里必须跟着改。useSSLfalse是为了避免MySQL 8默认开启SSL后握手失败。serverTimezoneAsia/Shanghai是因为JDBC驱动需要知道时区否则会报“The server time zone value”错误。characterEncodingutf8是防止中文乱码的关键少了它中文会变成问号。连接不是用一次就没但每次用完都要关闭。建议用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 ?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8; 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转成TableModelJTable是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中加上useSSLfalse对于MySQL 8.0.4以后版本可能还要加allowPublicKeyRetrievaltrue。我的经验是本地课程设计连接串统一写成jdbc:mysql://localhost:3306/supermarket?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaicharacterEncodingutf8有些同学从网上复制连接串只写了serverTimezoneUTC在国内时区跑会显示时间差8小时如果你设计里需要展示销售时间最好显式写成Asia/Shanghai。5.2 现象插入数据库的中文变成了问号这是字符集问题而且常常出现在你已经做了准备的情况下。原因可能有三层数据库或表的字符集不是utf8mb4JDBC URL没带characterEncoding导入.sql文件时客户端默认字符集不对。解决方法是先检查建库语句是否包含DEFAULT CHARACTER SET utf8mb4再检查URL连接串最后在导入数据时设置客户端字符集。我一般用MySQL Workbench导入时会在连接设置里把Character Set选为utf8mb4而不是靠系统默认。如果用了命令行导入就写成mysql --default-character-setutf8mb4 -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本身是为桌面工具设计的压得住场比什么都重要。希望这份笔记能让你少熬几个夜也祝你的课程设计一次过。希望帮到你。本文还有配套的精品资源点击获取