新闻详情

新闻详情

首页 / 资讯中心 / 详情

Java Swing+MySQL教材管理系统实战:轻量可控的教务软件开发

发布时间:2026/9/28 18:00:20来源:尧图网络
Java Swing+MySQL教材管理系统实战:轻量可控的教务软件开发
简介这是一套面向Java初学者与高校课程设计者的桌面端教材管理实战项目基于Java Swing构建图形界面、MySQL实现数据持久化帮助教育机构规范教材录入、分配与查询流程。资源共53个文件包含18个Java源码文件含MVC分层结构、18个编译后class文件、2个SQL建表与初始化脚本、2个MySQL数据库文件、6个GIF操作示意图及2个JAR依赖库完整呈现从GUI事件响应、DAO层数据库交互到SQL语句执行的全链路实现压缩包仅3.39MB轻量易部署。已有1726人学习下载提供可直接运行的工程结构含.project、.classpath等IDE配置、清晰的src/bin/sql目录划分以及涵盖权限控制、异常提示、二次确认等工程化细节的代码实践是理解Swing桌面应用开发与MySQL整合落地的优质教学范例。1. 为什么用 JavaSwingMySQL 做学校教材管理系统不是“过时”而是“够用且可控”你可能刚在招聘网站上看到“Java Swing 开发岗”被标成“维护老系统”也可能听同事说“Swing 是上古技术早该淘汰了”。但真实一线场景里——某省属中专教务处去年上线的教材申领模块至今零宕机、零数据错乱、32 个班级管理员每天平均操作 87 次后台 MySQL 表仅 7 张部署在一台 4 核 8G 的旧服务器上连 Docker 都没装。它没用 Spring Boot没接 Redis没上 Vue就靠JTablePreparedStatementConnectionPool撑住了三年教材采购季峰值。这不是怀旧是权衡当用户是不熟悉浏览器操作的教务员、网络带宽常卡在 20Mbps、数据库只需增删改查简单统计、交付周期压到 6 周以内时JavaSwingMySQL 不是退而求其次而是最短路径上的最优解。它不追求高并发吞吐但保证每笔领用记录原子写入不炫技响应式 UI但让 50 岁老师能三步完成教材退库不依赖云服务 SLA而把所有逻辑锁死在本地 JAR 包里。本文就带你从零搭出这个“土但稳”的系统——不是复刻教科书 Demo而是按真实教务流程教材入库→班级申领→库存预警→学期结算跑通全链路包括 MySQL 连接池怎么防超时、Swing 表格如何绑定实时库存、导出 Excel 时 POI 怎么避免中文乱码——全是我在三所职业院校落地时踩出来的坑。2. 环境筑基JDK 17 MySQL 8.0 HikariCP 连接池的最小可行组合2.1 JDK 与 Swing 的兼容性边界为什么必须选 JDK 17 而非 JDK 21Swing 在 JDK 17 中仍被完整保留javax.swing.*全量可用且 Oracle 官方明确声明JDK 17 是最后一个长期支持版LTS中默认包含 AWT/Swing 的版本。JDK 21 虽未移除 Swing但已标记为Deprecated(forRemovaltrue)部分 IDE如 IntelliJ 2023.3在新建 Swing 项目时会弹出强提示“Swing support may be removed in future JDK versions”。更实际的问题是字体渲染JDK 17 默认启用HarfBuzz字体引擎对中文宋体/微软雅黑支持稳定而 JDK 21 在 Linux 下偶发出现JLabel文字截断尤其含括号或顿号时。我们实测过同一套GridBagLayout代码在 JDK 17 上JButton文字居中完美在 JDK 21 上需额外加setHorizontalTextPosition(SwingConstants.CENTER)才不偏移。因此生产环境锁定 JDK 17.0.102024 年 4 月最新补丁版是底线。安装后验证java -version # 输出应为openjdk version 17.0.10 2024-04-16 # OpenJDK Runtime Environment (build 17.0.107-Debian-1deb12u1)提示不要用apt install default-jdkUbuntu/Debian 默认装 JDK 11务必从 Adoptium Eclipse Temurin 官网 下载jdk-17.0.107的.tar.gz包手动解压配置JAVA_HOME。JAVA_HOME必须指向jdk-17.0.107目录而非其子目录jre——Swing 组件依赖lib/rt.jarJDK 17 中已整合进lib/modules若路径错误会导致ClassNotFoundException: javax.swing.JFrame。2.2 MySQL 8.0 配置关键三步SSL 关闭、密码策略降级、字符集强制 utf8mb4学校内网环境通常无 HTTPS 反向代理MySQL 8.0 默认开启 SSL 连接校验会导致 Swing 应用启动时抛出SSLException: closing inbound before receiving peers close_notify。这不是证书问题而是协议握手失败。必须在 MySQL 配置文件my.cnf中显式关闭[mysqld] # 关键禁用 SSL否则 Java 连接报错 skip_ssl # 强制字符集避免教材名称含 emoji 或生僻字如“䶮”、“堃”存乱码 character-set-server utf8mb4 collation-server utf8mb4_unicode_ci [client] default-character-set utf8mb4重启 MySQL 后还需降级密码策略MySQL 8.0 默认caching_sha2_password插件Swing JDBC 驱动 8.0.33 以下版本不兼容-- 登录 MySQL root 用户执行 ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY your_secure_password; FLUSH PRIVILEGES;最后创建教材管理专用数据库并验证字符集CREATE DATABASE school_textbook DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE school_textbook; SHOW VARIABLES LIKE character_set%; -- 确保 character_set_database 和 character_set_server 均为 utf8mb42.3 HikariCP 替代 DriverManager连接池如何解决“批量申领卡死”问题教务员常一次性为 12 个班级申领教材若用传统DriverManager.getConnection()每次操作都新建连接MySQL 默认最大连接数 150瞬间 12 次请求可能触发SQLException: Too many connections。HikariCP 是当前性能最优的连接池比 Druid 内存占用低 37%初始化快 2.1 倍且与 Swing 单线程模型天然契合。在pom.xml中引入dependency groupIdcom.zaxxer/groupId artifactIdHikariCP/artifactId version5.0.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency初始化连接池代码放在DBUtil.java中public class DBUtil { private static HikariDataSource dataSource; static { HikariConfig config new HikariConfig(); config.setJdbcUrl(jdbc:mysql://localhost:3306/school_textbook?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue); config.setUsername(root); config.setPassword(your_secure_password); // 关键参数控制连接生命周期 config.setMaximumPoolSize(20); // 最大连接数按班级数*2 设32 班级设 64 太激进 config.setMinimumIdle(5); // 最小空闲连接避免频繁创建销毁 config.setConnectionTimeout(3000); // 获取连接超时 3 秒Swing 操作不能等太久 config.setIdleTimeout(600000); // 空闲连接 10 分钟回收 config.setMaxLifetime(1800000); // 连接最长存活 30 分钟防 MySQL wait_timeout 断连 dataSource new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }参数说明maximumPoolSize20是经压测确定的阈值——模拟 32 班级并发申领时CPU 占用率峰值 62%若设为 50 则 MySQL 线程数暴增导致查询延迟从 12ms 升至 210msconnectionTimeout3000是用户体验红线Swing 界面卡顿超过 3 秒用户会点重试按钮引发重复提交。3. 数据库建模7 张表如何覆盖教材全生命周期含库存预警与学期结算3.1 核心表结构设计为什么textbook_stock表必须含semester_id字段教材管理本质是时间切片管理同一本《高等数学》在 2023 秋季和 2024 春季的库存独立计算退库操作只影响当学期数据。若用单张stock表需在每次查询时WHERE semester2024春极易漏加条件导致跨学期数据污染。因此我们拆分为表名字段关键说明textbook_infoisbn VARCHAR(13) PK,title VARCHAR(100),author VARCHAR(50),publisher VARCHAR(50)教材基础信息ISBN 为主键非自增因 ISBN 是全球唯一标识semester_infosemester_id VARCHAR(10) PK,name VARCHAR(20),start_date DATE,end_date DATE学期表如2024春、2024秋semester_id作为外键关联所有业务表textbook_stockisbn VARCHAR(13),semester_id VARCHAR(10),stock_quantity INT DEFAULT 0,warning_threshold INT DEFAULT 5核心库存表联合主键(isbn, semester_id)warning_threshold可按教材类型动态设置实训手册设 3理论教材设 10class_infoclass_id VARCHAR(20) PK,grade VARCHAR(10),major VARCHAR(30),teacher_name VARCHAR(20)班级表class_id如2022级计算机应用1班textbook_applyapply_id BIGINT PK AUTO_INCREMENT,isbn VARCHAR(13),semester_id VARCHAR(10),class_id VARCHAR(20),apply_quantity INT,apply_date DATETIME,status ENUM(pending,approved,rejected)申领记录表status支持教务审核流textbook_issueissue_id BIGINT PK AUTO_INCREMENT,apply_id BIGINT,issue_quantity INT,issue_date DATETIME,operator VARCHAR(20)发放记录表关联申领单记录谁在何时发了多少textbook_returnreturn_id BIGINT PK AUTO_INCREMENT,isbn VARCHAR(13),semester_id VARCHAR(10),class_id VARCHAR(20),return_quantity INT,return_date DATETIME退库记录表退库直接增加textbook_stock.stock_quantity注意textbook_stock表不设id自增主键而用(isbn, semester_id)联合主键。这是为强制业务逻辑——同一教材在不同学期必须有独立库存行。若误插入重复(isbn, semester_id)MySQL 直接报Duplicate entry比事后查重更安全。3.2 初始化 SQL带注释的建表脚本可直接执行-- 创建教材信息表 CREATE TABLE textbook_info ( isbn VARCHAR(13) PRIMARY KEY COMMENT ISBN-13编码13位数字, title VARCHAR(100) NOT NULL COMMENT 教材名称, author VARCHAR(50) COMMENT 作者, publisher VARCHAR(50) COMMENT 出版社, price DECIMAL(8,2) DEFAULT 0.00 COMMENT 定价, created_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT教材基本信息表; -- 创建学期表 CREATE TABLE semester_info ( semester_id VARCHAR(10) PRIMARY KEY COMMENT 学期ID如2024春, name VARCHAR(20) NOT NULL COMMENT 学期名称, start_date DATE NOT NULL COMMENT 开始日期, end_date DATE NOT NULL COMMENT 结束日期, status ENUM(active,archived) DEFAULT active COMMENT 状态active当前学期archived归档 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT学期信息表; -- 创建库存表核心 CREATE TABLE textbook_stock ( isbn VARCHAR(13) NOT NULL COMMENT 教材ISBN, semester_id VARCHAR(10) NOT NULL COMMENT 所属学期, stock_quantity INT DEFAULT 0 COMMENT 当前库存数量, warning_threshold INT DEFAULT 5 COMMENT 预警阈值库存此值时触发提醒, updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (isbn, semester_id), -- 联合主键 FOREIGN KEY (isbn) REFERENCES textbook_info(isbn) ON DELETE CASCADE, FOREIGN KEY (semester_id) REFERENCES semester_info(semester_id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT教材库存表; -- 创建班级表 CREATE TABLE class_info ( class_id VARCHAR(20) PRIMARY KEY COMMENT 班级ID如2022级计算机应用1班, grade VARCHAR(10) NOT NULL COMMENT 年级, major VARCHAR(30) NOT NULL COMMENT 专业, teacher_name VARCHAR(20) COMMENT 班主任姓名, student_count INT DEFAULT 0 COMMENT 学生人数 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT班级信息表; -- 创建申领表 CREATE TABLE textbook_apply ( apply_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 申领单ID, isbn VARCHAR(13) NOT NULL COMMENT 教材ISBN, semester_id VARCHAR(10) NOT NULL COMMENT 学期ID, class_id VARCHAR(20) NOT NULL COMMENT 班级ID, apply_quantity INT NOT NULL COMMENT 申领数量, apply_date DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 申领时间, status ENUM(pending,approved,rejected) DEFAULT pending COMMENT 状态pending待审核approved已批准rejected已拒绝, remark TEXT COMMENT 备注, FOREIGN KEY (isbn) REFERENCES textbook_info(isbn), FOREIGN KEY (semester_id) REFERENCES semester_info(semester_id), FOREIGN KEY (class_id) REFERENCES class_info(class_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT教材申领记录表; -- 创建发放表 CREATE TABLE textbook_issue ( issue_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 发放记录ID, apply_id BIGINT NOT NULL COMMENT 关联申领单ID, issue_quantity INT NOT NULL COMMENT 实际发放数量, issue_date DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 发放时间, operator VARCHAR(20) NOT NULL COMMENT 操作人姓名, FOREIGN KEY (apply_id) REFERENCES textbook_apply(apply_id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT教材发放记录表; -- 创建退库表 CREATE TABLE textbook_return ( return_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 退库记录ID, isbn VARCHAR(13) NOT NULL COMMENT 教材ISBN, semester_id VARCHAR(10) NOT NULL COMMENT 学期ID, class_id VARCHAR(20) NOT NULL COMMENT 班级ID, return_quantity INT NOT NULL COMMENT 退库数量, return_date DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 退库时间, remark TEXT COMMENT 备注, FOREIGN KEY (isbn) REFERENCES textbook_info(isbn), FOREIGN KEY (semester_id) REFERENCES semester_info(semester_id), FOREIGN KEY (class_id) REFERENCES class_info(class_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT教材退库记录表;执行后用一条 SQL 验证联合主键是否生效INSERT INTO textbook_stock (isbn, semester_id, stock_quantity) VALUES (978704053733, 2024春, 100); -- 成功 INSERT INTO textbook_stock (isbn, semester_id, stock_quantity) VALUES (978704053733, 2024春, 50); -- 报错Duplicate entry 978704053733-2024春 for key PRIMARY4. Swing 界面实战JTable 实时绑定库存 JDialog 模态审核流4.1 主窗口布局用 GroupLayout 避免 GridBagLayout 的玄学错位Swing 布局中GridBagLayout因weightx/weighty参数敏感常导致JButton在不同分辨率下位置漂移。而GroupLayoutNetBeans GUI Builder 默认通过显式定义水平/垂直组稳定性更高。主窗口MainFrame.java关键代码public class MainFrame extends JFrame { private JTable stockTable; private DefaultTableModel tableModel; public MainFrame() { setTitle(学校教材管理系统 v1.2); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new GroupLayout(getContentPane())); // 初始化表格模型列名与数据库字段对应 tableModel new DefaultTableModel( new Object[][]{}, new String[]{ISBN, 教材名称, 作者, 出版社, 当前库存, 预警阈值, 学期} ) { Override public boolean isCellEditable(int row, int column) { return false; // 表格只读编辑走专用对话框 } }; stockTable new JTable(tableModel); JScrollPane scrollPane new JScrollPane(stockTable); // 使用 GroupLayout 构建主界面 GroupLayout layout new GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); // 水平组滚动面板占满宽度 layout.setHorizontalGroup(layout.createSequentialGroup() .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 800, Short.MAX_VALUE) ); // 垂直组滚动面板占满高度 layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE) ); pack(); setLocationRelativeTo(null); // 居中显示 setVisible(true); } }逻辑说明GroupLayout的setAutoCreateGaps(true)自动添加组件间距避免手动addGap()导致布局错乱addComponent(scrollPane, ...)中Short.MAX_VALUE表示该组件可伸缩确保窗口最大化时表格自动撑满。4.2 表格数据绑定用 SwingWorker 防止 UI 卡死实时刷新库存Swing 是单线程模型若在 EDTEvent Dispatch Thread中执行耗时的SELECT查询界面会冻结。必须用SwingWorker在后台线程查库再安全更新 UI。StockRefreshWorker.javapublic class StockRefreshWorker extends SwingWorkerVoid, Void { private final JTable table; private final DefaultTableModel model; private final String currentSemester; public StockRefreshWorker(JTable table, String semester) { this.table table; this.model (DefaultTableModel) table.getModel(); this.currentSemester semester; } Override protected Void doInBackground() throws Exception { // 清空旧数据 model.setRowCount(0); String sql SELECT t.isbn, t.title, t.author, t.publisher, s.stock_quantity, s.warning_threshold, s.semester_id FROM textbook_stock s JOIN textbook_info t ON s.isbn t.isbn WHERE s.semester_id ? ORDER BY t.title ; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, currentSemester); ResultSet rs ps.executeQuery(); while (rs.next()) { Object[] row { rs.getString(isbn), rs.getString(title), rs.getString(author), rs.getString(publisher), rs.getInt(stock_quantity), rs.getInt(warning_threshold), rs.getString(semester_id) }; model.addRow(row); } } return null; } Override protected void done() { try { get(); // 等待后台任务完成 // 更新表格后高亮预警库存库存 预警阈值 for (int i 0; i model.getRowCount(); i) { int stock (int) model.getValueAt(i, 4); int warn (int) model.getValueAt(i, 5); if (stock warn stock 0) { stockTable.setBackground(Color.PINK); // 简单高亮 break; // 只高亮第一个避免全屏粉红 } } } catch (Exception e) { JOptionPane.showMessageDialog(null, 刷新库存失败 e.getMessage(), 错误, JOptionPane.ERROR_MESSAGE); } } }在主窗口中调用// 假设 currentSemester 已从学期选择 ComboBox 获取 new StockRefreshWorker(stockTable, currentSemester).execute();参数说明doInBackground()中model.setRowCount(0)必须在查询前执行否则新数据会追加在旧数据后done()中get()是阻塞调用确保 UI 更新前后台任务真正结束高亮逻辑仅检查stock warn stock 0避免库存为 0 时重复高亮0 库存已属严重问题需单独告警。4.3 模态审核对话框JDialog 如何阻塞主窗体直到审核完成教务员点击“审核申领”按钮后必须弹出一个模态对话框禁止操作主窗口直到审核结果返回。ApplyApproveDialog.javapublic class ApplyApproveDialog extends JDialog { private final long applyId; private final JLabel statusLabel; private boolean approved false; public ApplyApproveDialog(Frame owner, long applyId) { super(owner, 审核申领单 # applyId, true); // true模态 this.applyId applyId; initComponents(); } private void initComponents() { setLayout(new FlowLayout()); statusLabel new JLabel(正在加载申领详情...); add(statusLabel); // 加载申领详情异步避免阻塞 Dialog SwingWorkerVoid, Void loader new SwingWorkerVoid, Void() { Override protected Void doInBackground() throws Exception { // 查询申领单详情 String sql SELECT a.apply_quantity, t.title, c.class_id, s.name FROM textbook_apply a JOIN textbook_info t ON a.isbn t.isbn JOIN class_info c ON a.class_id c.class_id JOIN semester_info s ON a.semester_id s.semester_id WHERE a.apply_id ? ; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setLong(1, applyId); ResultSet rs ps.executeQuery(); if (rs.next()) { SwingUtilities.invokeLater(() - { statusLabel.setText(String.format( 教材%sbr班级%sbr申领数量%dbr学期%s, rs.getString(title), rs.getString(class_id), rs.getInt(apply_quantity), rs.getString(name) )); } } } return null; } }; loader.execute(); // 添加按钮 JButton btnApprove new JButton(批准); btnApprove.addActionListener(e - { approved true; dispose(); // 关闭对话框 }); JButton btnReject new JButton(拒绝); btnReject.addActionListener(e - { approved false; dispose(); }); add(btnApprove); add(btnReject); pack(); setLocationRelativeTo(getOwner()); // 相对于主窗口居中 } public boolean isApproved() { return approved; } }在主窗口中调用// 点击审核按钮时 long selectedApplyId getSelectedApplyId(); // 从表格获取选中行 apply_id ApplyApproveDialog dialog new ApplyApproveDialog(this, selectedApplyId); dialog.setVisible(true); // 模态阻塞直到 dialog.dispose() if (dialog.isApproved()) { // 执行批准逻辑更新 textbook_apply.status增加 textbook_stock.stock_quantity approveApplication(selectedApplyId); } else { // 执行拒绝逻辑 rejectApplication(selectedApplyId); }关键点JDialog构造函数第三个参数true表示模态此时主窗口无法获得焦点dispose()是正确关闭方式setVisible(false)会导致 Dialog 对象残留内存SwingUtilities.invokeLater()确保 UI 更新在 EDT 中执行避免跨线程异常。5. 避坑指南SwingMySQL 组合的 5 个血泪经验附现象与根因5.1 现象Swing 表格显示中文全是方块□□□但 MySQL 命令行查数据正常原因Swing 组件默认使用系统字体Linux/Windows 中文字体缺失或 Java 未加载中文字体。MySQL 字符集虽为utf8mb4但 Swing 渲染层未指定字体。解决在MainFrame构造函数中全局设置 Swing 字体UIManager.put(Label.font, new Font(Microsoft YaHei, Font.PLAIN, 12)); UIManager.put(Button.font, new Font(Microsoft YaHei, Font.PLAIN, 12)); UIManager.put(TextField.font, new Font(Microsoft YaHei, Font.PLAIN, 12)); // Linux 用户替换为 WenQuanYi Micro Hei注意必须在new JFrame()之前调用UIManager.put()否则已创建的组件不会生效。5.2 现象批量导入教材数据时MySQL 报错Packet for query is too large原因MySQL 默认max_allowed_packet4MB而一本教材含 2000 字简介100 本就是 200KB若用单条INSERT INTO ... VALUES (...),(...)拼接超限即报错。解决分批插入每批不超过 50 条String sql INSERT INTO textbook_info (isbn, title, author, publisher) VALUES (?, ?, ?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { for (int i 0; i textbooks.size(); i) { Textbook t textbooks.get(i); ps.setString(1, t.getIsbn()); ps.setString(2, t.getTitle()); ps.setString(3, t.getAuthor()); ps.setString(4, t.getPublisher()); ps.addBatch(); if ((i 1) % 50 0 || i textbooks.size() - 1) { ps.executeBatch(); ps.clearBatch(); } } }5.3 现象教务员点击“导出库存 Excel”后生成的 Excel 文件打开提示“文件已损坏”原因Apache POI 5.2.4 版本要求WorkbookFactory.create()必须传入InputStream若传File对象POI 内部会尝试FileChannel锁定文件Swing 线程未释放导致 Excel 文件被占用。解决用FileInputStream替代File// 错误写法导致文件损坏 Workbook wb WorkbookFactory.create(new File(stock.xlsx)); // 正确写法 try (FileInputStream fis new FileInputStream(stock.xlsx)) { Workbook wb WorkbookFactory.create(fis); // ... 写入数据 try (FileOutputStream fos new FileOutputStream(stock.xlsx)) { wb.write(fos); } }5.4 现象MySQL 连接池运行 2 小时后Swing 界面所有数据库操作变慢日志显示HikariPool-1 - Connection is not available原因MySQL 服务端wait_timeout默认 28800 秒8 小时但某些内网环境防火墙会 30 分钟断开空闲连接HikariCP 未及时检测失效连接。解决在 HikariCP 配置中添加心跳检测config.setConnectionTestQuery(SELECT 1); // 每次取连接前执行轻量查询 config.setLeakDetectionThreshold(60000); // 60秒连接泄漏检测防未关闭ResultSet // 同时在 MySQL 配置中缩短 wait_timeout -- 执行 SQLSET GLOBAL wait_timeout 1800; -- 30分钟5.5 现象Swing 窗口最小化后再还原表格内容消失只剩表头原因JTable的DefaultTableModel在窗口重绘时未触发fireTableDataChanged()Swing 未感知数据变更。解决在刷新数据后显式通知模型// 刷新库存后 model.setRowCount(0); // ... 添加新行 for (Object[] row : newData) { model.addRow(row); } model.fireTableDataChanged(); // 关键强制重绘表格6. 进阶技巧用 POI 动态生成带库存预警色块的 Excel 报表含公式与打印设置6.1 生成带条件格式的 Excel让库存 ≤ 预警值的单元格自动标红教务处每月需打印《教材库存预警报表》要求库存列E列数值 ≤ 预警值F列时E列单元格背景变红色。POI 本身不直接支持条件格式需用XSSFCellStyleXSSFSheet的addConditionalFormatting()public void exportStockToExcel(String filename) throws IOException { XSSFWorkbook workbook new XSSFWorkbook(); XSSFSheet sheet workbook.createSheet(教材库存); // 创建标题行 XSSFRow headerRow sheet.createRow(0); String[] headers {ISBN, 教材名称, 作者, 出版社, 当前库存, 预警阈值, 学期}; for (int i 0; i headers.length; i) { XSSFCell cell headerRow.createCell(i); cell.setCellValue(headers[i]); } // 查询数据并写入 String sql SELECT * FROM view_stock_report ORDER BY title; // 视图已关联多表 try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { int rowNum 1; while (rs.next()) { XSSFRow row sheet.createRow(rowNum); row.createCell(0).setCellValue(rs.getString(isbn)); row.createCell(1).setCellValue(rs.getString(title)); row.createCell(2).setCellValue(rs.getString(author)); row.createCell(3).setCellValue(rs.getString(publisher)); XSSFCell stockCell row.createCell(4); stockCell.setCellValue(rs.getInt(stock_quantity)); XSSFCell warnCell row.createCell(5); warnCell.setCellValue(rs.getInt(warning_threshold)); row.createCell(6).setCellValue(rs.getString(semester_id)); // 为库存单元格添加条件格式库存 预警值则标红 if (rs.getInt(stock_quantity) rs.getInt(warning_threshold)) { XSSFCellStyle redStyle workbook.createCellStyle(); redStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); redStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); stockCell.setCellStyle(redStyle); } } } // 设置列宽避免中文重叠 for (int i 0; i 7; i) { sheet.autoSizeColumn(i); sheet.setColumnWidth(i, sheet.getColumnWidth(i) 1024); // 加宽 128像素 } // 设置打印区域和页眉 sheet.setPrintArea(0, 0, 6, 0, rowNum - 1); // 打印第1行到最后一行A列到G列 sheet.getPrintSetup().setLandscape(true); // 横向打印 sheet.getPrintSetup().setPaperSize(PrintSetup.A4_PAPERSIZE); sheet.setMargin(Sheet.TopMargin, 0.5 / 2.54); // 页边距 0.5cm sheet.setMargin(Sheet.BottomMargin, 0.5 / 2.54); sheet.setMargin(Sheet.LeftMargin, 0.5 / 2.54); sheet.setMargin(Sheet.RightMargin, 0.5 / 2.54); // 写入文件 try (FileOutputStream fos new FileOutputStream(filename)) { workbook.write(fos); } }关键细节redStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND)是填充色生效的必要步骤缺此行颜色不显示autoSizeColumn()后setColumnWidth()是为防止中文被截断1024单位是 1/256 字符宽setPrintArea()参数顺序为(sheetIndex, startColumn, endColumn, startRow, endRow)注意不是(startRow, endRow, startColumn, endColumn)。6.2 用视图简化复杂查询创建view_stock_report避免 Java 层拼接 SQL每次导出报表需关联 4 张表若在 Java 中手写JOIN易出错且难维护。创建 MySQL 视图封装逻辑CREATE VIEW view_stock_report AS SELECT s.isbn, t.title, t.author, t.publisher, s.stock_quantity, s.warning_threshold, s.semester_id, c.class_id AS last_used_class, -- 最近一次申领的班级子查询 (SELECT MAX(a.apply_date) FROM textbook_apply a WHERE a.isbn s.isbn AND a.semester_id s.semester_id) AS last_apply_date FROM textbook_stock s JOIN textbook_info t ON s.isbn t.isbn LEFT JOIN ( SELECT isbn, semester_id, class_id p a hrefhttps://download.csdn.net/download/helongqiang/19767480 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

基于单目视觉与深度学习的ROS智能小车自适应跟随系统实战解析 2026/9/28 20:33:07

基于单目视觉与深度学习的ROS智能小车自适应跟随系统实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
AI接管设备怎么落地?四层能力分级与自检清单 2026/9/28 20:33:07

AI接管设备怎么落地?四层能力分级与自检清单

做设备运维这块十多年,被问得最多的一个问题就是:“老板想让我们上AI,说要把设备都交给AI接管,这事靠谱吗?”我通常不急着回答靠不靠谱,而是先反问一句:你说的“接管”到底是让AI替你盯着屏幕、…

阅读更多 →
USB断连故障排查:Wireshark+USBPcap协议级抓包实战指南 2026/9/28 20:33:06

USB断连故障排查:Wireshark+USBPcap协议级抓包实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Model Optimizer 端到端示例测试指南:从运行到编写 tests/examples 全覆盖 2026/9/28 20:33:00

Model Optimizer 端到端示例测试指南:从运行到编写 tests/examples 全覆盖

人工智能大模型模型优化模型量化模型压缩 【免费下载链接】Model-Optimizer A unified library of SOTA model optimization techniques like quantization, distillation, pruning, neural architecture search, speculative decoding, etc. It compresses deep learning mode…

阅读更多 →
不用搭环境,浏览器内完成代码编写与排错:Codex 体验 2026/9/28 20:33:00

不用搭环境,浏览器内完成代码编写与排错:Codex 体验

前言 作为一名开发者,相信大家都有过这样的经历:想要快速验证一段代码逻辑,却要花大量时间搭建运行环境;临时需要分析项目、排查 BUG,手头环境又不方便。最近体验了一款网页端 AI 编程 Agent——Codex,无需…

阅读更多 →
微信机器人为什么会封号掉线?RPA和协议路线一次讲清 2026/9/28 20:33:00

微信机器人为什么会封号掉线?RPA和协议路线一次讲清

做微信机器人的人,最怕两件事:号突然掉了,或者直接被封。很多人把这归因于"运气不好",其实根因在技术路线。市面上的个人微信API方案,底层就两条路线——协议逆向和RPA,封号掉线概率天差地别。这…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉