简介:这是一套面向Java初学者与课程设计学习者的图书管理系统完整源码,基于Swing桌面界面与MySQL数据库实现,适合用来练习GUI编程、JDBC操作与基础分层设计。系统覆盖用户登录、图书类别管理(类别添加与维护)、图书管理(图书添加与维护)等核心业务模块,功能闭环完整,可直接作为课程作业或毕业设计的参考方案。压缩包共105个文件,约3.51MB,包含43个class编译文件、14个java源码、36个png界面素材、4个jar依赖库、4个properties配置以及1个sql建表脚本,源码、资源与数据库脚本齐备,便于导入IDE后直接运行调试。目前已有659人学习下载,读者可借此理解Swing窗体布局、事件监听、DAO数据访问与数据库连接配置的完整实现思路,并在此基础上扩展借阅归还、权限管理等功能,快速搭建属于自己的桌面管理应用。
1. 从一份 Java Swing 图书管理系统源码说起:它到底能跑通什么
如果你正在找一份能直接跑起来的 Java 桌面端练手项目,Java+Swing+MySQL 图书管理系统 3.0大概率已经出现在你的搜索结果里了。我拿到这份源码包的第一反应不是看界面,而是先翻目录结构——因为一个 Swing 项目能不能用,八成取决于它的分层是不是清晰、数据库脚本是不是完整、JDBC 连接有没有硬编码。这份 3.0 版本给我的感觉是:它不像很多网上流传的“半成品”,界面能点但数据库对不上,它是真的把图书的增删改查、借阅归还、用户权限这几条主线都串起来了。
它解决的核心问题很具体:给 Java 初学者或需要交课程设计的人一个结构完整、逻辑闭环、能本地部署的桌面管理系统。技术栈就是最经典的 Java SE + Swing + JDBC + MySQL,没有 Spring、没有 Maven 复杂依赖,用 Eclipse 或 IDEA 导入就能跑。适合谁?一是刚学完 JDBC 想找个项目练手的人,二是需要快速搭一个桌面管理类 Demo 的开发者,三是想拿它当模板改造成其他管理系统(比如实验室设备、仓库物资)的人。下面我按实际拆解的顺序,把这份源码从环境配置到核心逻辑再到踩坑点,一层层说清楚。
2. 环境搭建与数据库初始化:把黑匣子拆开的第一步
2.1 运行环境与依赖版本确认
拿到源码别急着导入 IDE,先确认三件事:JDK 版本、MySQL 版本、JDBC 驱动版本。这份 3.0 源码用的是 JDK 8 语法(没有 var、没有 record),MySQL 建议 5.7 或 8.0 都行,但驱动包要对应——MySQL 8.0 用mysql-connector-java-8.0.x.jar,5.7 用5.1.x也可以,但连接 URL 参数不一样。我一般会先看lib目录下有没有自带 jar 包,有的话直接用,省得版本对不上。
常见做法是:在项目根目录建一个lib文件夹,把 JDBC 驱动丢进去,然后在 IDE 里 Add to Build Path。如果你用 IDEA,右键 jar 选Add as Library就行。这一步不做,后面一定报ClassNotFoundException: com.mysql.cj.jdbc.Driver,这是血泪经验。
2.2 数据库建库建表与初始数据导入
源码包里通常会带一个.sql文件,名字可能是book_management.sql或db.sql。我建议不要直接双击运行,而是先打开看一眼字符集和引擎。常见坑是建表语句用了utf8而不是utf8mb4,导致中文书名插入乱码。
操作步骤很直接:
# 登录 MySQL mysql -u root -p # 创建数据库并指定字符集 CREATE DATABASE book_management DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 切换到该库 USE book_management; # 导入 SQL 文件(在系统命令行执行,不是 MySQL 交互界面) mysql -u root -p book_management < /path/to/book_management.sql导入完成后,执行SHOW TABLES;应该能看到book_info、user_info、borrow_record这几张核心表。如果只有一两张表,说明 SQL 文件不完整,需要手动补建。我一般会检查book_info里有没有book_id、book_name、author、publisher、stock这几个字段,这是借阅逻辑的基础。
2.3 JDBC 连接配置的修改位置
源码里数据库连接通常封装在一个DBUtil或BaseDao类里。打开它,你会看到类似这样的代码:
// DBUtil.java 中的连接方法 public static Connection getConnection() { Connection conn = null; try { // 加载驱动,MySQL 8.0 用 com.mysql.cj.jdbc.Driver Class.forName("com.mysql.cj.jdbc.Driver"); // 连接 URL,注意时区和 SSL 参数 String url = "jdbc:mysql://localhost:3306/book_management?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false"; conn = DriverManager.getConnection(url, "root", "你的密码"); } catch (Exception e) { e.printStackTrace(); } return conn; }这里有几个参数必须改:localhost:3306换成你的实际地址和端口,book_management换成你建的库名,root和密码换成你自己的。serverTimezone不设的话,MySQL 8.0 会报时区错误;useSSL=false是避免控制台一堆警告。改完这些,写一个main方法调一下getConnection(),能打印出连接对象就说明通了。
3. 核心功能模块拆解:登录、图书管理与借阅逻辑怎么串
3.1 登录模块与用户权限校验
登录界面通常是LoginFrame.java,里面两个输入框加一个按钮。点击按钮后触发ActionListener,拿到用户名密码后去user_info表查。这里有个细节:密码是明文还是 MD5?我拆过的版本里,3.0 多数还是明文比对,少数会加一个MD5Util工具类。如果是明文,你可以在SELECT * FROM user_info WHERE username=? AND password=?这里直接传参;如果是 MD5,就要先把输入密码转成 MD5 再查。
权限区分一般靠role字段,admin和user看到的主界面菜单不一样。管理员能进图书管理、用户管理,普通用户只能查书和借阅。这个逻辑写在登录成功后的if-else里,跳转不同的JFrame。我建议在这里加一个Session类或者静态变量存当前登录用户,不然后面借书时不知道是谁借的。
3.2 图书增删改查的 Swing 表格绑定
图书管理界面是重头戏,核心组件是JTable+DefaultTableModel。数据从book_info表查出来,塞进Vector或List,再刷到表格里。我一般会封装一个BookDao,里面四个方法:findAll()、addBook()、updateBook()、deleteBook()。
// BookDao.java 查询全部图书并返回 List public List<Book> findAll() { List<Book> list = new ArrayList<>(); String sql = "SELECT * FROM book_info"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery()) { while (rs.next()) { Book book = new Book(); book.setBookId(rs.getInt("book_id")); book.setBookName(rs.getString("book_name")); book.setAuthor(rs.getString("author")); book.setStock(rs.getInt("stock")); list.add(book); } } catch (SQLException e) { e.printStackTrace(); } return list; }表格刷新时调findAll(),然后tableModel.setRowCount(0)清空再逐行addRow。删除操作要拿到选中行的book_id,用table.getValueAt(selectedRow, 0)取第一列的值。这里容易翻车的地方是:表格列顺序和数据库字段顺序不一致,导致删错记录。我一般会在DefaultTableModel的列名数组里明确写死顺序,取值时按列名对应的索引来。
3.3 借阅归还与库存联动逻辑
借阅功能是这份源码里逻辑最绕的部分。正常流程是:用户选中一本书,点借阅,系统先查stock是否大于 0,大于则stock - 1,同时往borrow_record插一条记录,包含用户 ID、图书 ID、借出日期、归还日期(空)。归还时反过来:stock + 1,更新borrow_record的归还日期。
// BorrowDao.java 借书操作(简化版) public boolean borrowBook(int userId, int bookId) { String checkSql = "SELECT stock FROM book_info WHERE book_id=?"; String updateSql = "UPDATE book_info SET stock=stock-1 WHERE book_id=? AND stock>0"; String insertSql = "INSERT INTO borrow_record(user_id, book_id, borrow_date) VALUES(?,?,NOW())"; try (Connection conn = DBUtil.getConnection()) { conn.setAutoCommit(false); // 开启事务 // 检查库存 PreparedStatement checkPs = conn.prepareStatement(checkSql); checkPs.setInt(1, bookId); ResultSet rs = checkPs.executeQuery(); if (rs.next() && rs.getInt("stock") > 0) { // 扣库存 PreparedStatement updatePs = conn.prepareStatement(updateSql); updatePs.setInt(1, bookId); updatePs.executeUpdate(); // 插借阅记录 PreparedStatement insertPs = conn.prepareStatement(insertSql); insertPs.setInt(1, userId); insertPs.setInt(2, bookId); insertPs.executeUpdate(); conn.commit(); return true; } conn.rollback(); return false; } catch (SQLException e) { e.printStackTrace(); return false; } }这段代码的关键是setAutoCommit(false)和commit/rollback。没有事务的话,扣了库存但插记录失败,数据就脏了。参数说明:userId从登录后的 Session 取,bookId从表格选中行取,NOW()是 MySQL 函数直接生成当前时间。归还逻辑类似,只是把stock-1换成stock+1,再UPDATE borrow_record SET return_date=NOW() WHERE ...。
4. 避坑与排查:源码跑不起来时先看这几条
4.1 驱动加载失败或连接超时
现象:控制台报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver或者Communications link failure。原因:驱动 jar 没加到 classpath,或者 MySQL 服务没启动,或者 URL 里的端口写错。解决:先确认lib下的 jar 已经在 Build Path 里;然后telnet localhost 3306看端口通不通;最后检查 URL 是不是写成了jdbc:mysql://localhost:3306/库名,少一个斜杠都会出问题。
4.2 中文乱码从数据库到界面一路飘红
现象:图书名显示成????或者更多。原因:建库时字符集不是utf8mb4,或者 JDBC URL 没加characterEncoding=utf8。解决:ALTER DATABASE book_management CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;然后重新导入数据。URL 里补上useUnicode=true&characterEncoding=utf8。Swing 界面本身一般没问题,乱码多半出在数据库连接层。
4.3 表格数据不刷新或删不掉
现象:点了添加按钮,数据库里有新记录,但表格还是旧的;或者删除时报ArrayIndexOutOfBoundsException。原因:添加后没重新调findAll()刷新模型;删除时没判断getSelectedRow()是否返回 -1。解决:在添加/修改/删除成功后统一调一次refreshTable()方法,内部重新查库并fireTableDataChanged()。删除前加if (selectedRow == -1) { JOptionPane.showMessageDialog(null, "请选中一行"); return; }。
4.4 借阅时库存扣成负数
现象:两本书库存 1,两个人同时借,结果库存变成 -1。原因:检查库存和扣库存分了两步,中间没有锁或事务隔离。解决:把UPDATE book_info SET stock=stock-1 WHERE book_id=? AND stock>0放在事务里,用executeUpdate()的返回值判断是否真的扣成功。返回 0 说明库存不足,直接rollback。这是并发场景下的经典坑,单机跑可能遇不到,但逻辑上必须这么写。
4.5 窗口关闭后进程不退出
现象:点了右上角关闭按钮,界面没了但 Java 进程还在任务管理器里。原因:Swing 默认关闭时只是隐藏窗口,没有调System.exit(0)。解决:在主JFrame上setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE),或者在windowClosing事件里手动System.exit(0)。我一般直接在main方法最后加一句System.exit(0)兜底。
5. 二次开发与验证:把这份源码改成你自己的管理系统
5.1 从图书管理迁移到其他实体的改造清单
这份源码的价值不只是“图书管理”,而是一套Swing + JDBC 的 CRUD 模板。如果你想改成实验室设备管理、仓库物资管理,只需要替换三样东西:数据库表名和字段、实体类属性、界面上的标签文字。具体来说,book_info改成device_info,book_name改成device_name,BookDao改成DeviceDao,Book实体类改属性名。Swing 界面里的JLabel文本从“图书名称”改成“设备名称”,JTable的列名数组同步改。改完编译运行,只要数据库对得上,逻辑完全复用。
我一般会先画一张字段映射表,避免改漏:
| 原字段 | 新字段 | 类型 | 说明 |
|---|---|---|---|
| book_id | device_id | int | 主键自增 |
| book_name | device_name | varchar | 名称 |
| author | brand | varchar | 品牌/厂商 |
| stock | quantity | int | 库存数量 |
| borrow_record | usage_record | 表名 | 使用记录 |
这张表列清楚再动手,比边改边想要快得多。
5.2 验证改造是否成功的三个检查点
改完之后别急着说“完成了”,跑一遍这三个检查点:第一,新增一条记录,看数据库里有没有,表格有没有刷新;第二,修改这条记录的名称,看数据库和界面是否同步;第三,删除这条记录,看关联的使用记录表有没有残留脏数据。第三个点最容易忽略——如果usage_record里还有指向已删除设备的记录,说明外键约束没做或者删除逻辑没级联。我一般会在DeviceDao.delete()里先DELETE FROM usage_record WHERE device_id=?再删主表,用事务包起来。
5.3 一个我踩过的坑:事务边界别乱放
早期我改这类项目时,喜欢把conn.setAutoCommit(false)写在DBUtil.getConnection()里,觉得“全局事务”很省事。结果有一次借书失败回滚了,但界面上的表格已经刷新了,用户看到的是“借阅成功”的假象。后来我强制自己:事务只在 DAO 的具体业务方法里开,方法结束前必须 commit 或 rollback,绝不跨方法传递 Connection。从那以后我每次改完 DAO 方法,都会在finally块里检查conn是否关闭、事务是否终结。这个习惯帮我省掉了至少三次数据不一致的排查时间。
如果你拿到这份源码只是想跑通看看效果,按第 2 章的步骤配好环境、导好 SQL、改好连接,基本就能看到登录界面了。如果你想拿它当模板改造成自己的东西,第 5 章的字段映射和事务边界检查点建议你动手前先过一遍。源码包里的注释不算多,但核心逻辑都在 DAO 层,顺着BookDao往下读,比从界面事件往里追要快。希望这份拆解能帮你少走点弯路。
本文还有配套的精品资源,点击获取