
简介这是一套面向高校数据库系统课程设计的Java图书馆管理系统完整项目适合需要完成数据库建模、管理系统开发与课程报告的学生参考。压缩包共277个文件、总大小约30.91MB内含49个Java源码文件、4个SQL数据库脚本、31个Vue页面、32个JavaScript脚本、22个CSS样式以及XML配置、JAR依赖等基本覆盖从数据库建表到前端页面展示的完整链路。目录按后端逻辑、前端组件、配置文件等模块划分层次清楚便于快速定位代码和数据库初始化内容。已有1488人学习下载说明这套源码在同类课设中具有较高参考价值也适合用来补充项目文档与答辩说明。借助该资源可快速理清图书信息管理、借阅归还、读者管理等典型模块的实现思路并可直接导入数据库和运行项目为课程设计与答辩准备提供支撑。1. 数据库课程设计选型为什么JavaMySQL的图书馆管理系统成了课设默认答案如果你手头躺着一个名为数据库系统课程设计java图书馆管理系统源码数据库.zip的压缩包大概率已经在为数据库课程设计做准备了。这个标题拆开看就是三块Java、数据库、图书馆管理系统再加一个源码数据库的交付形态。说白了你要的东西是一套能跑通的Java课设带建表脚本打开就能演示增删改查最后还能写进实验报告和答辩PPT。为什么偏偏是图书馆管理系统因为它的业务边界清晰实体关系容易画借书、还书、逾期、罚款这些场景天然需要多张表联动正好覆盖数据库课程设计要求的ER图、范式、外键、索引和事务。Java 配上MySQL则几乎是高校机房和笔记本上的默认环境JDBC连接数据库的代码模板到处都有遇到问题也最容易搜到答案。这个方案适合正在做课设的在校生也适合想快速搭一个练手项目来复习Java和数据库基础的人目标是拿到一个能演示、能讲清楚逻辑、经得起答辩追问的完整作业而不是造一个生产级系统。2. 拆开ZIP看骨架源码目录、数据库脚本与JDBC连接配置怎么对齐拿到压缩包第一件事不是双击运行而是先搞清楚里面到底装了什么。课程设计源码包的排布方式几十年没大变过一个Java工程加一个SQL脚本少数会附带说明文档和依赖包。先认清结构后面所有配置才有落点。2.1 源码包的标准组成src目录、lib目录和SQL脚本谁都不能少我一般会把这类压缩包按下面这张清单核对一遍缺了哪一样运行阶段就会在哪个环节卡住。文件/目录常见命名作用缺失时的表现Java源码目录src/存放 .java 源文件按包分层没有它工程无法编译编译输出/依赖lib/ 或 WEB-INF/lib/放置 JDBC 驱动 jar、第三方库缺 MySQL 驱动必报 ClassNotFoundException数据库脚本library.sql、db.sql、init.sql建库、建表、插入演示数据没导库程序一查表就报表不存在配置文件db.properties、config.properties数据库连接参数无配置或配错连接直接失败说明文档README.txt、实验报告.doc运行步骤、表结构说明只能靠猜踩坑成本变高注意很多从学长手里拷来的包并没有lib目录因为 IDEA 或 Eclipse 的依赖是写进工程配置文件里的比如.idea/libraries或.classpath。这时候你需要确认自己用的 IDE 能不能识别这些配置识别不了就把mysql-connector-java-x.x.x.jar手动加进项目依赖。核对完清单不要急着点运行。先打开src目录看包结构。常见的设计是entity放实体类dao放数据库操作类ui或view放窗口界面。这个分层本身就是答辩时的讲解线索后面改动代码也都在这些包里进行。2.2 数据库脚本导入先建库再导表的命令与可视化工具姿势数据库脚本是源码能跑起来的先决条件。所谓源码数据库意思就是SQL文件已经给你备好了剩下的工作是在本地MySQL里执行它。常见做法是用MySQL命令行导入速度快、报错直接mysql -u root -p -e CREATE DATABASE IF NOT EXISTS library DEFAULT CHARACTER SET utf8mb4; mysql -u root -p library D:/course_design/library_db.sql第一行先建空库指定utf8mb4字符集第二行把脚本导入到library库里。utf8mb4不是可有可无的参数——如果建库用了默认的latin1后面Java写进中文读者姓名、书名时十有八九在表里变成乱码。用-e执行建库语句的好处是库已存在时不会报错脚本可以重复执行。如果你不喜欢敲命令行用可视化工具导 SQL 也可以新建数据库library设置排序规则为utf8mb4_general_ci然后右键数据库选择运行SQL文件选中压缩包里的 SQL 脚本执行。注意执行完毕后要刷新一下左侧的表列表很多工具不会自动刷新你会误以为建表失败。导入完成后务必验证一遍执行SHOW TABLES;看看表是否齐全执行SELECT COUNT(*) FROM book;看看演示数据有没有进去。这一步能提前暴露SQL脚本本身带语法错误或导出时少了数据的问题别等到Java程序跑起来才发现库里空空如也。2.3 改对连接配置db.properties五个参数决定能不能连上MySQL源码包里的配置文件名不固定但内容万变不离其宗就是JDBC连接数据库要用的几个参数。我见过太多人卡在这一步程序编译通过一按运行就报连接超时或拒绝访问。先把典型配置贴出来jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/library?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8 jdbc.usernameroot jdbc.password123456这份配置里有五个关键点驱动类名、连接URL、端口号、用户名、密码。驱动类名用com.mysql.cj.jdbc.Driver是MySQL 8.0 之后的写法如果你的MySQL是5.7及以下有的旧项目会写com.mysql.jdbc.Driver两种写法在新版驱动里前者才被推荐后者在MySQL 8的驱动里已经被移除。URL里的localhost:3306对应你本机的MySQL端口装过多个MySQL实例的同学要确认实际端口不然会连到不存在的服务上。characterEncodingutf8解决中文乱码serverTimezoneAsia/Shanghai解决MySQL 8的时区报错没有这个参数会看到The server time zone value Öйú±ê׼ʱ¼ä is unrecognized这种乱码式报错。用户名密码改成你自己MySQL的账号root默认密码为空?那你密码栏就留空但很多驱动要求空密码也用password占位。配置文件的读取方式通常是Java代码里写一个Properties工具类加载.properties文件加载不到时会抛NullPointerException。最常见的翻车是把配置文件放错位置IDE编译后没有把它复制到classes目录。检查方法是去target/classes或out/production目录里翻一翻没有就把文件挪到src根目录下重新编译。3. 数据库设计是答辩考点图书馆管理系统的表结构、外键与查询SQL课程设计的分数很大程度取决于数据库设计能不能讲清楚。老师问的从来不是代码能不能跑而是为什么这样建表外键为什么加在这里查询为什么这样写。所以这一章重点讲表结构背后的取舍。3.1 从ER图到DDL图书表、读者表、管理员表的字段取舍图书馆业务里最基础的两个实体是图书和读者。管理员表应该单独建不要和读者混在一张表里因为权限语义完全不同。下面是一份被无数课设验证过的建表SQLCREATE TABLE book ( book_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 图书ID, book_name VARCHAR(100) NOT NULL COMMENT 书名, author VARCHAR(50) NOT NULL COMMENT 作者, isbn VARCHAR(20) DEFAULT NULL COMMENT ISBN号, publisher VARCHAR(80) DEFAULT NULL COMMENT 出版社, price DECIMAL(8,2) DEFAULT 0 COMMENT 定价, total INT NOT NULL DEFAULT 0 COMMENT 馆藏总量, left_count INT NOT NULL DEFAULT 0 COMMENT 当前可借数量, category VARCHAR(30) DEFAULT NULL COMMENT 分类 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT图书表; CREATE TABLE reader ( reader_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 读者ID, reader_name VARCHAR(30) NOT NULL COMMENT 姓名, phone VARCHAR(20) DEFAULT NULL COMMENT 联系电话, id_card VARCHAR(18) DEFAULT NULL COMMENT 证件号, major VARCHAR(50) DEFAULT NULL COMMENT 专业/班级, reg_date DATE NOT NULL DEFAULT (CURRENT_DATE) COMMENT 注册日期 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT读者表; CREATE TABLE admin ( admin_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 管理员ID, username VARCHAR(30) NOT NULL UNIQUE COMMENT 登录名, password VARCHAR(64) NOT NULL COMMENT 密码建议存MD5/SHA散列值 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT管理员表;几个字段设计上的讲究需要留意。book_id用自增主键就行不要拿ISBN当主键ISBN可能重复、可能为空而且实际馆藏中同一本书有多册。total和left_count拆成两个字段是因为前者表示馆藏总量后者表示实时可借数量借书还书只动left_count这样统计馆藏规模和查询可借状态都不需要额外计算避免每次COUNT聚合。reg_date用DEFAULT (CURRENT_DATE)是MySQL 8.0.13之后的写法如果版本低可以改到Java代码里用new Date()传入或建表后手动ALTER TABLE reader MODIFY reg_date DATE NOT NULL DEFAULT CURRENT_DATE;调整这也是mysql数据库修改结构的典型场景。管理员表的密码字段我特意写成VARCHAR(64)长度按SHA-256的十六进制字符串设计的。很多同学图省事把密码明文存进去答辩时一旦被问到安全性就只能尬住。哪怕只是课设用DigestUtils.md5Hex(password)转一下再入库这一句话就能让你在答辩中多拿两分。注意ID_CARD这类敏感字段只做展示不要参与业务主键。3.2 借阅记录表的设计复合外键、状态字段与级联策略借阅记录是整个系统的核心它同时关联图书表和读者表是典型的多对多关联实体。借书、还书、逾期、罚款全都落在这一张表上设计错了后面代码全乱。建议结构如下CREATE TABLE borrow_record ( borrow_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 借阅ID, book_id INT NOT NULL COMMENT 图书ID, reader_id INT NOT NULL COMMENT 读者ID, borrow_date DATE NOT NULL DEFAULT (CURRENT_DATE) COMMENT 借出日期, due_date DATE NOT NULL COMMENT 应还日期, return_date DATE DEFAULT NULL COMMENT 实际归还日期未还为NULL, fine DECIMAL(8,2) NOT NULL DEFAULT 0 COMMENT 逾期罚款, status TINYINT NOT NULL DEFAULT 0 COMMENT 0借出中1已归还2逾期未还, CONSTRAINT fk_borrow_book FOREIGN KEY (book_id) REFERENCES book(book_id), CONSTRAINT fk_borrow_reader FOREIGN KEY (reader_id) REFERENCES reader(reader_id), INDEX idx_borrow_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT借阅记录表;status字段不要省。很多课设只用 return_date是否为空 来判断还书状态但逾期未还的图书也需要区别对待单独加一个状态字段能让查询和界面展示清爽很多。fine字段用于记录逾期产生的罚款未逾期就是0还书时由Java计算后更新进来。外键为什么要建一是保证不存在的图书或读者无法插入借阅记录二是在ER图上多对多关系必须通过关联表体现这是课程设计评分点。级联策略要慎重。上面代码没有写ON DELETE CASCADE这意味着删除图书时如果存在未归还的借阅记录会报外键约束错误。这其实是故意为之——物理删除一本有借阅历史的书会破坏借阅记录的完整性。如果你确实想删常见做法是先清理或修改借阅记录再删图书。如果老师明确要求演示删除主表数据后从表自动清理再改成ON DELETE CASCADE并在答辩时说明两种策略的区别。这里没有标准答案但一定要能自圆其说。3.3 答辩必问的查询SQL模糊查询、分页与逾期统计数据库课设里最难的部分不是建表而是写出让老师满意的查询。图书模糊搜索是最常见的功能Java界面上一个文本框输入关键字下面是结果列表。对应的SQL要写对-- 按书名或作者模糊搜索并按书名排序 SELECT book_id, book_name, author, publisher, price, total, left_count FROM book WHERE book_name LIKE CONCAT(%, ?, %) OR author LIKE CONCAT(%, ?, %) ORDER BY book_id;这里用CONCAT(%, ?, %)拼接模糊查询条件而不是直接写LIKE %?%——%是SQL通配符不能写在占位符外面直接套字符串。更重要的是这能防止简单的SQL注入风险课设阶段老师看到PreparedStatement加占位符基本就认可了。分页查询建议掌握MySQL的LIMIT写法SELECT book_id, book_name, author, publisher, total, left_count FROM book WHERE left_count 0 ORDER BY book_id LIMIT 10 OFFSET 20;LIMIT 10表示取10条OFFSET 20表示跳过前20条也就是第3页的数据。Java里把两个值算好再传进来比把整张表加载到内存再截取列表要专业得多。别小看这个细节凡是做Web版课设的同学分页查询几乎是必考。逾期统计是另一个高频问题。查询当前所有逾期未还的借阅记录SELECT r.reader_name, b.book_name, br.borrow_date, br.due_date, DATEDIFF(CURDATE(), br.due_date) AS overdue_days FROM borrow_record br JOIN reader r ON br.reader_id r.reader_id JOIN book b ON br.book_id b.book_id WHERE br.status 0 AND br.due_date CURDATE();DATEDIFF直接算出逾期天数省去在Java里用日期类计算的麻烦也避开了时区导致的天数偏差。三个表 JOIN 查询把实体关系和异常天数一次讲清楚这段SQL值得在答辩前反复练习因为老师大概率会顺着业务逻辑问到它。4. 把源码跑起来JDBC封装、DAO层与借书还书的事务处理数据库设计好了接下来就是Java代码怎么和它对接。很多同学的源码是从网上下的代码能跑但不一定理解答辩一紧张就露馅。这一章把 JDBC 和 DAO 层讲透你就有能力改代码、加功能甚至自己重写一版。4.1 JDBC六步从Class.forName到ResultSet的标准写法JDBC 操作数据库有固定的六步不管代码怎么封装最终都回到这个流程。第一步加载驱动第二步获取连接第三步创建语句第四步执行查询第五步处理结果集第六步释放资源。最小化代表如下// 1. 加载驱动MySQL 8 用 com.mysql.cj.jdbc.Driver Class.forName(com.mysql.cj.jdbc.Driver); // 2. 建立连接 String url jdbc:mysql://localhost:3306/library?characterEncodingutf8serverTimezoneAsia/Shanghai; Connection conn DriverManager.getConnection(url, root, 123456); // 3. 预编译SQL语句 String sql SELECT * FROM book WHERE book_name LIKE ?; PreparedStatement ps conn.prepareStatement(sql); ps.setString(1, % keyword %); // 4. 执行查询 ResultSet rs ps.executeQuery(); // 5. 遍历结果集 while (rs.next()) { System.out.println(rs.getInt(book_id) - rs.getString(book_name)); } // 6. 释放资源顺序ResultSet - PreparedStatement - Connection rs.close(); ps.close(); conn.close();这段代码的逻辑顺序务必背下来。第1步的Class.forName在JDBC 4.0之后可以省略因为驱动包会自动注册但保留它是兼容老版本的习惯。第2步的 URL 三个参数——characterEncodingutf8管中文serverTimezoneAsia/Shanghai管时区useSSLfalse管连接协议少一个都可能报奇怪的错。第3步用PreparedStatement而不是Statement除了防注入还能预编译SQL循环执行时性能更好。第6步的资源释放顺序不能乱先关ResultSet再关PreparedStatement最后关Connection反向关闭是规矩。用 try-with-resources 可以自动关闭但课设里写成这种直观的六步讲起来脉络更清楚。实际项目里不会每次都写六遍。常见做法是抽一个DBUtil工具类把加载驱动、获取连接封成静态方法把释放资源也封成静态方法。源码包里的DBUtil.java基本就是这个角色你只需要确认它的返回连接用的是上面这套URL配置。4.2 DAO层实现图书增删改查PreparedStatement与自增主键回填DAO 层的价值是把数据库操作从界面逻辑里剥离出来。以新增图书为例界面拿到用户填好的字段调用BookDAO.addBook(book)里面只负责拼SQL、传参数、返回结果。一个完整的方法长这样public int addBook(Book book) throws SQLException { String sql INSERT INTO book(book_name, author, isbn, publisher, price, total, left_count, category) 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.getIsbn()); ps.setString(4, book.getPublisher()); ps.setBigDecimal(5, book.getPrice()); ps.setInt(6, book.getTotal()); ps.setInt(7, book.getTotal()); // 新书 left_count 默认等于 total ps.setString(8, book.getCategory()); int rows ps.executeUpdate(); if (rows 0) { return 0; } // 回填自增主键让业务层拿到新生成的 book_id try (ResultSet keys ps.getGeneratedKeys()) { if (keys.next()) { book.setBookId(keys.getInt(1)); } } return book.getBookId(); } }重点在两个地方。第一prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)这行的意义自增主键是数据库生成的普通的executeUpdate()不会把主键返回给Java后续如果要用刚插入记录的book_id去写借阅表就拿不到正确值。加了RETURN_GENERATED_KEYS之后通过getGeneratedKeys()读取。这是很多课设源码里没有的正确姿势你手动补上就是一个加分点。第二left_count初始赋值等于total这是业务一致性问题新书入库时馆藏总量和可借数量应当一致。如果源码里把这几个字段漏掉借书时扣减left_count就会变成负数。所有 SQL 语句都用 try-with-resources 包起来连接、语句、结果集全部自动关闭不用在 finally 里手写一坨释放代码这也是Java近几年的标准写法面试聊到JDBC时能接得上话。删除、修改、查询单本的逻辑结构完全一样无非是把SQL换成DELETE或UPDATE参数少几个。真正要注意的是UPDATE和DELETE返回的是受影响行数代码里要判一下int rows ps.executeUpdate(); if (rows 0) { /* 提示用户没有这条记录 */ }避免界面提示操作成功实际什么都没发生。4.3 借书还书用事务setAutoCommit(false)与回滚演示图书借出这个动作表面是一个按钮背后其实是两步写库在borrow_record插入一条借阅记录再把book表的left_count减一。这两步要么都成功要么都失败不能出现借阅记录有了但库存没减或者反过来。JDBC 的Connection默认是自动提交的每条SQL执行完立刻 commit所以必须手动关掉自动提交自己控制事务边界。Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 // 第一步写入借阅记录 String insertBorrow INSERT INTO borrow_record(book_id, reader_id, borrow_date, due_date, status) VALUES(?,?,?,?,0); PreparedStatement ps1 conn.prepareStatement(insertBorrow); ps1.setInt(1, bookId); ps1.setInt(2, readerId); ps1.setDate(3, new java.sql.Date(System.currentTimeMillis())); ps1.setDate(4, new java.sql.Date(System.currentTimeMillis() 30L * 24 * 60 * 60 * 1000)); ps1.executeUpdate(); ps1.close(); // 第二步库存减一 String updateBook UPDATE book SET left_count left_count - 1 WHERE book_id ? AND left_count 0; PreparedStatement ps2 conn.prepareStatement(updateBook); ps2.setInt(1, bookId); int rows ps2.executeUpdate(); ps2.close(); if (rows 0) { throw new SQLException(库存不足借书失败); } conn.commit(); // 两步都成功才提交 } catch (SQLException e) { if (conn ! null) { conn.rollback(); // 任何一步出错全部回滚 } throw e; } finally { if (conn ! null) { conn.setAutoCommit(true); // 恢复自动提交 conn.close(); } }setAutoCommit(false)是事务的开关执行完所有SQL后commit()才真正落库。中间任何一步异常rollback()会撤销之前所有的写入。还书流程是对称的把borrow_record的status改成1、return_date填当前日期再把left_count加一同样要包在事务里。注意UPDATE book SET left_count left_count - 1 WHERE left_count 0这种写法把库存校验揉进SQL里避免了先查询再更新的竞态窗口虽然课设是单机演示但这个习惯值得养成。答辩时老师问借书时数据库断电了怎么办你要能说出事务回滚借阅记录和库存都不会产生脏数据这句话这个知识点就真正吃透了。5. 避坑排查图书馆管理系统课设最常见的五个翻车现场与修复顺序这一章是我见过最多的真实翻车合集。每一条都是现象 → 原因 → 解决的结构建议你在答辩前逐条对照检查。5.1 驱动类找不到ClassNotFoundException 的第一排查顺序现象程序一运行控制台第一行就是java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver或者是com.mysql.jdbc.Driver。原因要么 MySQL 驱动 jar 没放进项目依赖要么驱动类名写错。MySQL 8 的驱动包mysql-connector-java-8.x.x.jar里只有com.mysql.cj.jdbc.Driver老驱动 5.x 里用的才是com.mysql.jdbc.Driver把 5.x 的类名套到 8.x 的驱动上必然找不到。解决先把Class.forName里的类名确认为com.mysql.cj.jdbc.Driver再打开 IDE 的 Project Structure确认lib目录下的 jar 已经被加入 Libraries。命令行跑的同学检查java -cp是否把 jar 和 classes 都列进去。这一个问题占课设运行失败原因的六成以上先查驱动别急着改代码。5.2 中文乱码数据库连接URL和建库字符集双重检查现象界面上显示的书名、读者姓名全是问号或者??替换了所有中文字符MySQL 命令行里查表也是乱码。原因三个环节任一环节字符集不对——建库时没指定utf8mb4连接URL没带characterEncodingutf8Java 源文件本身编码不是 UTF-8。解决数据库端执行ALTER DATABASE library CHARACTER SET utf8mb4;把库默认字符集改过来已存在的表用ALTER TABLE book CONVERT TO CHARACTER SET utf8mb4;转换连接端在URL里补上characterEncodingutf8参数IDE 端检查 File Encoding 设置把源码文件转为 UTF-8 后重新编译。三个环节改完乱码基本消灭。注意utf8和utf8mb4的差异utf8mb4能存 emoji 和生僻字课设阶段直接用utf8mb4最省事。5.3 数据删不掉外键约束在作怪现象执行DELETE FROM book WHERE book_id 1;报错Cannot delete or update a parent row: a foreign key constraint fails。原因borrow_record表通过外键引用了book表的这条记录外键默认策略RESTRICT禁止删除被引用的父行。解决有三个方向先删除借阅记录再删图书SQL顺序调整为子表先删或者把建表语句改成ON DELETE CASCADE让借阅记录跟着图书自动删或者业务上做软删除给book表加一个is_deleted字段删除变成UPDATE book SET is_deleted 1 WHERE book_id ?。我建议用软删除既保留借阅历史又不会触发外键阻碍答辩还能多聊一句为什么不用物理删除。5.4 逾期天数算错日期计算别用毫秒数直接除现象还书时计算逾期天数明明逾期3天界面显示2天有时还出现负数。原因很多同学用(System.currentTimeMillis() - dueDate.getTime()) / (1000*60*60*24)算天数java.util.Date包含时分秒借书日期和还书日期的时间点不同毫秒差除以一天的毫秒数会在边缘时刻向下取整差一天甚至差两天。解决改用java.time.LocalDate计算只比较年月日ChronoUnit.DAYS.between(dueDate.toLocalDate(), LocalDate.now())或者直接把逾期计算写进 SQL用DATEDIFF(CURDATE(), due_date)。索引LocalDate不携带时区信息不依赖服务器时区配置比Date更不容易踩坑。5.5 自增ID获取不到getGeneratedKeys 是正解现象新增图书成功但下一步想在界面上提示添加成功编号为xxx显示的却是0或null。原因代码调用了executeUpdate()之后没有获取数据库生成的主键而是回头用SELECT MAX(book_id)查或者直接什么也没做。MAX(book_id)在多线程或并发环境下会拿到错误值在单机课设里也可能因为连接复用拿到旧数据。解决参照4.2节prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)然后getGeneratedKeys()读取。这段代码虽然只有三行却是区分能跑和写对了的分水岭。6. 答辩前最后一小时演示验证清单与两个加分小技巧最后这段时间不要改功能了改坏了没有后悔药按清单过一遍演示流程更靠谱。先过一遍环境确认MySQL服务已启动library库存在且包含演示数据IDE里能正常编译运行主类。接着按业务顺序演示登录管理员账号 → 新增一本图书 → 模糊搜索刚才添加的书 → 新增一个读者 → 借书 → 查借阅列表 → 还书 → 查库存恢复。每一步看一眼控制台或界面有没有异常输出。这里有个容易被忽略的小坑借书时输入的读者ID和图书ID必须是库里真实存在的直接输入不存在的数字会触发外键异常演示前手工查一下SELECT book_id, reader_id FROM borrow_record;把可用的ID抄在纸条上。加分技巧一预置一批边界数据。在SQL脚本里提前插入20本计算机类图书、5个读者其中包含书名相近的记录比如Java编程思想和Java核心技术方便演示模糊查询。再制造一条逾期未还的记录把某条borrow_record的due_date修改成昨天。这样演示时打开逾期列表就能直接展示不用现场编数据。技巧二演示完借还书流程后主动把代码切到4.3节的事务那段说一句借书在写入借阅记录和扣减库存时是有事务保护的任何一步失败都会回滚然后现场关掉MySQL服务或断开连接试一次——如果能安全报错且数据不变这个演示效果远比顺利跑通加分。这些年我带过的课设里翻车最多的从来不是代码难而是提前没跑通。我自己当年答辩前在教室电脑上第一次运行因为MySQL端口被占用借书按钮点了三次没反应满头冒汗。后来学乖了答辩前一定在机器上完整走两遍流程特别是数据库服务和驱动配置这种环境问题。这个习惯一直留到现在希望帮到你。本文还有配套的精品资源点击获取