拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Java+Swing+MySQL教材管理系统源码实战:从跑通到避坑

Java+Swing+MySQL教材管理系统源码实战:从跑通到避坑

简介:这份资源是面向高校学生与Java初学者的一套教材管理系统源代码,适合作为课程设计、毕业设计或Swing桌面开发练手项目。系统围绕管理员对教材的日常管理展开,涵盖教材信息录入、按教材号查询、入库与出库、库存查询以及统计打印等核心业务,出库时通过教材号删除对应记录,查询则输出教材完整信息,功能闭环较为完整。压缩包共7个文件,全部为java源码,整体约10KB,体量轻巧,便于阅读与二次修改,其中包含登录、图书录入、图书浏览、查询与数据库连接等模块,结构清晰,适合对照学习Swing界面与MySQL交互的写法。目前已有1193人学习下载,可作为课设参考模板,帮助读者快速理解桌面端管理系统的分层思路、数据库操作流程与调试方法,也可在此基础上扩展借阅、用户权限等功能。

1. 从一份教材管理系统源码说起:Java+Swing+MySQL 到底能跑出什么

很多同学在课程设计或毕业设计阶段会拿到一份「Java+Eclipse+Swing+MySql 教材管理系统源代码」,第一反应往往是双击导入、点运行,然后发现连不上数据库、界面乱码、按钮点了没反应。这份源码本质上是一个典型的 C/S 桌面应用:Swing 负责前端窗口和控件,Java 负责业务逻辑,MySQL 负责持久化教材信息、库存、出入库记录。它解决的核心问题是——让教材管理员不依赖浏览器,在本地就能完成教材入库、领用、归还、查询和统计。适合谁?适合正在学 Java 基础、面向对象编程、JDBC 连接、Swing 事件驱动的人,也适合需要快速交付一个可演示课程设计的人。但要注意,这类源码质量参差不齐,直接跑通只是第一步,真正值钱的是你能看懂它的分层、改掉它的硬编码、补上它缺失的校验。下面我按「先跑通、再拆解、后避坑」的顺序,把这份源码从环境到代码讲透。

2. 把源码跑起来:Eclipse 导入、MySQL 建库、JDBC 连接三件事

2.1 环境准备与 Eclipse 导入项目的正确姿势

拿到源码压缩包后,先别急着 Import。我一般会先看目录结构:如果根目录下有.project和.classpath,说明是 Eclipse 工程,可以直接用File → Import → General → Existing Projects into Workspace导入;如果只有src和lib,那就新建一个 Java Project,再把src拷进去、把lib下的 jar 加到 Build Path。这里有个血泪经验:很多源码用的是 JDK 8,而你本地装的是 JDK 17 或 21,导入后满屏红叉。解决办法是右键项目 → Properties → Java Build Path → Libraries,把 JRE 换成 1.8,或者改源码里的语法兼容。Eclipse 安装教程网上很多,但关键就一句:装完记得在eclipse.ini里把-Dfile.encoding=UTF-8加上,否则中文注释和界面文字会变问号。

导入后先别运行,检查lib目录下有没有mysql-connector-java-x.x.x.jar。如果没有,去 MySQL 官网下载对应版本,注意 8.0 以上用mysql-connector-j-8.x.x.jar,驱动类名从com.mysql.jdbc.Driver变成了com.mysql.cj.jdbc.Driver,URL 也要加时区和 SSL 参数。这一步不做,后面必报ClassNotFoundException或Communications link failure。

# 查看本地 MySQL 版本,决定用哪个驱动 mysql --version # 输出示例:mysql Ver 8.0.36 for Win64 on x86_64

上面命令用来确认 MySQL 大版本。5.7 和 8.0 在驱动、URL、认证插件上都有差异,先确认版本再选驱动,能省掉一半的连不上问题。

2.2 MySQL 建库建表:从 SQL 脚本到字符集设置

源码里通常会带一个.sql文件,但很多人直接双击运行会报错,因为里面可能写了CREATE DATABASE却没指定字符集。我一般手动建库,再导入表结构。下面是一份教材管理系统最小可用的建库脚本,字段按常见需求设计,你可以对照源码里的实体类调整。

-- 创建数据库,强制 utf8mb4,避免中文乱码 CREATE DATABASE textbook_ms DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE textbook_ms; -- 教材表 CREATE TABLE tb_textbook ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL COMMENT '教材名称', author VARCHAR(50) COMMENT '作者', publisher VARCHAR(100) COMMENT '出版社', isbn VARCHAR(20) UNIQUE COMMENT 'ISBN号', price DECIMAL(10,2) DEFAULT 0.00 COMMENT '单价', stock INT DEFAULT 0 COMMENT '库存数量', category VARCHAR(50) COMMENT '分类' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 领用记录表 CREATE TABLE tb_record ( id INT PRIMARY KEY AUTO_INCREMENT, textbook_id INT NOT NULL, user_name VARCHAR(50) NOT NULL COMMENT '领用人', amount INT NOT NULL COMMENT '领用数量', record_time DATETIME DEFAULT CURRENT_TIMESTAMP, status VARCHAR(20) DEFAULT '已领用', FOREIGN KEY (textbook_id) REFERENCES tb_textbook(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

建库时最容易翻车的是字符集。如果数据库用latin1,Swing 界面输入的中文存进去就是乱码,查出来也是问号。所以从库到表到连接 URL 都要统一utf8mb4。另外stock字段建议设默认值 0,对应热词里提到的「mysql设置默认值为0」,避免插入时 null 导致库存计算异常。外键约束看源码需求,如果源码里没做级联删除,建议先不加外键,否则删教材时会被记录表拦住。

2.3 JDBC 连接封装:URL 参数、驱动类与连接池取舍

源码里最常见的写法是写一个DBUtil或BaseDao类,里面硬编码url、user、password。跑通阶段可以这样,但你要知道每个参数的含义。下面是一个标准封装示例:

import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBUtil { // 8.0 驱动类名,5.7 用 com.mysql.jdbc.Driver private static final String DRIVER = "com.mysql.cj.jdbc.Driver"; // useSSL=false 关闭 SSL 警告,serverTimezone 解决时区报错 private static final String URL = "jdbc:mysql://localhost:3306/textbook_ms" + "?useUnicode=true&characterEncoding=utf8mb4" + "&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"; private static final String USER = "root"; private static final String PASSWORD = "你的密码"; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { throw new RuntimeException("MySQL 驱动未加载,检查 lib 下是否有 connector jar", e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }

这段代码的关键在 URL 参数。useSSL=false解决 MySQL 8.0 默认要求 SSL 导致的连接失败,对应热词里的「mysql ssl连接错误」;serverTimezone=Asia/Shanghai解决The server time zone value异常;allowPublicKeyRetrieval=true解决 8.0 的 caching_sha2_password 认证问题。如果你用连接池(如 Druid、HikariCP),把getConnection换成从 DataSource 取即可,但课程设计阶段没必要,直连更直观。注意密码不要提交到公开仓库,这是基本习惯。

3. Swing 界面与事件驱动:教材管理系统的前端骨架怎么搭

3.1 主窗口布局:JFrame、JTable 与菜单栏的组合

Swing 做管理系统,核心就三块:JFrame主窗口、JTable数据表格、JMenuBar菜单栏。源码里通常有一个MainFrame类继承 JFrame,构造方法里设置setSize、setLocationRelativeTo(null)居中、setDefaultCloseOperation(EXIT_ON_CLOSE)。表格用DefaultTableModel承载数据,列名和数据库字段对应。下面是一个最小主界面骨架:

import javax.swing.*; import javax.swing.table.DefaultTableModel; import java.awt.*; public class MainFrame extends JFrame { private JTable table; private DefaultTableModel tableModel; public MainFrame() { setTitle("教材管理系统"); setSize(900, 600); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); setLayout(new BorderLayout()); // 菜单栏 JMenuBar menuBar = new JMenuBar(); JMenu menu = new JMenu("教材管理"); menu.add(new JMenuItem("新增教材")); menu.add(new JMenuItem("领用登记")); menuBar.add(menu); setJMenuBar(menuBar); // 表格模型,列名与数据库字段对应 String[] columns = {"ID", "教材名称", "作者", "出版社", "ISBN", "单价", "库存"}; tableModel = new DefaultTableModel(columns, 0) { @Override public boolean isCellEditable(int row, int column) { return false; // 表格只读,编辑走弹窗 } }; table = new JTable(tableModel); add(new JScrollPane(table), BorderLayout.CENTER); setVisible(true); } }

这里有个细节:DefaultTableModel重写isCellEditable返回 false,防止用户直接在表格里改数据导致和数据库不同步。菜单项要绑定ActionListener,点击后弹出JDialog做新增或领用。布局用BorderLayout把表格放中间,工具栏或按钮放北边或南边。如果你看到源码里用绝对定位setBounds,那说明作者没学好布局管理器,窗口拉伸后控件会错位,建议改成BorderLayout+GridBagLayout。

3.2 事件监听与数据刷新:按钮点击后表格怎么更新

Swing 是单线程模型,所有界面更新必须在事件分发线程(EDT)里做。常见错误是在按钮监听里直接查数据库、更新表格,数据量大时界面卡死。正确做法是:查询逻辑放后台线程(SwingWorker),结果回到 EDT 更新tableModel。下面演示新增教材后刷新表格的流程:

JButton addBtn = new JButton("新增教材"); addBtn.addActionListener(e -> { // 弹出输入对话框,实际项目用自定义 JDialog String name = JOptionPane.showInputDialog(this, "请输入教材名称"); if (name == null || name.trim().isEmpty()) { JOptionPane.showMessageDialog(this, "教材名称不能为空"); return; } // 后台执行插入,避免阻塞 EDT new SwingWorker<Void, Void>() { @Override protected Void doInBackground() throws Exception { String sql = "INSERT INTO tb_textbook(name, stock) VALUES(?, 0)"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, name.trim()); ps.executeUpdate(); } return null; } @Override protected void done() { loadTableData(); // 回到 EDT 刷新表格 } }.execute(); });

SwingWorker的doInBackground在后台线程跑 JDBC,done在 EDT 执行,保证线程安全。loadTableData方法里清空tableModel再重新查库填充。注意每次操作后都要刷新,否则用户以为没保存。如果源码里用Vector存数据、手动fireTableDataChanged,原理一样,但DefaultTableModel更省事。热词里提到的「swing带勾选框的tree」属于进阶控件,教材管理系统一般用不到,但如果你要加分类树,可以用JTree加CheckBoxTreeCellRenderer,那是另一个话题。

4. 避坑与排查:源码跑不通时先查这五个地方

4.1 数据库连不上:从驱动到权限逐层排查

现象:启动报Communications link failure或Access denied for user。原因通常有三层:驱动 jar 没加、URL 参数不对、MySQL 用户权限不够。解决顺序是——先确认lib下有 connector jar 且已 Build Path;再检查 URL 端口 3306 是否被占用或 MySQL 没启动;最后用命令行mysql -u root -p登录,执行SELECT user, host FROM mysql.user;看 root 是否只允许 localhost。如果是 8.0 的caching_sha2_password插件导致,可以ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '密码';临时切换,但生产环境不建议。

4.2 中文乱码:库、表、连接、JVM 四处都要 UTF-8

现象:界面输入中文,存库后变问号,或查出来是乱码。原因:字符集不统一。解决:建库用utf8mb4,表用utf8mb4,JDBC URL 加characterEncoding=utf8mb4,Eclipse 工作空间编码设为 UTF-8,JVM 启动参数加-Dfile.encoding=UTF-8。四处缺一不可。如果已经存了乱码数据,只能重新录入,因为乱码是不可逆的。

4.3 主类找不到:Eclipse 运行配置与包名不匹配

现象:点运行报找不到或无法加载主类。原因:源码包名和实际目录不一致,或者运行配置里 Main class 选错。解决:右键项目 → Build Path → Source,确认源码根目录;再右键主类 → Run As → Java Application。如果源码里包名是com.textbook.view,但文件放在src/view下,就会报错,需要手动改包声明或移动文件。

4.4 表格数据不显示:查询结果集映射漏字段

现象:界面出来了,表格空白。原因:ResultSet遍历时字段名写错,或者tableModel没addRow。解决:在while(rs.next())里打印rs.getString("name")确认有数据;检查tableModel.addRow是否在循环内;检查列顺序和columns数组是否一致。常见错误是rs.getString(1)索引从 1 开始,有人写成 0 直接异常。

4.5 按钮点击无反应:事件监听未绑定或异常被吞

现象:点按钮没反应,控制台也没报错。原因:addActionListener没加,或者监听里抛异常被try-catch吞了。解决:在监听方法第一行加System.out.println("clicked")确认是否触发;检查catch块里是否只写了e.printStackTrace()但没弹窗提示。Swing 的异常默认打到控制台,用户看不到,建议用JOptionPane把错误弹出来。

5. 从能跑到好用:源码改造的三个进阶技巧

5.1 把硬编码 SQL 换成 DAO 分层

原始源码经常在按钮监听里直接写 SQL,改一个字段要翻好几个文件。我一般会抽一个TextbookDao类,方法包括findAll()、insert(Textbook t)、updateStock(int id, int delta)。实体类Textbook和表字段一一对应。这样界面层只调 DAO,业务逻辑清晰,也方便后面换数据库。下面是一个 DAO 方法示例:

public List<Textbook> findAll() throws SQLException { List<Textbook> list = new ArrayList<>(); String sql = "SELECT id, name, author, publisher, isbn, price, stock FROM tb_textbook"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery()) { while (rs.next()) { Textbook t = new Textbook(); t.setId(rs.getInt("id")); t.setName(rs.getString("name")); t.setStock(rs.getInt("stock")); list.add(t); } } return list; }

try-with-resources自动关闭连接、语句、结果集,避免资源泄漏。返回List而不是ResultSet,因为ResultSet在连接关闭后失效,这是新手常踩的坑。

5.2 库存扣减用事务保证一致性

领用教材时,要先查库存够不够,再扣减,再插记录。这三步必须在一个事务里,否则并发下会超卖。MySQL 事务处理用conn.setAutoCommit(false),成功后commit,异常rollback。下面是一个扣库存的事务方法:

public boolean lendBook(int textbookId, String userName, int amount) throws SQLException { String checkSql = "SELECT stock FROM tb_textbook WHERE id=? FOR UPDATE"; String updateSql = "UPDATE tb_textbook SET stock=stock-? WHERE id=? AND stock>=?"; String insertSql = "INSERT INTO tb_record(textbook_id, user_name, amount) VALUES(?,?,?)"; try (Connection conn = DBUtil.getConnection()) { conn.setAutoCommit(false); try (PreparedStatement check = conn.prepareStatement(checkSql); PreparedStatement update = conn.prepareStatement(updateSql); PreparedStatement insert = conn.prepareStatement(insertSql)) { check.setInt(1, textbookId); try (ResultSet rs = check.executeQuery()) { if (!rs.next() || rs.getInt("stock") < amount) { conn.rollback(); return false; } } update.setInt(1, amount); update.setInt(2, textbookId); update.setInt(3, amount); if (update.executeUpdate() == 0) { conn.rollback(); return false; } insert.setInt(1, textbookId); insert.setString(2, userName); insert.setInt(3, amount); insert.executeUpdate(); conn.commit(); return true; } catch (SQLException e) { conn.rollback(); throw e; } } }

FOR UPDATE行锁防止并发查库存时读到旧值,UPDATE ... WHERE stock>=?再兜底一次。事务提交前任何一步失败都回滚,保证记录和库存一致。这是教材管理系统从「能跑」到「能用」的关键一步。

5.3 用表格排序和筛选提升查询体验

教材多了以后,用户需要按名称、库存排序。JTable自带RowSorter,一行代码就能开启:table.setAutoCreateRowSorter(true);。如果要在数据库层排序,SQL 加ORDER BY stock ASC即可,对应热词里的「mysql排序」。筛选可以用TableRowSorter加RowFilter,根据输入框内容过滤。注意排序后行索引会变,取选中行数据要用table.convertRowIndexToModel(table.getSelectedRow()),否则删错记录。这个坑我踩过,删了不该删的教材,后悔药都没得吃。

最后说个习惯:每次改完源码,先备份一份能跑的版本,再动刀。我一般用 Git 本地仓库,提交信息写清楚「修复库存事务」还是「调整表格排序」。这样翻车了能回退,也方便对比自己改了什么。希望帮到你。

本文还有配套的精品资源,点击获取

返回列表