ARTICLE DETAIL

资讯详情

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

Java Swing+MySQL教材管理系统实战:轻量可控的校园信息化方案

Java Swing+MySQL教材管理系统实战:轻量可控的校园信息化方案 简介这是一套面向Java初学者与高校课程设计学生的桌面端教材管理实战项目基于Java Swing构建图形界面、MySQL实现数据持久化解决学校教材信息登记、分配与查询等核心教务管理需求。资源包共53个文件含18个Java源码文件覆盖MVC分层结构与DAO数据访问逻辑、18个编译后class文件、2个SQL建表脚本及数据库文件、2个JAR依赖库辅以GIF操作示意与JPG界面截图整体3.39MB结构清晰便于理解Swing事件驱动机制与MySQL连接实践。已有1726人学习下载提供完整可运行工程含.project/.classpath配置开箱即用代码注释充分包含权限控制逻辑、异常处理范式及二次确认交互设计是掌握Java GUI开发、数据库集成与软件工程规范的典型教学案例。1. 为什么用 JavaSwingMySQL 做学校教材管理系统不是“过时”而是“够用且可控”去年帮本地一所中职院校重构教材管理模块时校方明确提了三条硬需求不连外网、单机或局域网部署、管理员是教务老师非IT背景、数据必须落盘可查、三年内不许换系统。我们筛掉所有带Web前端、需要Tomcat或Spring Boot的方案最后选了JavaSwingMySQL——不是因为怀旧而是它把「业务逻辑清晰可见」「数据库直连无中间层」「界面操作零学习成本」三件事同时做实了。Swing虽不炫但JTableJDialog组合能快速搭出教材入库、班级领用、库存预警、报废登记四个核心页MySQL本地安装后只需一个my.ini配置和一条CREATE DATABASE语句就能让教务员自己备份.frm/.ibd文件Java编译成jar包双击即用连JRE版本都锁死在8u291——比任何“现代化”框架都更扛住U盘拷贝、老旧电脑、断电重启这些真实场景。这不是技术选型是交付契约你要的不是“最新”是“改得动、查得着、停不了”。2. 从零搭建环境JDK 8 MySQL 5.7 Swing 工程结构落地实录2.1 JDK 8 安装与环境变量固化避坑关键在 PATH 顺序学校机房常见多版本JDK共存比如教师电脑装了JDK 17跑在线考试系统但Swing组件在JDK 11默认禁用必须回退到JDK 8。下载官方jdk-8u291-windows-x64.exe注意不是OpenJDKOracle JDK 8u291是最后一个提供Windows安装包的版本安装路径务必设为C:\Program Files\Java\jdk1.8.0_291不能含空格或中文否则后续MySQL JDBC驱动加载失败。安装后手动配置系统环境变量JAVA_HOMEC:\Program Files\Java\jdk1.8.0_291 PATH%JAVA_HOME%\bin;%PATH%提示PATH中%JAVA_HOME%\bin必须放在最前面。曾有学校因C:\Windows\System32在前导致javac调用的是系统自带的旧版工具链编译Swing时JFileChooser报NoSuchMethodError。验证命令java -version # 输出必须为java version 1.8.0_291 javac -version # 输出必须为javac 1.8.0_2912.2 MySQL 5.7.44 本地安装与权限精简配置学校服务器普遍禁用远程root登录且不允许创建新用户——这是安全基线要求。我们采用“单库单用户”策略只建textbook_db库只授权textbook_user对该库的SELECT,INSERT,UPDATE,DELETE权限禁用CREATE/ALTER/DROP防教务员误删表。安装步骤Windows 10下载mysql-5.7.44-winx64.zip解压至D:\mysql-5.7.44-winx64创建my.ini必须UTF-8无BOM[mysqld] port3306 basedirD:\\mysql-5.7.44-winx64 datadirD:\\mysql-5.7.44-winx64\\data max_connections100 character-set-serverutf8mb4 collation-serverutf8mb4_unicode_ci sql_modeSTRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION [client] port3306 default-character-setutf8mb4初始化并启动cd /d D:\mysql-5.7.44-winx64\bin mysqld --initialize-insecure --usermysql mysqld --install MySQL57 net start MySQL57登录并建库授权用mysql -u root -p初始密码为空CREATE DATABASE textbook_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER textbook_userlocalhost IDENTIFIED BY TextBook2024!; GRANT SELECT,INSERT,UPDATE,DELETE ON textbook_db.* TO textbook_userlocalhost; FLUSH PRIVILEGES;2.3 Eclipse WindowBuilder 工程初始化Swing开发效率核心Swing手工写布局极易失控必须用可视化设计器。Eclipse 2021-06兼容JDK 8 WindowBuilder插件是当前最稳组合IntelliJ IDEA对Swing Designer支持弱NetBeans已停止维护。安装步骤Eclipse官网下载eclipse-java-2021-06-R-win64.zip启动后Help → Install New Software → Add → Name: WB → Location: https://download.eclipse.org/windowbuilder/latest/选择WindowBuilder Core和Swing Designer安装重启新建Java Project→Properties → Java Build Path → Libraries → Add External JARs→ 添加mysql-connector-java-5.1.49.jar必须用5.1.x8.0.x驱动在JDK 8下会报java.lang.NoClassDefFoundError: javax/xml/bind/DatatypeConverter工程结构强制约定src/ ├── model/ // 实体类Textbook.java, BorrowRecord.java ├── dao/ // 数据访问TextbookDAO.java封装JDBC ├── service/ // 业务逻辑TextbookService.java含库存校验、领用规则 ├── ui/ // 界面MainFrame.java主窗口、AddTextbookDialog.java等 └── util/ // 工具DBUtil.java单例DataSource、SwingUtil.javaJOptionPane封装注意ui/包下每个JFrame/JDialog必须继承自JFrame或JDialog禁止直接new JFrame()写在main方法里——否则WindowBuilder无法识别设计视图。3. 核心表结构设计紧扣教材管理业务流拒绝“教科书式范式”3.1 四张表如何覆盖真实业务闭环学校教材管理不是CRUD练习它有强业务约束教材入库时要关联ISBN和出版社但同一ISBN可能有多个版次如“第3版”“第4版”班级领用必须按学期绑定且同一教材同一学期只能领一次库存预警不是简单“10”而是按年级专业动态计算如汽修专业《汽车电气》每班需配30本库存低于90本才告警报废登记需留痕谁在何时因何原因报废且报废后原库存数不可逆。因此表结构必须包含业务字段而非仅主键外键表名关键字段业务含义索引建议textbook_infoisbn VARCHAR(17),edition VARCHAR(20),publisher VARCHAR(50),stock INT DEFAULT 0,min_stock_threshold INTISBN版次唯一标识一本教材min_stock_threshold存各专业最低库存阈值如汽修:90,机电:60PRIMARY KEY(isbn, edition),INDEX(publisher)class_infograde TINYINT,major VARCHAR(20),class_name VARCHAR(30)年级1/2/3、专业汽修/机电/计算机、班级名22级汽修1班PRIMARY KEY(class_id)borrow_recordsemester VARCHAR(10),class_id INT,isbn VARCHAR(17),edition VARCHAR(20),borrow_qty INT,borrow_date DATE,operator VARCHAR(20)学期格式为2024-1operator存教务员姓名非ID便于审计UNIQUE KEY(semester, class_id, isbn, edition),INDEX(semester)scrap_recordscrap_id BIGINT AUTO_INCREMENT,isbn VARCHAR(17),edition VARCHAR(20),scrap_qty INT,scrap_reason ENUM(破损,过期,重复采购),scrap_date DATETIME,operator VARCHAR(20)scrap_reason用ENUM而非VARCHAR防止录入歧义INDEX(isbn, edition),INDEX(scrap_date)提示textbook_info.min_stock_threshold字段存JSON字符串如{汽修:90,机电:60}是折中方案——MySQL 5.7支持JSON函数但不推荐复杂查询若学校未来升级到8.0可改用JSON列虚拟列索引。3.2 初始化SQL脚本含测试数据与约束校验执行前确认已切换到textbook_db库-- 创建教材信息表 CREATE TABLE textbook_info ( isbn VARCHAR(17) NOT NULL, edition VARCHAR(20) NOT NULL, title VARCHAR(100) NOT NULL, author VARCHAR(50), publisher VARCHAR(50), price DECIMAL(8,2), stock INT DEFAULT 0, min_stock_threshold TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (isbn, edition) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 创建班级表简化版实际可关联年级表 CREATE TABLE class_info ( class_id INT PRIMARY KEY AUTO_INCREMENT, grade TINYINT NOT NULL CHECK (grade IN (1,2,3)), major VARCHAR(20) NOT NULL, class_name VARCHAR(30) NOT NULL, UNIQUE KEY uk_major_class (major, class_name) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 创建领用记录表联合唯一约束防重复领用 CREATE TABLE borrow_record ( record_id BIGINT PRIMARY KEY AUTO_INCREMENT, semester VARCHAR(10) NOT NULL COMMENT 格式2024-1, class_id INT NOT NULL, isbn VARCHAR(17) NOT NULL, edition VARCHAR(20) NOT NULL, borrow_qty INT NOT NULL CHECK (borrow_qty 0), borrow_date DATE NOT NULL DEFAULT (CURRENT_DATE), operator VARCHAR(20) NOT NULL, FOREIGN KEY (class_id) REFERENCES class_info(class_id), FOREIGN KEY (isbn, edition) REFERENCES textbook_info(isbn, edition), UNIQUE KEY uk_semester_class_isbn (semester, class_id, isbn, edition) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 插入测试数据供教务员首次登录看到真实数据 INSERT INTO textbook_info VALUES (978-7-04-050694-6, 第4版, C语言程序设计, 谭浩强, 高等教育出版社, 45.00, 120, {计算机:150}, 2024-03-01), (978-7-302-53210-2, 2023年修订版, 汽车电气设备构造与维修, 李春明, 清华大学出版社, 52.80, 85, {汽修:90}, 2024-03-05); INSERT INTO class_info (grade, major, class_name) VALUES (2, 计算机, 22级计算机1班), (2, 汽修, 22级汽修2班);4. Swing界面与JDBC交互三层解耦写法让教务员改界面不碰SQL4.1 DBUtil基于HikariCP的轻量连接池非Druid因Druid依赖JDK 8u20新APISwing应用并发低但需防连接泄漏。HikariCP 3.4.5 是JDK 8兼容的最终稳定版比原生DriverManager高效且自动回收。util/DBUtil.java核心代码public class DBUtil { private static HikariDataSource dataSource; static { HikariConfig config new HikariConfig(); config.setJdbcUrl(jdbc:mysql://localhost:3306/textbook_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8mb4); config.setUsername(textbook_user); config.setPassword(TextBook2024!); config.setDriverClassName(com.mysql.jdbc.Driver); // 注意5.1.x驱动类名 config.setMaximumPoolSize(5); // 学校最多5人同时操作够用 config.setConnectionTimeout(30000); config.setIdleTimeout(600000); config.setMaxLifetime(1800000); dataSource new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { if (rs ! null) try { rs.close(); } catch (SQLException e) {} if (ps ! null) try { ps.close(); } catch (SQLException e) {} if (conn ! null) try { conn.close(); } catch (SQLException e) {} } }参数说明maximumPoolSize5对应学校教务处最多5台电脑并发maxLifetime180000030分钟避免MySQL空闲连接超时断开useSSLfalse因学校内网无需加密开启反而报错。4.2 TextbookDAO参数化查询事务控制领用/报废必须原子性dao/TextbookDAO.java中关键方法// 领用教材扣减库存 写入领用记录必须事务 public boolean borrowTextbook(String semester, int classId, String isbn, String edition, int qty, String operator) { String sql INSERT INTO borrow_record (semester, class_id, isbn, edition, borrow_qty, operator) VALUES (?, ?, ?, ?, ?, ?); String updateSql UPDATE textbook_info SET stock stock - ? WHERE isbn ? AND edition ?; try (Connection conn DBUtil.getConnection(); PreparedStatement psInsert conn.prepareStatement(sql); PreparedStatement psUpdate conn.prepareStatement(updateSql)) { conn.setAutoCommit(false); // 开启事务 // 插入领用记录 psInsert.setString(1, semester); psInsert.setInt(2, classId); psInsert.setString(3, isbn); psInsert.setString(4, edition); psInsert.setInt(5, qty); psInsert.setString(6, operator); psInsert.executeUpdate(); // 扣减库存 psUpdate.setInt(1, qty); psUpdate.setString(2, isbn); psUpdate.setString(3, edition); int affected psUpdate.executeUpdate(); if (affected 0) { conn.rollback(); // 库存不足则回滚 return false; } conn.commit(); return true; } catch (SQLException e) { try { if (conn ! null) conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); return false; } }注意psUpdate.executeUpdate()返回值判断库存是否足够——这是比“先SELECT再UPDATE”更可靠的防超领方案避免并发时SELECT查到足够库存但UPDATE时已被他人扣减。4.3 MainFrame用JTableTableModel实现动态表格教务员最常操作的页面ui/MainFrame.java中教材列表页核心// 使用AbstractTableModel避免手写大量getRowCount/getValueAt private class TextbookTableModel extends AbstractTableModel { private ListTextbook data new ArrayList(); private final String[] columnNames {ISBN, 版次, 书名, 作者, 出版社, 单价, 库存, 预警阈值}; Override public int getRowCount() { return data.size(); } Override public int getColumnCount() { return columnNames.length; } Override public Object getValueAt(int rowIndex, int columnIndex) { Textbook t data.get(rowIndex); switch (columnIndex) { case 0: return t.getIsbn(); case 1: return t.getEdition(); case 2: return t.getTitle(); case 3: return t.getAuthor(); case 4: return t.getPublisher(); case 5: return t.getPrice(); case 6: return t.getStock(); case 7: return parseThreshold(t.getMinStockThreshold(), t.getPublisher()); // 解析JSON阈值 default: return ; } } Override public String getColumnName(int column) { return columnNames[column]; } } // 刷新表格方法教务员点击“刷新”按钮调用 private void refreshTable() { ListTextbook list textbookService.getAllTextbooks(); tableModel.setData(list); // setData方法内部notifyAll() table.repaint(); }parseThreshold()方法解析min_stock_threshold字段private String parseThreshold(String jsonStr, String publisher) { if (jsonStr null) return N/A; try { JSONObject obj new JSONObject(jsonStr); return obj.optString(publisher, 未设置); } catch (Exception e) { return 解析错误; } }这样教务员在表格里一眼看到“高等教育出版社”的教材预警值无需打开数据库查JSON字段。5. 避坑指南SwingMySQL在学校真实环境中踩过的5个血泪坑5.1 现象Swing界面在Win10高分屏上文字模糊、按钮错位原因JDK 8默认禁用DPI缩放适配而学校新采购的显示器多为1920×1080125%缩放。解决在启动jar包的bat脚本中添加JVM参数java -Dsun.java2d.uiScale1.25 -jar textbook-system.jaruiScale值需与系统设置一致125%→1.25150%→1.5。若教务员电脑缩放比例不一可在MainFrame构造方法开头加try { Toolkit.getDefaultToolkit().setDynamicLayout(true); } catch (Exception ignored) {}5.2 现象MySQL插入中文乱码日志显示??原因my.ini中character-set-serverutf8mb4生效但JDBC URL未指定characterEncodingutf8mb4且mysql-connector-java-5.1.49.jar默认用latin1。解决JDBC URL必须显式声明jdbc:mysql://localhost:3306/textbook_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8mb4注意characterEncoding参数必须小写大写CHARACTERENCODING无效useSSLfalse不可省略否则JDK 8下握手失败。5.3 现象教务员双击jar包无反应任务管理器看不到java进程原因学校电脑禁用javaw.exe安全策略或JRE未正确注册文件关联。解决提供run.bat脚本替代双击echo off java -jar %~dp0textbook-system.jar log.txt 21 if %errorlevel% neq 0 ( echo 启动失败请检查JRE是否安装 pause )脚本将日志输出到log.txt教务员遇到问题可直接发给技术支持看ClassNotFoundException或SQLException。5.4 现象库存更新后JTable不刷新需重启软件才看到新数据原因Swing事件模型中tableModel.fireTableDataChanged()未在EDTEvent Dispatch Thread中调用。解决所有UI更新必须包裹SwingUtilities.invokeLater()SwingUtilities.invokeLater(() - { tableModel.fireTableDataChanged(); statusLabel.setText(库存更新成功); });即使refreshTable()方法本身在按钮点击事件中也必须确保fireTableDataChanged()在EDT执行否则Swing渲染线程不同步。5.5 现象导出Excel时中文显示为方框字体宋体失效原因Apache POI 5.x要求JDK 11而JDK 8下Workbook.createFont()无法设置中文字体。解决降级使用POI 3.17JDK 8兼容最终版并强制设置字体Font font workbook.createFont(); font.setFontName(宋体); font.setFontHeightInPoints((short)10); CellStyle style workbook.createCellStyle(); style.setFont(font);若学校要求导出PDF推荐iText 5.5.13非7.x因其对JDK 8支持完善且中文支持稳定。6. 教务员真正需要的三个进阶技巧让系统从“能用”变成“离不开”6.1 把库存预警做成桌面弹窗非托盘图标而是全屏遮罩教务员最怕漏看库存告警。Swing原生TrayIcon在Win10上常被杀毒软件拦截我们改用JDialog全屏提示public void showStockAlert(ListTextbook lowStockList) { if (lowStockList.isEmpty()) return; JDialog alert new JDialog(this, ⚠️ 库存预警, true); alert.setUndecorated(true); // 去掉标题栏 alert.setBackground(new Color(0, 0, 0, 180)); // 半透明黑色背景 JLabel label new JLabel(htmlbody styletext-align:center h2以下教材库存低于阈值/h2 lowStockList.stream() .map(t - String.format(【%s】%s库存%d阈值%s, t.getIsbn(), t.getTitle(), t.getStock(), parseThreshold(t.getMinStockThreshold(), t.getPublisher()))) .collect(Collectors.joining(br)) brbrfont coloryellow请立即补货/font/body/html); label.setFont(new Font(微软雅黑, Font.BOLD, 16)); JButton okBtn new JButton(我知道了); okBtn.addActionListener(e - alert.dispose()); JPanel panel new JPanel(new BorderLayout()); panel.add(label, BorderLayout.CENTER); panel.add(okBtn, BorderLayout.SOUTH); alert.add(panel); alert.setSize(800, 400); alert.setLocationRelativeTo(null); alert.setVisible(true); }关键点setUndecorated(true)让弹窗无法被最小化必须点击“我知道了”才能关闭setBackground(new Color(0,0,0,180))制造视觉压迫感HTML标签支持换行和颜色比纯文本更易读。6.2 用JFileChooser定制教材封面导入支持拖拽但禁用多选教务员常需为新教材上传封面图。Swing原生JFileChooser不支持拖拽我们用TransferHandler扩展public class ImageDropHandler extends TransferHandler { private final JLabel previewLabel; public ImageDropHandler(JLabel previewLabel) { this.previewLabel previewLabel; } Override public boolean canImport(TransferSupport support) { return support.isDataFlavorSupported(DataFlavor.javaFileListFlavor); } Override public boolean importData(TransferSupport support) { try { Transferable t support.getTransferable(); SuppressWarnings(unchecked) ListFile files (ListFile) t.getTransferData(DataFlavor.javaFileListFlavor); if (files.size() ! 1) { JOptionPane.showMessageDialog(null, 只能拖入一张图片, 提示, JOptionPane.WARNING_MESSAGE); return false; } File file files.get(0); if (!isImageFile(file)) { JOptionPane.showMessageDialog(null, 仅支持JPG/PNG格式, 错误, JOptionPane.ERROR_MESSAGE); return false; } ImageIcon icon new ImageIcon(file.getAbsolutePath()); previewLabel.setIcon(new ImageIcon(icon.getImage().getScaledInstance(200, 280, Image.SCALE_SMOOTH))); return true; } catch (Exception e) { e.printStackTrace(); return false; } } private boolean isImageFile(File file) { String name file.getName().toLowerCase(); return name.endsWith(.jpg) || name.endsWith(.jpeg) || name.endsWith(.png); } }绑定到JLabelpreviewLabel.setTransferHandler(new ImageDropHandler(previewLabel));效果教务员直接把封面图拖到预览区自动缩放显示且限制只能拖一张——避免误拖文件夹导致程序卡死。6.3 生成学期报表PDF含图表但不用JFreeChart——太重学校每学期要交纸质报表。JFreeChart在JDK 8下渲染中文图表极不稳定我们改用iText 5.5.13JFreeChart精简版仅用CategoryDatasetpublic void exportSemesterReport(String semester) { Document document new Document(PageSize.A4); try { PdfWriter.getInstance(document, new FileOutputStream(教材领用报表_ semester .pdf)); document.open(); // 标题 Font titleFont FontFactory.getFont(SimSun, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); Paragraph title new Paragraph(XX学校教材领用统计报表 semester , titleFont); title.setAlignment(Element.ALIGN_CENTER); title.setFont(new Font(Font.FontFamily.UNDEFINED, 16, Font.BOLD)); document.add(title); // 表格从borrow_record统计各专业领用量 PdfPTable table new PdfPTable(3); table.setWidthPercentage(100); table.addCell(new PdfPCell(new Phrase(专业, titleFont))); table.addCell(new PdfPCell(new Phrase(领用教材数, titleFont))); table.addCell(new PdfPCell(new Phrase(总金额, titleFont))); ListObject[] stats textbookService.getSemesterStats(semester); for (Object[] row : stats) { table.addCell(new PdfPCell(new Phrase((String) row[0], titleFont))); table.addCell(new PdfPCell(new Phrase(String.valueOf(row[1]), titleFont))); table.addCell(new PdfPCell(new Phrase(¥ row[2], titleFont))); } document.add(table); // 图表占位实际用iText绘制矩形条形图代码略 document.add(new Paragraph(\n图表说明横轴为专业纵轴为领用数量, titleFont)); } catch (Exception e) { e.printStackTrace(); } finally { document.close(); } }关键FontFactory.getFont(SimSun, ...)指定宋体BaseFont.IDENTITY_H支持中文PdfPTable比JTable更适合PDF导出图表部分用iText原生Rectangle绘制避免JFreeChart依赖。我坚持用这套方案做了三年每年开学前教务员自己运行init.sql清空上学期数据新学期第一天就能用每次系统升级只替换jar包数据库不动连最抗拒新技术的老教务主任现在都会主动说“帮我把《新能源汽车》那本的预警值调到120”。技术没有高低只有“能不能让一线的人少点焦虑多点确定性”。希望帮到你。本文还有配套的精品资源点击获取
返回列表