ARTICLE DETAIL

资讯详情

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

Java Swing+MySQL物资管理系统实战:CRUD到报表导出全解析

Java Swing+MySQL物资管理系统实战:CRUD到报表导出全解析 简介一套面向高校课程设计与期末大作业的Java Swing物资信息管理系统项目整合MySQL数据库定位为可直接运行、可二次扩展的完整示例。资源共34个文件包含27个Java源码覆盖登录、主界面、物资出入库、供应商管理、统计查询等模块、3个SQL脚本含建库表、触发器、存储过程、数据库设计文档及README说明整体压缩包仅4.74MB结构清晰便于按需查阅。目前已有133人学习下载属于高分完成度项目评审达98分。下载后可获得经过本地编译验证的完整工程、数据库初始化语句与设计文档能支撑从环境搭建、表结构设计到Swing界面与业务逻辑联调的全过程适合希望快速理解分层开发或直接改造用于自身课设的初学者。1. 为什么“高分”的反而是老实巴交的JavaSwingMySQL先把项目定位看清楚单看技术栈Java Swing MySQL 在AI和微服务满天飞的今天显得像上个时代的产物。但真正落到课程设计、毕业设计和实训验收上这类组合反而最容易出“高分”评分老师看重的从来不是框架多新而是业务闭环完整度。物资信息管理系统本质上就是一个标准的 CRUD 出入库流程 统计报表训练场Swing 负责把数据库里的数据变成肉眼可见的表格、树和对话框MySQL 负责把每一笔账记明白。它适合三类人需要课程设计交付物的在校生、想用桌面程序练手 JDBC 与事务的 Java 初学者、以及要给单位做内部小工具却不想上 Web 项目的后端工程师。这个标题真正的价值不在“界面好看”而在“从登录到报表一个环节不缺”。2. 用SwingMySQL把最小骨架跑通环境选型与JDBC连接池2.1 环境选型JDK 8 MySQL 5.7/8.0为什么不用Spring Boot先说结论这套项目最佳环境就是 JDK 8 MySQL 5.7 或 8.0IDE 用 Eclipse 或 IntelliJ IDEA 都行。不引入 Spring Boot 的原因很现实课设和实训的验收环境往往是一台没有外网、没有 Maven 私服的教学机你能拿到的只有 JDK 和一个 MySQL 服务端。Swing 是 JDK 自带的双击就能编译运行JDBC 驱动打个 jar 包扔进 lib 目录就能用。这不是技术倒退这是交付稳定性优先的务实选择。我一般建议 MySQL 用 5.7.44 或 8.0 系注意安装时把字符集选成 utf8mb4排序规则选 utf8mb4_general_ci。如果装的是 MySQL 8.0连接驱动的配置会和 5.x 有差异这事后面避坑章专门讲。JDK 版本别追新JDK 8 对 Swing 的渲染最稳定高版本 JDK 在高分屏上偶尔会出现模糊或布局错位的老毛病。2.2 先立规矩建库建表与初始数据脚本动手写界面之前先建数据库。很多新手一上来就写界面写到后面发现字段对不上返工成本极高。正确的顺序是先设计表结构再写 DAO最后布局界面。下面是这套系统最核心的四张表我通常把它们写在一个 init.sql 里用 Navicat 或命令行直接执行。CREATE DATABASE IF NOT EXISTS material_db DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; USE material_db; -- 用户表登录模块的唯一数据来源 CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, real_name VARCHAR(50) ) ENGINEInnoDB; -- 物资分类表parent_id 为 0 表示一级分类支持无限层级 CREATE TABLE t_category ( id INT PRIMARY KEY AUTO_INCREMENT, parent_id INT DEFAULT 0, name VARCHAR(100) NOT NULL ) ENGINEInnoDB; -- 物资信息表stock 是实时库存safety_stock 是低库存预警线 CREATE TABLE t_material ( id INT PRIMARY KEY AUTO_INCREMENT, code VARCHAR(50) NOT NULL UNIQUE, name VARCHAR(100) NOT NULL, category_id INT, spec VARCHAR(100), unit VARCHAR(20), stock INT DEFAULT 0, safety_stock INT DEFAULT 0, remark VARCHAR(255) ) ENGINEInnoDB; -- 出入库流水表table_type 用 1 表示入库、2 表示出库 CREATE TABLE t_stock_io ( id INT PRIMARY KEY AUTO_INCREMENT, material_id INT NOT NULL, io_type TINYINT NOT NULL COMMENT 1入库 2出库, quantity INT NOT NULL, operator VARCHAR(50), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB;逻辑说明t_user 用于登录密码我建议存 MD5 摘要而不是明文t_category 用 parent_id 实现树形结构这是 Swing 里 JTree 的数据基础t_material 的 stock 字段是冗余设计的虽然违反第三范式但换来了查询速度——你不需要每次 SUM 流水表才知道库存t_stock_io 是流水账任何出入库操作都往这里插一条记录作为审计依据。参数说明数量字段全部用 INT物资管理场景几乎不会出现小数。如果要支持按箱、按包入库建议加一个 conversion 单位换算字段但这属于进阶基础版先不做。create_time 用 DATETIME 而不是 TIMESTAMP避免 2038 年问题。2.3 写一个不翻车的DBUtilProperties配置 静态连接获取Swing 项目里最怕的不是界面复杂而是数据库连接管理混乱。常见做法是写一个 DBUtil 工具类把 URL、用户名、密码全部放进 db.properties 配置文件避免硬编码。这个类是整个系统的黑匣子所有 DAO 都从它身上拿 Connection。package com.ermu.util; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.util.Properties; public class DBUtil { private static String url; private static String user; private static String password; // 静态代码块只在类加载时执行一次 static { try (InputStream in DBUtil.class.getClassLoader() .getResourceAsStream(db.properties)) { Properties props new Properties(); props.load(in); // 8.x 驱动必须用 com.mysql.cj.jdbc.Driver Class.forName(props.getProperty(driver)); url props.getProperty(url); user props.getProperty(user); password props.getProperty(password); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws Exception { return DriverManager.getConnection(url, user, password); } }对应的 db.properties 文件drivercom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/material_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8 userroot password123456逻辑说明静态代码块保证驱动只注册一次getConnection 每次调用都是全新的物理连接。对于课设级别的并发量几十人同时用够用了没必要上连接池。如果日后要接入 HikariCP只需要改这个方法内部实现调用方不用动。参数说明useSSLfalse 一定要加MySQL 8.0 默认开启 SSL 验证不加会直接报 SSL 连接错误serverTimezoneAsia/Shanghai 用于解决时区差 8 小时的问题。只要这两项没配齐程序跑起来就是玄学报错而且是随机报错。2.4 用JFrameJTable拼出主窗体从代码看到界面数据库就绪后先做一个最小主窗体验证整条链路窗体加载时从 t_material 读数据显示在 JTable 里。这个步骤跑通了后面的业务功能都是在同一套模式下往里面加。public class MainFrame extends JFrame { private JTable table; private DefaultTableModel model; public MainFrame() { setTitle(物资信息管理系统); setSize(900, 600); setDefaultCloseOperation(EXIT_ON_CLOSE); // 表格列名 String[] columns {编号, 物资编码, 物资名称, 规格, 单位, 库存}; model new DefaultTableModel(columns, 0); table new JTable(model); add(new JScrollPane(table), BorderLayout.CENTER); loadData(); // 从数据库加载 setLocationRelativeTo(null); setVisible(true); } private void loadData() { try (Connection conn DBUtil.getConnection(); Statement st conn.createStatement(); ResultSet rs st.executeQuery( SELECT id, code, name, spec, unit, stock FROM t_material)) { while (rs.next()) { model.addRow(new Object[]{ rs.getInt(id), rs.getString(code), rs.getString(name), rs.getString(spec), rs.getString(unit), rs.getInt(stock) }); } } catch (Exception e) { JOptionPane.showMessageDialog(this, 加载失败: e.getMessage()); } } }逻辑说明这里用 DefaultTableModel 而非自定义 TableModel是为了省事。addRow 每次调用都会触发表格重绘数据量在几千条以内完全无感。窗口布局用 BorderLayout表格放在 CENTER 区域未来左侧放分类树的 JTree、顶部放工具栏就齐活了。参数说明new DefaultTableModel(columns, 0) 第二个参数是初始行数传 0 表示空表格然后逐行 addRow。setSize(900, 600) 是常规桌面分辨率下限低于这个在 1366×768 的笔记本上会显得拥挤。这一步做完你应该能看到一张能显示数据库内容的表格看不到就说明 JDBC 链路有问题回头检查 2.3 的配置。3. 物资管理的核心业务分类树、资料维护、出入库与库存账3.1 用带勾选框的JTree做物资分类勾选联动与节点展开的坑物资分类用树形结构展示是最直观的JTree 本身支持默认节点但要实现“勾选框勾选分类下方表格只显示该分类下物资”的效果得动点手脚。热词里提到的“swing 带勾选框的 tree”正是这个功能的搜索入口这里给出一个可复现的做法。class CheckBoxTreeNode extends DefaultMutableTreeNode { private boolean selected false; public CheckBoxTreeNode(Object userObject) { super(userObject); } public boolean isSelected() { return selected; } public void setSelected(boolean selected) { this.selected selected; // 联动子节点勾选父节点时子节点全部跟随 for (int i 0; i getChildCount(); i) { Object child getChildAt(i); if (child instanceof CheckBoxTreeNode) { ((CheckBoxTreeNode) child).setSelected(selected); } } } // 关键重写 children()让 JTree 正确识别勾选状态 Override public Enumeration? children() { return super.children(); } }逻辑说明这个自定义节点的核心是 setSelected 方法里的递归遍历——勾选一级分类时它的所有子分类自动同步勾选状态这是树形勾选最常见的交互预期。重写 children() 是为了确保 JTree 的展开和折叠逻辑不被自定义字段干扰很多 Swing 开发者在这里翻车不重写时节点表现为“能勾选但展开后子节点状态错乱”。渲染器部分需要把默认的文本渲染器换成 JCheckBox 与文本的组合核心是把 TreeCellRenderer 的 getTreeCellRendererComponent 返回一个带勾选框的面板。参数上注意树节点数据加载时用 t_category 表的 parent_id 递归构建一级节点的 parent_id 为 0。勾选状态一般不用持久化到数据库——它是查询条件不是业务数据关掉软件重新打开后清空是正常行为。3.2 物资资料维护TableModel封装与增删改查物资资料的增删改查是整个系统最枯燥也最核心的部分。这里建议抛掉 DefaultTableModel自己继承 AbstractTableModel把对表的修改操作收敛到一个类里界面代码会干净很多。public class MaterialTableModel extends AbstractTableModel { private String[] columns {编码, 名称, 分类, 规格, 单位, 库存, 预警线}; private ListMaterial list new ArrayList(); Override public int getRowCount() { return list.size(); } Override public int getColumnCount() { return columns.length; } Override public Object getValueAt(int rowIndex, int columnIndex) { Material m list.get(rowIndex); switch (columnIndex) { case 0: return m.getCode(); case 1: return m.getName(); case 2: return m.getCategoryName(); case 3: return m.getSpec(); case 4: return m.getUnit(); case 5: return m.getStock(); case 6: return m.getSafetyStock(); default: return null; } } Override public String getColumnName(int column) { return columns[column]; } public void setData(ListMaterial data) { this.list data; fireTableDataChanged(); } public Material getRow(int row) { return list.get(row); } }逻辑说明这个模型把表格列和 Material 实体类字段做了映射。删除操作先 getRow 拿到当前选中行对象再拿它的 id 执行 DELETE SQL最后调用 setData 刷新。编辑操作建议单独弹一个 JDialog 表单不要在表格里直接改值——物资编码和名称这类字段在表格里直接编辑时用户很容易误触而且校验逻辑写起来更麻烦。参数说明getColumnName 必须重写否则表头显示“A、B、C”而不是中文。fireTableDataChanged 是通知 JTable 重绘的关键漏掉它表格不会刷新。名归类字段需要连表查询拿分类名称SQL 用 LEFT JOIN t_category而不是单纯存一个 categoryId 显示数字——这是很多初稿被老师批“不友好”的重灾区。3.3 入库与出库事务、行锁与流水表一次写完入库和出库是本系统的核心操作也是最容易出数据不一致的地方。常见错误写法是先 UPDATE 库存再 INSERT 流水两步中间没加事务。一旦第二步抛异常库存变了流水没记账目就对不上。正确做法是把两步包在一个事务里同时对库存行加锁。public void inStock(int materialId, int quantity, String operator) { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 1. 锁定库存行防止并发重复入库 String sqlLock SELECT stock FROM t_material WHERE id ? FOR UPDATE; try (PreparedStatement ps conn.prepareStatement(sqlLock)) { ps.setInt(1, materialId); ps.executeQuery(); } // 2. 更新库存 String sqlUpdate UPDATE t_material SET stock stock ? WHERE id ?; try (PreparedStatement ps conn.prepareStatement(sqlUpdate)) { ps.setInt(1, quantity); ps.setInt(2, materialId); ps.executeUpdate(); } // 3. 写入流水 String sqlInsert INSERT INTO t_stock_io(material_id, io_type, quantity, operator) VALUES(?, 1, ?, ?); try (PreparedStatement ps conn.prepareStatement(sqlInsert)) { ps.setInt(1, materialId); ps.setInt(2, quantity); ps.setString(3, operator); ps.executeUpdate(); } conn.commit(); } catch (Exception e) { if (conn ! null) { try { conn.rollback(); } catch (Exception ex) { ex.printStackTrace(); } } throw new RuntimeException(入库失败: e.getMessage(), e); } finally { if (conn ! null) { try { conn.close(); } catch (Exception e) { } } } }逻辑说明SELECT ... FOR UPDATE 是悲观锁在事务提交前锁住 t_material 表对应行。出库操作的代码和入库几乎一样只是 UPDATE 时改成 stock - quantity并在执行前校验一下当前库存是否足够。这里用事务把所有 SQL 包起来任何一步失败都会整体回滚库存和流水永远保持一致。参数说明conn.setAutoCommit(false) 之后commit 和 rollback 必须成对出现。finally 块里关闭连接时要小心——connection.close() 会自动回滚未提交的事务所以在关闭前一定先走完 commit 或 rollback。operator 参数是当前登录用户的 real_name这为实现“谁领的、谁入的账”提供审计线索。3.4 库存查询与低库存预警GROUP BY、HAVING 与排序的实战库存查询页面通常要支持按名称模糊搜索、按分类过滤、按库存量排序并高亮显示低于预警线的物资。这节的 SQL 是整系统里最需要思考的部分尤其是排序和聚合的组合使用。SELECT m.id, m.code, m.name, m.spec, m.unit, m.stock, m.safety_stock, c.name AS category_name FROM t_material m LEFT JOIN t_category c ON m.category_id c.id WHERE m.name LIKE CONCAT(%, ?, %) AND (? 0 OR m.category_id ?) ORDER BY m.stock ASC;逻辑说明排序用 ORDER BY m.stock ASC把库存最少的物资排在前面配合 JTable 行渲染器把 stock 小于 safety_stock 的行背景标红低库存预警就完成了。参数里 ? 0 OR m.category_id ? 这个写法是 MyBatis 动态 SQL 的半成品替代方案——当分类参数传 0 时条件自动失效查询全部分类。参数说明LIKE 拼接用 CONCAT 而不是直接塞参数可以防止 SQL 注入。ORDER BY 方向建议用户点击表头切换这需要给 JTable 加 TableRowSorterSwing 自带的排序器足够应付单列排序。如果要做“按月末库存排序”就需要在列表页拿子查询结果这里不做展开。4. 把报表讲清楚SQL统计 图表的落地写法4.1 月度出入库统计三条SQL解决老板问题物资系统做到最后使用者最关心的是“这个月入了什么、出了什么、还剩什么”。SQL 统计报表不需要引入任何报表引擎三条带 DATE_FORMAT 的聚合查询足够覆盖绝大多数需求。下面这条统计每个月入库总量的 SQL 是最常用的。SELECT DATE_FORMAT(create_time, %Y-%m) AS month, SUM(CASE WHEN io_type 1 THEN quantity ELSE 0 END) AS total_in, SUM(CASE WHEN io_type 2 THEN quantity ELSE 0 END) AS total_out FROM t_stock_io WHERE create_time DATE_SUB(CURDATE(), INTERVAL 6 MONTH) GROUP BY DATE_FORMAT(create_time, %Y-%m) ORDER BY month DESC;逻辑说明CASE WHEN 把入库和出库数量摊到两个列上一行就是一个月的出入汇总。DATE_FORMAT 在 GROUP BY 里直接对格式化后的字符串分组这是 MySQL 支持的做法。结果集三列数据直接灌进 JTable 的 TableModel一个统计面板就完成了。参数说明WHERE 条件限定了最近 6 个月避免统计表无限膨胀。ORDER BY month DESC 是让最新月份显示在最上面对应热词里的 mysql 排序需求。DATE_SUB(CURDATE(), INTERVAL 6 MONTH) 是 MySQL 的日期计算函数跨年场景会自动进位不用自己拼月份字符串。4.2 用JFreeChart把月度数据画成柱状图坐标轴与数据集的配置表格式报表终究不够直观把月度出入库画成柱状图是“高分”项目的加分项。Swing 生态里用 JFreeChart 最顺手引入 jfreechart jar 包后核心是把上面的查询结果构造成数据集。// 构造柱状图数据集 DefaultCategoryDataset dataset new DefaultCategoryDataset(); while (rs.next()) { String month rs.getString(month); int totalIn rs.getInt(total_in); int totalOut rs.getInt(total_out); dataset.addValue(totalIn, 入库, month); dataset.addValue(totalOut, 出库, month); } JFreeChart chart ChartFactory.createBarChart( 近6个月出入库统计, // 主标题 月份, // X轴标签 数量, // Y轴标签 dataset, PlotOrientation.VERTICAL, true, // 显示图例 true, // 生成工具提示 false // 不生成URL ); // 将图表面板嵌入 Swing 窗体 ChartPanel chartPanel new ChartPanel(chart); chartPanel.setPreferredSize(new Dimension(600, 300));逻辑说明DefaultCategoryDataset 的 addValue 三个参数分别是数值、系列名、分类名JFreeChart 用系列名生成图例里的柱状颜色区分。ChartFactory.createBarChart 返回 JFreeChart 对象ChartPanel 是 Swing 组件直接 add 到 JPanel 即可。如果不想引入第三方 jar也可以用 JPanel 的 paintComponent 自绘柱状图但坐标轴刻度、抗锯齿这些都要自己处理投入产出比太低。参数说明PlotOrientation.VERTICAL 决定柱子是竖直还是水平。createBarChart 最后一个参数传 false会跳过一个 URL 生成器这个功能在桌面端完全没有意义。图表标题不要写死“统计报表”用当前年份拼接更专业。4.3 导出CSV报表用 UTF-8 BOM 解决 Excel 中文乱码报表做出来后总有人要求“导出来发给我”。最轻量可靠的方案是导出 CSV但有个坑直接用 FileWriter 写 UTF-8 编码的 CSV用 Excel 打开时中文会乱码。原因在于 Excel 需要文件头有 BOM 标记才能正确识别 UTF-8解决方法是写文件前先写入 BOM 字节。try (FileOutputStream fos new FileOutputStream(库存报表.csv); OutputStreamWriter osw new OutputStreamWriter(fos, StandardCharsets.UTF_8)) { // 写入 UTF-8 BOM否则 Excel 打开中文乱码 fos.write(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF}); osw.write(物资编码,物资名称,库存,预警线\n); for (Material m : list) { osw.write(m.getCode() , m.getName() , m.getStock() , m.getSafetyStock() \n); } } catch (IOException e) { JOptionPane.showMessageDialog(this, 导出失败: e.getMessage()); }逻辑说明注意必须先写 BOM 再写入内容顺序反了 Excel 依然乱码。CSV 转义规则很简单字段含逗号时用双引号包裹字段含双引号时把双引号换成两个双引号这套基本够用。导出按钮触发时用 SwingWorker 包一层因为文件写入在数据量大时还是有点延迟的。参数说明文件名用“库存报表.csv”是硬编码实际开发中拼接当前日期更合理比如 String.format(库存报表_%s.csv, LocalDate.now())。标准库的 OutputStreamWriter 负责字符编码转换不要用 FileWriter——它只按平台默认编码写无法强制 UTF-8。5. 物资信息管理系统避坑指南五个我亲手踩过的坑5.1 MySQL 8.0 连接报 SSL 连接错误程序时好时坏现象连接代码第一次运行正常第二次启动直接抛”SSL connection error”或者提示”Public Key Retrieval is not allowed”。 原因MySQL 8.0 默认开启 SSL 与 caching_sha2_password 认证JDBC 驱动在第一次握手时需要获取 RSA 公钥但配置里没放开权限。 解决在连接 URL 尾部加上 useSSLfalse 和 allowPublicKeyRetrievaltrue完整写法是 jdbc:mysql://localhost:3306/material_db?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue。这个坑在 2.3 的配置里出现过一次但很多同学改完 URL 忘了重启 MySQL 服务报错照旧。5.2 驱动类名加载失败NoClassDefFoundError现象程序编译通过运行到 Class.forName 那行抛 java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver。 原因mysql-connector-java 5.x 版本的驱动类是 com.mysql.jdbc.Driver8.x 版本改成了 com.mysql.cj.jdbc.Driver。如果拿 5.x 驱动去跑 8.x 的 MySQL或者反过来都会加载失败。 解决统一使用 MySQL Connector/J 8.0.33驱动类名固定写 com.mysql.cj.jdbc.Driver。另外确认 jar 包真的在构建路径里——IDEA 里双击 lib 目录下的 jar看模块依赖是否勾选这是个非常隐蔽的翻车点。5.3 Swing 界面卡成白板数据库操作把事件线程堵死了现象点击“查询”按钮后整个窗体无法拖动鼠标变成转圈状态几秒到几十秒后才恢复。 原因JTable 的数据加载、SQL 查询这些耗时操作直接写在了按钮的事件回调里也就是 EDT事件分发线程上。EDT 被堵住界面重绘、鼠标响应全部排队看起来就像死机。 解决耗时操作丢进 SwingWorker。doInBackground 里执行 JDBC 查询done 方法里更新 JTable。习惯是任何查询按钮的事件处理器都只做一件事new LoadDataTask().execute()不做任何数据库操作。5.4 出入库数量对不上账事务忘写了或者SELECT之后忘了锁现象入库流程测试时单次操作正常连续快速录入多笔时库存数字和流水累计对不上偶尔还会出现库存变成负数。 原因两步 SQL 之间没有事务包裹UPDATE 成功、INSERT 失败时数据就丢了更隐蔽的是并发场景下两个窗口同时入库两个事务都读到旧库存后提交的覆盖先提交的。 解决按 3.3 的模板完整事务 SELECT ... FOR UPDATE 行锁。自己测试时开两个窗口同时提交确认不会互相覆盖。这里没有玄学只有事务边界和锁的范围。5.5 中文乱码与打包后连不上库编码和配置文件路径现象自己电脑运行正常打包成 jar 发给同学他打开后要么数据库中文全是问号要么直接报空指针找不到 db.properties。 原因打包时 db.properties 没有被打进 jar 的 classpath或者编译时源文件编码不是 UTF-8。中文乱码通常是因为 JVM 默认字符集与文件编码不一致。 解决IDEA 里把云编码统一设置为 UTF-8包括源文件、properties 文件和控制台。db.properties 放 src/main/resources 目录打包确认 jar 里能看到它。读取用 DBUtil 里的 getResourceAsStream 方式不要依赖绝对路径——别人电脑上没有你那个 D:\project 路径。最后在代码启动第一行输出一个 System.out.println(编码验证: Charset.defaultCharset())查看运行环境是否按预期用 UTF-8。6. 把“高分”再往上顶一档打包交付与文档说明里的加分细节项目代码写完只完成了一半另一半是交付形态。这份源码能跑还不算高分能让人快速跑起来才是。打包时我用 maven-shade 插件打一个 fat-jar把 MySQL 驱动 jar 合并进去这样对方不用手动建 lib 目录双击 java -jar material.jar 就启动JDK 8 环境零依赖。如果对方机器连 JDK 都没有再用 exe4j 或 jpackage 生成 exe这一步能规避掉九成环境问题。文档说明里最值得花时间的不是操作手册而是一张 ER 图——用 draw.io 画 t_user、t_category、t_material、t_stock_io 四张表的关系主键外键标清楚。第二重要的是数据流图登录后进入主界面左侧分类树约束右侧物资表格点击入库按钮弹表单、填数量、提交事务最后流水表多一条记录。把这两张图放进去再配核心代码的注释截图评分老师一眼就知道你把系统架构想明白了。最后一条个人习惯交付前我会把数据库导出成 init.sql让验收者一键重建环境而不是发给他一个已经运行过的数据文件——新环境跑不起来时这招是后悔药。回看这个项目技术难点不在复杂的架构设计而在于每一条数据流都要闭环登录要能退、入库要能查、出库要能追、报表要能导。我自己带过的团队里凡是认真把这一套做完的人后面对 JDBC 事务、Swing 事件模型、MySQL 聚合查询的理解都相当扎实。这套组合的边界也清楚适合内部小范围使用不适合高并发生产环境真要上线再考虑搬 Web 框架。希望这份拆解能帮你少踩几个坑把力气花在业务细节上。本文还有配套的精品资源点击获取
返回列表