ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

物流管理系统Java课程设计实战:Swing+MySQL全流程解析

物流管理系统Java课程设计实战:Swing+MySQL全流程解析 简介一套基于Java SE的物流管理系统完整项目采用Swing构建图形界面以MySQL作为数据持久化方案并通过JDBC完成数据增删改查适合正在学习Java SE、需要完成课程设计或想了解桌面应用开发流程的读者。压缩包共770个文件约5.76MB以Java源码为主717个同时包含MySQL的SQL脚本、properties等配置文件、项目报告PDF及IDEA导入教程pem/keystore等安全证书文件可用于数据库SSL连接等进阶场景。目前已吸引199人学习下载。项目中源码注释详尽系统展示了面向对象设计、输入输出、多线程、网络编程等JavaSE核心技术配套报告书对各功能模块进行了介绍配合导入教程可在IDEA中快速运行是理解C-S架构和完整项目组织方式的一份实用范例。1. 物流管理系统这个Java课程设计比你想象中更能打物流管理系统这个题目一听到“物流”两个字很多人第一反应是B/S架构、微服务、大屏可视化。但如果你准备入手的是一个用 Java Swing MySQL 搭建的桌面版项目别急着觉得它“老气”——在你需要交一份能跑、能讲、能答辩的课程设计或者想在一周内把JDBC和GUI整个串起来的场景下这套技术栈反而是最不容易翻车的组合。Swing没有前端依赖事件模型直白MySQL又是面试必问的东西一个项目把三者打通你简历上和答辩桌上都有话说。这篇笔记就按我实际做这类项目的顺序来先设计数据库再搭Swing界面再回到IDEA里把整个工程跑通最后把那些我踩过的坑一次说清楚。适合正在做课程设计、或者想用桌面应用练手JDBC的同学。2. 先立住数据底座物流管理系统的表结构和SQL文件怎么设计拿到一个带sql文件的项目第一步不是急着连数据库而是把SQL先读一遍。很多同学上来直接运行结果登录界面上不去一查是表里连admin账号都没有或者运单表的外键指向的货物表根本不存在。这个环节决定了后面所有代码能不能正常跑值得花半小时理清楚。2.1 五张核心表怎么划分从入库到签收的完整闭环一个物流管理系统的核心业务可以拆成两条线一条是货物流转一条是客户信息。常见的表划分是用户表、货物表、客户表、运单表和登录日志表。用户表负责登录和权限货物表存物品名称、重量、体积和数量客户表存寄件人和收件人的联系方式运单表则是整个系统的中枢记录了货物从哪个客户发出、送到哪个客户、当前状态是什么。登录日志表属于加分项用来记录谁在什么时间登录过系统报告里多一张表功能模块也多一个。我见过不少同学把表拆到八张以上每个小功能都单开一张表结果外键关系绕来绕去写JDBC查询的时候恨不得三表连查还带子查询。做课程设计表数量在五张左右最合适覆盖了登录、基础资料管理、业务流转三类场景CRUD操作都能体现又不至于把精力全耗在关联查询上。表之间的关键约束是运单表通过货物ID关联货物表通过客户ID关联客户表但注意不要在运单表里把寄件人和收件人拆成两个客户ID字段那样会让SQL变得很别扭。常见做法是运单表直接冗余存一份收件人姓名和电话寄件人信息则通过关联客户表获取这叫适度冗余报告里还能写一句“为减少联表查询次数”。2.2 建表SQL示例字段类型、默认值和状态字段的取舍直接看运单表的建表语句其他表的结构可以照这个思路扩展。注意这里用的是MySQL 8.0。CREATE DATABASE IF NOT EXISTS logistics DEFAULT CHARSET utf8mb4; USE logistics; CREATE TABLE t_user ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(30) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, real_name VARCHAR(30) DEFAULT , role TINYINT NOT NULL DEFAULT 1 COMMENT 1管理员 2操作员, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_goods ( id INT AUTO_INCREMENT PRIMARY KEY, goods_name VARCHAR(50) NOT NULL, weight DECIMAL(8,2) DEFAULT 0 COMMENT 单位kg, quantity INT NOT NULL DEFAULT 1, remark VARCHAR(255) DEFAULT ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_customer ( id INT AUTO_INCREMENT PRIMARY KEY, customer_name VARCHAR(50) NOT NULL, phone VARCHAR(20) NOT NULL, address VARCHAR(255) DEFAULT ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_order ( id INT AUTO_INCREMENT PRIMARY KEY, order_no VARCHAR(32) NOT NULL UNIQUE COMMENT 运单号, goods_id INT NOT NULL, customer_id INT NOT NULL, consignee_name VARCHAR(50) NOT NULL COMMENT 收件人, consignee_phone VARCHAR(20) NOT NULL, consignee_address VARCHAR(255) DEFAULT , status TINYINT NOT NULL DEFAULT 0 COMMENT 0待发货 1在途 2已签收, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_order_goods FOREIGN KEY (goods_id) REFERENCES t_goods(id), CONSTRAINT fk_order_customer FOREIGN KEY (customer_id) REFERENCES t_customer(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;字段类型有三个地方值得刻意说明。金额和重量这类数值用 DECIMAL 不用 FLOAT这个问题你可能现在感受不深等做报表求运费总和时精度对不上回头改表结构就麻烦了。状态字段用 TINYINT 并加上注释代码里用数字0、1、2表示而不是直接存“待发货”这样的字符串——中文状态在Java里比较大小、做条件过滤都别扭数字加注释在报告里也好解释。时间字段用 DATETIME 并且默认值指向 CURRENT_TIMESTAMP这样Java代码里不用手动塞时间删掉了一整类“日期格式不一致”的隐患。关于外键我建议保留。虽然有些老开发会说生产环境不用外键但这是课程设计外键能让ER图更好画也能让MySQL在数据完整性上帮你兜底。注意外键字段的类型必须和主表主键完全一致比如都是 INT否则建表直接报错。2.3 预置数据为什么SQL文件里必须有一管一号和两条测试运单很多项目下载下来连登录都进不去原因就是SQL文件里只有建表语句没有任何数据。你自己的项目至少要在SQL文件里放入这几个预置数据一个管理员账号用于登录系统两条已完成的运单和一条在途的运单用于展示列表和状态流转两个客户记录用于新增运单时下拉选择。INSERT INTO t_user (username, password, real_name, role) VALUES (admin, 123456, 系统管理员, 1); INSERT INTO t_customer (customer_name, phone, address) VALUES (张伟, 13800138000, 上海市浦东新区XX路1号); INSERT INTO t_customer (customer_name, phone, address) VALUES (李娜, 13900139000, 北京市朝阳区XX街2号); INSERT INTO t_goods (goods_name, weight, quantity) VALUES (电子产品, 2.50, 3); INSERT INTO t_goods (goods_name, weight, quantity) VALUES (日用百货, 8.00, 10); INSERT INTO t_order (order_no, goods_id, customer_id, consignee_name, consignee_phone, consignee_address, status) VALUES (YD20250101001, 1, 1, 王强, 13700137000, 广州市天河区XX大道, 2); INSERT INTO t_order (order_no, goods_id, customer_id, consignee_name, consignee_phone, consignee_address, status) VALUES (YD20250101002, 2, 2, 赵敏, 13600136000, 深圳市南山区XX路, 0);密码这里直接存了明文课程设计能接受但报告里最好写一句“生产环境应对密码做MD5加盐处理”这能体现你对安全问题的意识。预置数据还有一个作用验证你的JTable列表显示、状态筛选、删除、修改这几个核心操作时随时有数据可用不用每次重新造数。3. 用Swing把界面立起来主窗口、表格刷新和事件绑定的完整骨架Swing这套UI库的特点是“直给”——没有前端框架的中间层你拖一个按钮写一个事件点击就触发。但直给也有直给的坑比如表格刷新、数据校验、线程阻塞这些点写错了运行时不报错演示时却会卡死或闪退。这一章给出的三段代码正好覆盖Swing项目最核心的三个瞬间窗口怎么搭、数据怎么上表格、按钮怎么落库。3.1 JFrame骨架与布局为什么用BorderLayout而不是自由拖拽先看主窗口的搭建代码。注意几个容易被忽略的细节包括窗口关闭方式、居中方式、表格放在哪个区域。public class MainFrame extends JFrame { private JTable table; private DefaultTableModel model; private JTextField orderNoField; private JTextField consigneeField; public MainFrame() { setTitle(物流管理系统); setSize(960, 600); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); JPanel topPanel new JPanel(); topPanel.add(new JLabel(运单号:)); orderNoField new JTextField(15); topPanel.add(orderNoField); topPanel.add(new JLabel(收件人:)); consigneeField new JTextField(10); topPanel.add(consigneeField); JButton queryBtn new JButton(查询); JButton addBtn new JButton(新增运单); topPanel.add(queryBtn); topPanel.add(addBtn); model new DefaultTableModel( new String[]{运单号, 收件人, 联系电话, 状态, 创建时间}, 0); table new JTable(model); JScrollPane scrollPane new JScrollPane(table); setLayout(new BorderLayout()); add(topPanel, BorderLayout.NORTH); add(scrollPane, BorderLayout.CENTER); } public static void main(String[] args) { new MainFrame().setVisible(true); } }这段代码里的三个设计决定是有讲究的。setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)让点击右上角X时直接退出整个Java进程如果你改用DISPOSE_ON_CLOSE窗口关了但后台线程没退演示完会发现IDEA的控制台一直亮着红色的停止按钮。setLocationRelativeTo(null)是窗口居中属于新手最容易漏掉的那一行。布局用BorderLayout而不是直接setLayout(null)后拖组件是因为后者在你换屏幕分辨率或拉伸窗口时组件位置全部乱掉BorderLayout把顶部查询区和中间表格区固定好组件数量少结构又清晰是这类管理系统最省心的布局方案。主线程里直接new MainFrame().setVisible(true)即可。Swing有EDT线程模型的说法课程设计阶段不需要你去手动SwingUtilities.invokeLater只有涉及后台线程更新UI时才有意义那是进阶话题。3.2 让表格显示数据库数据DefaultTableModel清空再填充窗口只是空壳接下来把查询结果填进表格。这个方法的调用时机有两个窗口初始化时主动调一次以及后面每次增删改完成后调一次保证界面永远和数据库同步。private void refreshTable(String keyword) { // 第一步清空表格现有数据 model.setRowCount(0); StringBuilder sql new StringBuilder( SELECT order_no, consignee_name, consignee_phone, status, create_time FROM t_order WHERE 11); ListString params new ArrayList(); if (keyword ! null !keyword.trim().isEmpty()) { sql.append( AND (order_no LIKE ? OR consignee_name LIKE ?)); String like % keyword.trim() %; params.add(like); params.add(like); } sql.append( ORDER BY id DESC); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql.toString())) { for (int i 0; i params.size(); i) { ps.setString(i 1, params.get(i)); } try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Object[] row new Object[]{ rs.getString(order_no), rs.getString(consignee_name), rs.getString(consignee_phone), rs.getInt(status), rs.getString(create_time) }; model.addRow(row); } } } catch (SQLException e) { JOptionPane.showMessageDialog(this, 查询失败 e.getMessage()); } }这里最容易被忽略的是“先清空再填充”的顺序。DefaultTableModel不像你想象中有一个setDataVector可以直接批量替换直接把新查询结果addRow上去旧数据不会自己消失只会越叠越多。model.setRowCount(0)就是把行数归零这是Swing表格刷新的标准动作连很多工作两三年的Java程序员都容易在这里惯性犯错。查询条件用StringBuilder动态拼SQL参数统一放进List最后通过setString逐位填充这也是为了避开字符串拼接注入问题。WHERE 11看着像废话但它是“后续条件都是AND开头”这个写法的保底方案——没有它当关键词为空时你得多写一个分支去处理“WHERE”后面是AND还是WHERE代码立刻变丑。3.3 新增运单的按钮事件与PreparedStatement最后是新增运单的完整事件监听器。这段代码我建议直接照抄进你的项目再把类名和字段名改成自己的。addBtn.addActionListener(e - { String orderNo orderNoField.getText().trim(); String consignee consigneeField.getText().trim(); if (orderNo.isEmpty() || consignee.isEmpty()) { JOptionPane.showMessageDialog(MainFrame.this, 运单号和收件人不能为空, 校验提示, JOptionPane.WARNING_MESSAGE); return; } String sql INSERT INTO t_order(order_no, goods_id, customer_id, consignee_name, consignee_phone, status) VALUES(?, ?, 1, ?, 13800138000, 0); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { conn.setAutoCommit(false); ps.setString(1, orderNo); ps.setInt(2, 1); // 默认关联第1个货物演示用 ps.setString(3, consignee); ps.executeUpdate(); conn.commit(); refreshTable(null); orderNoField.setText(); consigneeField.setText(); JOptionPane.showMessageDialog(MainFrame.this, 运单创建成功); } catch (SQLException ex) { JOptionPane.showMessageDialog(MainFrame.this, 保存失败 ex.getMessage(), 错误, JOptionPane.ERROR_MESSAGE); } });代码里commit和setAutoCommit(false)的搭配是知识点默认情况下单条SQL会自动提交这里手动开启事务是为了演示“一个业务动作包含多条写操作时要么全成功要么全回滚”的思路。等你后期加“创建运单同时扣减库存”的功能直接在这段事务里再塞一条UPDATE即可。组件事件里的校验是前端校验写SQL时用PreparedStatement属于后端防线两道都有答辩时被问到“安全怎么保证”就有话讲。三个关键参数值得记住ps.setString给的是字符串字段ps.setInt给的是整数字段占位符下标从1开始。JOptionPane.WARNING_MESSAGE和ERROR_MESSAGE决定了弹窗左侧的图标差异这在报告界面说明里能写一句“友好的提示机制”。4. 回到IDEA里把项目跑起来导入、配驱动、连库三步走第三步拆解的是从拿到压缩包到界面正常弹出的完整过程。这三节里每一节都是我在帮同学调项目时反复操作的路径按顺序走基本能一次通过。4.1 IDEA导入工程的两种方式Open和Add as Maven Project别搞混拿到项目压缩包解压后打开IDEA注意一个关键原则用File Open选中项目根目录不是双击目录里的某个.java文件也不是File New Project。如果项目根目录下存在pom.xmlIDEA会自动识别为Maven工程并在右下角弹提示“Add as Maven Project”点它然后等依赖下载完。如果项目没有pom.xmlIDEA会把它当普通Java项目打开这种情况你不需要额外配置什么直接进入下一步装驱动。下载IDE时直接用IntelliJ IDEA社区版即可这个版本免费对Java基础开发和Swing完全够用没必要费心思折腾Ultimate旗舰版的授权问题。课程设计场景下社区版唯一的缺憾是缺少数据库自带的图形工具但这个问题可以用单独装一个MySQL Workbench解决后面导入SQL文件时也用得上。如果你之前用的是Eclipse跳过来之后最需要适应的就是IDEA的Project Structure窗口——它是管理依赖的核心位置后面几步都要在里面操作。4.2 装载JDBC驱动lib目录和Maven依赖两条路怎么选驱动装不上的典型症状是运行时报ClassNotFoundException: com.mysql.cj.jdbc.Driver结果你翻遍整个项目也找不到这个类在哪。原因很简单驱动JAR包根本没进编译路径。有Maven的项目在pom.xml里加入这一段即可版本号用你自己下载的即可注意和本机MySQL版本匹配。dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency没有Maven的项目走传统路线在项目根目录建一个lib文件夹把下载好的mysql-connector-java-8.0.x.jar复制进去然后右键这个JAR文件选择Add as Library弹出的窗口直接确认。这一步的本质是把JAR包注册到IDEA的模块依赖中操作完成后Project Structure Libraries里能看到它。这里有一条重要判断如果项目的代码里Class.forName用的还是“com.mysql.jdbc.Driver”这个老类名而你装的是MySQL 8的新驱动运行时大概率会报错。老驱动类名在8.0版本里已经改名成com.mysql.cj.jdbc.Driver。项目源码如果用的是老写法你有两条路改代码里的类名或者在驱动JAR里确认是否包含兼容类。多数情况下直接改代码类名最省事注意DBUtil类里只有一处Class.forName。4.3 连接参数配置useSSL、时区和公钥参数为什么要一起写驱动装好接下来是连接字符串。很多项目跑不起来问题不在代码而在JDBC URL少写了参数。直接给一套我亲测稳定的配置注意从中找到DBUtil类把URL替换掉String url jdbc:mysql://localhost:3306/logistics ?useSSLfalse characterEncodingutf8 serverTimezoneAsia/Shanghai allowPublicKeyRetrievaltrue; String user root; String password 你自己的密码;逐项解释为什么必须写。useSSLfalse解决的是MySQL 8把SSL握手默认开启后本地开发环境出现的大量告警以及偶发连接失败serverTimezoneAsia/Shanghai解决的是“The server time zone value ”这个报错MySQL 8时区默认是UTC而本机是东八区两者对不上直接拒绝连接allowPublicKeyRetrievaltrue配合MySQL 8默认的caching_sha2_password认证插件使用不把这个参数打开连接时会因为拿不到服务器公钥而中止——注意这个公钥参数和SSL不是一回事别混了。characterEncodingutf8解决的是中文写入乱码隐患。如果建库时指定了utf8mb4这里也可以写成utf8mb4Java端的字符集参数和MySQL端的表字符集尽量保持一致避免中间环节转码出错。这个字符集配置与MySQL安装时的默认字符集有关建议在MySQL Workbench里执行SHOW VARIABLES LIKE ‘character_set%’确认一下免得两边的预期不一致。数据库连接参数调通之后用Workbench把SQL文件跑一遍打开Workbench连上本机MySQL选择File Open SQL Script打开项目里的sql文件然后点击闪电图标执行。执行完刷新左侧导航树确认出现logistics库如果用命令行导入也可以语法是source /路径/sql文件但Workbench对新手更直观能看到具体的执行报错日志。5. 避坑Swing MySQL联调时最容易翻车的5个现场这章写的是我在帮人排查课程设计时遇到频率最高的五个问题每一条都是真实翻车现场。建议把这章截图存着出问题时对照着排查。5.1 中文乱码存进MySQL全是问号入库的中文变成“???”界面上显示也是乱码。原因是字符集链路中至少有一个环节不是UTF-8数据库表不是utf8mb4或连接URL没带characterEncodingutf8或IDEA的文件编码是GBK导致.java源码里的中文字符串本身就错了。解决办法是按顺序排查先执行SHOW CREATE TABLE确认表字符集再确认连接URL带characterEncodingutf8最后在IDEA的Settings Editor File Encodings里把Global Encoding和Project Encoding都设为UTF-8改完重启IDEA。最容易忽视的是第三项因为IDE默认的编码在不同系统上不一样Windows часто是GBK。5.2 MySQL 8连接报Communications link failure这个报错很抽象后面会跟一堆英文描述。原因集中在两种一是MySQL服务根本没启动在本机服务管理里把MySQL服务启动即可二是驱动版本太老8.0的MySQL Server配5.x的驱动认证协议对不上。解决办法是换成mysql-connector-java 8.0.x版本同时把URL补上serverTimezoneAsia/Shanghai和allowPublicKeyRetrievaltrue——这两个参数在新版本环境里几乎必填少了任何一个都可能在握手阶段中断。5.3 JDBC驱动类找不到但JAR包明明在运行时抛出ClassNotFoundException且在项目里搜索驱动类也搜索不到。原因大概率是JAR包只是躺在文件夹里没有被IDEA加入模块依赖。File Open里打开的项目如果带pom.xml且你点了Add as Maven Project等右下角进度条跑完即可如果没有pom.xml需要手动右键JAR文件选择Add as Library再等IDEA刷新。还有一个小概率是JAR放到了target目录里IDEA重新编译后把target目录清掉了JAR应该放到一个独立的lib目录而不是源码目录里。5.4 JTable点删除按钮时选中行一直是-1点击删除按钮没有反应断点发现getSelectedRow()返回-1。原因是用户没有先选中表格里的某一行或者表格的数据被排序/筛选过视图行号和模型行号对不上。解决办法是加一个判断如果selectedRow小于0则弹提示“请先选择一条记录”如果表格用了TableRowSorter删除前需要调用table.convertRowIndexToModel(selectedRow)把视图行号转换成模型行号否则你删除的可能是另一条数据。注意Swing的JTable选中状态默认是行级别的单击格子不算选中整行最好在表格上开启行选择模式。5.5 打包成JAR后双击无法运行在IDEA里运行一切正常但通过Artifacts打成JAR包后双击无反应命令行运行报“找不到主类”。原因是没有把第三方依赖JAR打进去或者MANIFEST.MF里没有声明Main-Class。解决方法是File Project Structure Artifacts选择Add JAR From modules with dependenciesMain Class选你的启动类同时在配置界面勾选“Copy to the output directory and link via manifest”这样打出来的JAR才包含JDBC驱动和完整的类路径。如果还不行最简单的兜底方案是直接给用户发一个“运行前先安装JDK并在命令行执行java -jar”的说明至少答辩时不会因为双击打不开冷场。6. 把这个项目讲成面试能打的样子三个改造方向和一次完整验证项目跑通了别急着交。课程设计答辩和面试官最烦的一句话是“这个项目是我网上找的”。你可以把这套Swing物流系统当底子做三个小改造就能在讲项目时底气足很多。第一处是用DAO模式把SQL从事件监听器里剥出来。现在代码里很多SQL写在按钮事件的匿名内部类里这在前面的代码示例里也有体现。你可以建一个OrderDao类把新增、查询、更新状态封装成方法按钮事件里只调dao.insert(order)——十行代码变一行面试官一看就知道你懂分层。第二处是把状态字段从魔法数字换成枚举。现在代码里status的0、1、2散落在SQL和Java里建一个OrderStatus枚举内部维护int值和中文字符串界面上显示中文、数据库存数字通过枚举转换彻底解决“状态对不上号”的问题。第三处是给运单号加唯一索引这在一开始建表时就已经体现面试问“并发重复提交怎么防止”时你可以说“运单号有UNIQUE约束重复插入直接抛异常再配合事务回滚”。做完改造动手跑一次完整的冒烟用例十个步骤启动MySQL服务IDEA里启动项目用admin账号登录进入主界面确认表格里有预置的3条数据输入关键词查询新增一条运单修改这条运单的状态为“在途”删除一条测试运单关闭程序重新打开确认数据持久化。这一步走下来你的程序交付给别人时基本不会有低级错误。我第一次用Swing做管理类系统时把状态字段在代码里写成了中文字符串“在途”报表模块那边存的却是数字1两边对不上数据怎么都对不齐后来才明白“数据库存数字、界面上做映射”才是这类系统该有的约定。这个教训之后我做任何带状态流转的系统先定状态字典再动手写代码节省的时间远超过前期那十分钟。希望这些细节对你也有用照着这个路径走一遍把数据库、界面、驱动三层跑通这个物流管理系统就是你的项目了。本文还有配套的精品资源点击获取
返回列表