恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Java实训图书馆管理系统:从CRUD到事务与连接池的完整实现

  • 首页
  • 资讯中心
  • /
  • Java实训图书馆管理系统:从CRUD到事务与连接池的完整实现

相关资讯

SpringBoot+Vue实现家电维修绩效管理系统开发实践 2026/10/15 12:51:07
QT C++中SQLite嵌入式数据库实战:零依赖部署与线程安全方案 2026/10/15 12:46:07
SpringBoot赛务管理系统开发与实战经验分享 2026/10/15 12:46:07

最新资讯

爆火的 OpenClaw 到底怎么用?完整安装与使用教程(TaoToken 统一 Key 接入版)
layui输入框验证规则全解析:从lay-verify到form.verify的实践指南
AI科研助手赋能论文数据分析:从清洗到三线表的全流程实践
周立功CAN用户手册实战:从零跑通USBCAN收发链路与避坑指南
结婚证双认证公证需要什么材料?证天下一站式申办教程,附避坑指南
单通道盲源分离实战:SSA-ICA原理、Matlab代码与避坑指南

今日推荐

AI视频生成的时间一致性难题与时空建模演进
基于bbox的PDF文档解析:多栏重排与水印过滤实战
探矿数据进RAG前的清洗全攻略:TXT、Word、PDF、网页四类文档处理实践

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

Java实训图书馆管理系统:从CRUD到事务与连接池的完整实现

发布时间:2026/10/15 12:51:07
Java实训图书馆管理系统:从CRUD到事务与连接池的完整实现 简介一份面向Java初学者的图书馆管理系统实训项目完整资源适合计算机专业学生完成课程设计或实训任务时参考。内容围绕图书信息管理、读者信息管理、借阅归还、图书查询与统计分析等核心模块展开覆盖数据库ER图设计、JDBC连接与增删改查、Swing界面布局、MVC分层架构、异常处理与权限校验等关键知识点从这些源码和设计文件中可以还原图书表、读者表、借阅记录表的关系以及借阅超期、可借状态等业务判断逻辑。压缩包共1988个文件大小约6MB其中以XMI建模文件、Class字节码、Java源文件、XML配置和Eclipse工程相关文件为主大量XMI设计模型有助于理解类图结构和数据库建模Java源码与编译产物并存也便于导入开发环境后对照调试。就技能层级而言适合已经掌握Java语法、希望综合运用面向对象与数据库知识的读者也适合作为课程设计的起点模板。已有229人学习下载适合需要完整实训代码和设计文档对照的Java学习者。1. 图书馆管理系统Java 实训任务里最该较真的 CRUD 项目不少人在做图书馆管理系统java实训任务时一开始觉得它就是个普通的增删改查练习图书表、读者表、借阅表敲完几个 JSP 或 Swing 界面就算交差。但真正被导师挑出毛病的地方往往不在功能缺没缺而在事务边界、SQL 注入防护、日期处理和级联删除这些“看不见”的位置。这个系统能很好地检验你对 JDBC 封装、分层思想、数据库约束和基础设计模式的理解程度而且是 Java 后端求职简历里出现频率极高的项目类型。如果你正在找一份既能过实训考核、又能写进项目经验的代码包这篇笔记会把拆解过程和落地细节按完整流程讲清楚。适合的人群很明确刚学完 JavaSE 想做第一个综合项目的人或者被实训任务卡住需要参考完整代码结构的人。下文从表结构设计讲到 CRUD 与借还流程再讲到事务和连接池改造每一步都会给出参数说明与坑点记录照着走基本不会偏。2. 先理清需求和选型分层架构、数据库表与依赖取舍2.1 为何实训里的管理系统都爱用三层架构图书馆管理系统在实训任务里被反复布置是因为它业务边界清晰图书管理、读者管理、借阅归还、逾期统计每块都能独立测试。而三层架构是这类 CRUD 项目最稳的骨架因为它把界面、业务规则和数据访问拆开改界面不碰 SQL调 SQL 不动页面。常见的包结构如下src ├── com.library.entity // 实体类Book, Reader, BorrowRecord ├── com.library.dao // 数据访问层BookDao, ReaderDao, BorrowDao ├── com.library.service // 业务层BookService, BorrowService ├── com.library.view // 界面层Swing 或 Servlet/JSP └── com.library.util // 工具类DBUtil, StringUtil我在拆解模拟项目 X 时发现很多初学者把 SQL 直接写在界面类里导师一问“借阅表要加一个字段要改几个文件”当场就答不上来。分层改造后用 service 包住事务用 dao 统一管理 SQL加字段只需要动实体类和对应的 dao 映射界面层几乎不改这个思路在实训练习和后续做外包项目时同样适用。2.2 表结构设计五张表和一条外键原则图书管理系统最少需要四张表图书表、读者表、借阅表和管理员表。多数实训要求里还会加一个“分类表”便于做图书分类统计。表结构直接影响后面写 SQL 的复杂度所以先设计好再动手。表名关键字段说明bookid, book_name, author, publisher, category_id, stock, remainingcategory_id 关联分类表categoryid, category_name一个分类对应多本图书readerid, reader_no, name, phone, max_borrow_num每人可借上限borrow_recordid, book_id, reader_id, borrow_date, due_date, return_date, statusstatus 标记借出/已还/逾期adminid, username, password登录用密码存哈希值这里最容易翻车的是在 borrow_record 里直接存 book_name 而不是 book_id。虽然查询时确实少了一次 join但一旦书名改版所有历史借阅记录的关联就断了。我一般会坚持只存外键 id查询时用 JOIN 取名称这个习惯在实训代码里也会成为评分亮点。2.3 依赖选型JDBC 直连还是 MyBatis 封装实训场景里通常不让你上框架JDBC 是默认要求。但 JDBC 写起来繁琐所以项目包里一般会自带 DBUtil 工具类和 DAO 基类来做通用查询。如果你拿到的是这样的代码包先看它有没有帮你把连接关闭、预编译、结果集映射这些重复操作封装掉。下面是一个典型 JDBC 工具类的核心方法很多实训项目在此基础上扩展public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/library_db?useSSLfalsecharacterEncodingutf8; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } 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) {} } } }URL 里有两个参数特别关键useSSLfalse 是为了避免本机测试时 SSL 握手报错characterEncodingutf8 是为了防止中文写入数据库变成问号。如果你用的 MySQL 8.x驱动类名必须是 com.mysql.cj.jdbc.Driver旧版的 com.mysql.jdbc.Driver 在新驱动里已经移除了这也是拿到代码包后第一个要检查的位置。DAO 基类里的通用查询方法通常会封装一个 RowMapper 接口用 Java 8 的 lambda 简化结果集到实体的映射。如果你看到这样的设计说明代码作者对 JDBC 的抽象有一定实战经验这种写法在实训报告里也很加分。3. 图书 CRUD 与借还书的完整实现参数、SQL 与代码落地3.1 图书新增和更新PreparedStatement 传参的正确姿势图书模块是系统的门面新增和编辑功能一般放在一起共用同一个表单。先看一个标准的 BookDao 新增方法注意参数处理和返回值设计public int insertBook(Book book) { String sql INSERT INTO book (book_name, author, publisher, category_id, stock, remaining) VALUES (?, ?, ?, ?, ?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { ps.setString(1, book.getBookName()); ps.setString(2, book.getAuthor()); ps.setString(3, book.getPublisher()); ps.setInt(4, book.getCategoryId()); ps.setInt(5, book.getStock()); ps.setInt(6, book.getStock()); // 新增时剩余量与库存一致 ps.executeUpdate(); try (ResultSet keys ps.getGeneratedKeys()) { if (keys.next()) { return keys.getInt(1); } } return 0; } catch (SQLException e) { e.printStackTrace(); return -1; } }新增时让 remaining 等于 stock 是个很容易被忽略的业务规则。如果两者分开填就会出现“库存 10 本但可借 0 本”的逻辑错误。PreparedStatement 里加了 RETURN_GENERATED_KEYS 参数这样新增后可以立刻拿到主键用于跳转到详情页或继续录入借阅记录。如果你只需要返回影响行数去掉那个参数、直接走 executeUpdate 就可以。更新方法逻辑类似只是 SQL 变成 UPDATE并且要额外 set 一个条件 id。这里有一个常见误区有些代码会把 remaining 也放进更新表单里让用户去改这是不对的剩余量应该由借还业务自动维护手工改会导致数据错乱。3.2 图书搜索与列表分页LIKE 查询的参数化处理搜索功能在实训里通常支持按书名、作者两个字段模糊查询。模糊查询的 SQL 写法和参数拼接顺序很容易踩坑尤其要注意 % 符号的位置public ListBook searchBooks(String keyword, int pageStart, int pageSize) { String sql SELECT * FROM book WHERE book_name LIKE ? OR author LIKE ? LIMIT ?, ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { String pattern % keyword %; ps.setString(1, pattern); ps.setString(2, pattern); ps.setInt(3, pageStart); ps.setInt(4, pageSize); try (ResultSet rs ps.executeQuery()) { ListBook list new ArrayList(); while (rs.next()) { list.add(mapBook(rs)); } return list; } } catch (SQLException e) { e.printStackTrace(); return Collections.emptyList(); } }LIMIT ?, ? 是从第几行开始取、取多少行的意思不是“第几页”。假设每页 10 条第二页的 pageStart 应该是 10 而不是 2这个换算经常有人在 Service 层写错。我一般会在 Service 层统一做计算int pageStart (currentPage - 1) * pageSize;这样 DAO 层只管 SQL 执行逻辑。LIKE 查询参数化后完全不用字符串拼接也就杜绝了 SQL 注入风险。有些旧代码喜欢写WHERE book_name LIKE % keyword %实训时导师可能不会深究但代码审计或面试时这是致命伤。3.3 借书与还书事务控制的边界在哪借书操作涉及两件异步更新往 borrow_record 表插入一条记录同时把 book 表的 remaining 减一。这两个操作如果分开执行中间任何一步失败都会造成数据不一致。public boolean borrowBook(int bookId, int readerId) { String insertRecord INSERT INTO borrow_record (book_id, reader_id, borrow_date, due_date, status) VALUES (?, ?, CURDATE(), DATE_ADD(CURDATE(), INTERVAL 30 DAY), BORROWED); String updateBook UPDATE book SET remaining remaining - 1 WHERE id ? AND remaining 0; Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); try (PreparedStatement ps1 conn.prepareStatement(insertRecord); PreparedStatement ps2 conn.prepareStatement(updateBook)) { ps1.setInt(1, bookId); ps1.setInt(2, readerId); ps1.executeUpdate(); ps2.setInt(1, bookId); int rows ps2.executeUpdate(); if (rows 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; } finally { try { if (conn ! null) { conn.setAutoCommit(true); conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } }事务控制的精髓在conn.setAutoCommit(false)之后两条 SQL 共享同一个连接和同一个事务。UPDATE 语句里的AND remaining 0是一个防御性条件作用是在库存为 0 时返回影响行数 0然后主动回滚。如果没有这个条件库存变成负数也不会报错数据就直接坏了。还书操作是反向流程更新 borrow_record 的 return_date 和 status再把 book 表的 remaining 加一。同样需要事务控制。借还逻辑里日期处理可以直接用 MySQL 的 CURDATE() 和 DATE_ADD()这样不用在 Java 端处理日期格式化也避免了 SimpleDateFormat 线程安全的问题。你如果拿到代码包重点检查里面是不是也用这两个函数如果看到 Java 端自己拼日期字符串那就要留个心眼。3.4 读者借阅历史与逾期计算读者模块除了基本 CRUD还有一个被实训项目隐藏的特殊功能——借阅历史。通常的做法是拉取当前读者的借阅记录后在 Java 内存里计算是否有逾期。实际项目中逾期判断一般交给 SQL 查出来比如以下查询SELECT br.id, b.book_name, br.borrow_date, br.due_date, br.return_date, CASE WHEN br.return_date IS NULL AND br.due_date CURDATE() THEN 已逾期 WHEN br.return_date IS NOT NULL AND br.return_date br.due_date THEN 逾期归还 ELSE 正常 END AS status_text FROM borrow_record br JOIN book b ON br.book_id b.id WHERE br.reader_id ? ORDER BY br.borrow_date DESC;用 CASE WHEN 语句把状态直接计算出来展示层只需要渲染字符串不需要写复杂的判断逻辑。CASE WHEN 里的判断顺序是有讲究的先判断空值逾期再判断归还后逾期最后才是正常顺序反了会得到错误的结果。这里的“已逾期”和“逾期归还”是两个状态前者表示书还在读者手里但超过应还日期后者表示虽然还了但还晚了业务上扣款规则完全不同。读者表里有个字段 max_borrow_num借书前需要校验当前已借数量是否达到上限。校验的做法是查 borrow_record 中该 reader_id 且 return_date IS NULL 的记录数这个查询可以用 GROUP BY 做也可以用简单 COUNT。这里要注意已还的记录不能算进在借数量否则读者永远只能借一本书。4. 避坑与排查实训中常见的六个翻车现场与解法4.1 现象运行报 ClassNotFoundException驱动加载失败原因MySQL 驱动 JAR 包没放进 WEB-INF/lib 或项目构建路径里。这几乎是新手最低级但频率最高的错误。解决如果用 Maven 项目在 pom.xml 里确认 mysql-connector-java 依赖已加入如果是普通 Dynamic Web Project把驱动 JAR 拷贝到 WEB-INF/lib 目录后重新发布。某些开发工具还需要在 Project Structure 里把 JAR 添加到 Libraries光拷到目录不刷新索引也不行。4.2 现象控制台输出中文是乱码数据库里存进的是问号原因连接 URL 没有指定 characterEncodingutf8或者页面、数据库、连接串三层编码不一致。MySQL 数据库本身的字符集如果是 latin1存中文也会出问题。解决连接串加上?useUnicodetruecharacterEncodingutf8同时检查 MySQL 建库时的字符集。我会在代码里用SHOW CREATE DATABASE library_db查看默认字符集如果不是 utf8mb4把库表和连接串全部统一调整。前端页面也要在head里加meta charsetUTF-8三层缺一层都会乱码。4.3 现象借书明明成功但库存没减原因执行 UPDATE book SET remaining remaining - 1 时连接是自动提交模式且执行了多次同一事务或者 SQL 里没写AND remaining 0导致最后库存变成负数界面就不显示了。解决借还操作必须把多条 SQL 放在同一个事务里执行要么全成功、要么全回滚。同时给 UPDATE 语句加库存条件如果影响行数为 0说明库存不足代码要主动回滚并给用户提示。检查库存为负的方法很简单SELECT * FROM book WHERE remaining 0有查出来就是事务没做好。4.4 现象外键约束报错删不掉有借阅记录的图书原因book 表的主键被 borrow_record 表引用直接 DELETE FROM book WHERE id? 时数据库拒绝执行。解决删除前先检查 borrow_record 是否有未归还的记录有的话提示“该书存在未归还记录不可删除”。如果实训只要求逻辑删除给 book 表加一个 is_deleted 字段删除时执行 UPDATE 而不是 DELETE这样借阅历史里的 join 查询还能正常显示书名是最稳妥的做法。4.5 现象分页查询时页码越点越乱数据重复或缺失原因LIMIT 的起始位置被错误地传成了页码本身第二页执行的是 LIMIT 2,10“跳过两行”后的数据顺序就乱了。解决在 Service 层统一做换算参数传入 currentPage 和 pageSize内部计算start (currentPage - 1) * pageSize。另外注意 ORDER BY 不要漏掉不排序时 LIMIT 的结果顺序是不确定的容易出现翻页后数据跳动。4.6 现象导出 Excel 或打印报表时日期格式变成一串数字原因Java 里 Date 类型直接 toString() 输出的是英文格式或时间戳没有做格式化处理。有些代码用 SimpleDateFormat 但没指定时区导出的时间差了 8 小时。解决统一用一个日期工具类格式化显示。常见写法是new SimpleDateFormat(yyyy-MM-dd HH:mm:ss).format(date)并且设置时区TimeZone.setDefault(TimeZone.getTimeZone(Asia/Shanghai))。实训项目里日期多数只有年月日用 LocalDate 更轻便避免处理时分秒。5. 高分改造三板斧分页组件、事务优化与连接池接入最后一个环节分享几个能让实训评分往上走的改造点全部在现有代码基础上改就好不需要推倒重来。第一板斧把分页逻辑封装成 PageResult 对象。现有代码里分页计算通常散落在 Servlet 和 Service 层改造后用一个泛型类统一装数据public class PageResultT { private ListT list; private int totalCount; private int currentPage; private int pageSize; private int totalPages; public PageResult(ListT list, int totalCount, int currentPage, int pageSize) { this.list list; this.totalCount totalCount; this.currentPage currentPage; this.pageSize pageSize; this.totalPages (int) Math.ceil((double) totalCount / pageSize); } }这个类的价值在于把“总记录数、总页数、当前页、每页条数”打包成一个对象传给前端JSP 里用 EL 表达式直接读取避免在页面里重复写除法取余逻辑。Service 层查完数据后再执行一条SELECT COUNT(*) FROM book WHERE 条件就能构造 PageResult。注意 COUNT 查询条件和列表查询条件必须完全一致否则总页数和实际页数对不上这个坑我在模拟项目 X 里踩过多次。第二板斧给“借还事务”增加统一的事务管理器。现在的借书方法里写了一套事务控制还书方法里又写了一遍代码重复严重。可以抽出一个简单的 TransactionTemplate用接口回调的方式把事务控制统一收口public interface TransactionCallbackT { T doInTransaction(Connection conn) throws SQLException; } public class TransactionTemplate { public T T execute(TransactionCallbackT callback) throws SQLException { Connection conn DBUtil.getConnection(); try { conn.setAutoCommit(false); T result callback.doInTransaction(conn); conn.commit(); return result; } catch (SQLException e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(true); DBUtil.close(conn, null, null); } } }改造后借书方法只需要写 Lambda 表达式描述“在该事务里做什么”事务打开和回滚交给模板类统一处理。这个写法是 Spring 事务的一个简化版雏形你在实训报告的“设计亮点”部分写一笔通常会被导师多留意几眼。第三板斧引入简单的数据库连接池。JDBC 直连每次查询都要建立物理连接实训中数据量小体现不出问题但答辩时如果被问“你这个系统能支撑多少并发”直接答不上来。可以在不引入框架的情况下用 HikariCP 的最小配置替代 DBUtil 的 DriverManagerHikariConfig config new HikariConfig(); config.setJdbcUrl(jdbc:mysql://localhost:3306/library_db?useSSLfalse); config.setUsername(root); config.setPassword(123456); config.setMaximumPoolSize(10); config.setConnectionTimeout(3000); HikariDataSource ds new HikariDataSource(config);把 DataSource 注入 DBUtil之后所有 getConnection 都从池里获取性能和稳定性立马上一个台阶。HikariCP 是生产环境最常见的连接池修改量只有 DBUtil 一个类其他代码完全兼容。唯一要注意的是引入 JAR 包时确认版本和 MySQL 驱动兼容HikariCP 最新版需要 JDK 8 以上如果你实训机器用的是老 JDK 6就别折腾这个了保持直连就行。做完这三板斧这份图书馆管理系统代码就从“能跑”变成了“能讲”事务有边界、查询有分页、连接有池化、状态有 CASE WHEN 兜底。从那以后我每次搭这类管理系统都会强制走一遍同样的检查事务包没包住全部写操作LIKE 查询有没有参数化分页起始位置有没有换算对。量产项目里这些问题会让线上事故一夜之间爆出来实训时把血泪踩完总比上线后翻车好。希望这些拆解对你有用。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号