尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

数据库课程设计图书管理系统:表结构、事务与避坑指南

数据库课程设计图书管理系统:表结构、事务与避坑指南 简介这是一份数据库课程设计“图书管理系统”的完整课程设计报告面向需要完成数据库课程设计或图书管理相关项目的学生与开发者。系统针对传统人工管理图书信息存在的效率低、易出错、资源浪费等问题提出并实现了图书集中统一管理方案覆盖读者信息、书籍类别、书籍库存、借书还书以及超期罚款等核心模块。资源为1个doc文档约122KB正文从背景与需求分析切入详细给出实体关系模式、多张E-R图以及书籍类别、读者、书籍、借阅、归还、罚款等数据字典定义并说明系统实现要点。目前已有1897人学习下载。报告结构完整、设计思路清晰既可直接作为课程设计文档模板也可为小型图书管理系统的表结构设计、功能划分和文档撰写提供实用参考。1. 数据库课程设计图书管理系统先过表结构再谈界面图书管理系统是数据库课程设计里最经典也最容易“看起来简单、答辩翻车”的题目。很多同学把功夫花在 Swing 界面和按钮上结果老师一句“借书时如果两个人同时借同一本书怎么办”就卡住了。这个数据库课设真正考的不是界面多精致而是外键约束、事务边界和联表查询经不经得起追问。这篇按做课设的完整链路来表结构设计、JDBC 连接层、借还书事务、答辩前自测把能直接复用的方案和踩过的坑一起讲清楚适合正在凑功能又怕上台被问住的人。2. 从“借书”和“还书”倒推表结构五张表、两处约束、一组验证 SQL2.1 最小可行模型分类、图书、读者、借阅和管理员五张表课程设计要求里最常出现的功能是图书增删改查、读者增删改查、借书还书、逾期统计。从这些操作倒推最少需要五张表分类表、图书表、读者表、借阅记录表、管理员表。图书分类单独建表而不是在图书表里存一个字符串是为了避免“改一个分类名就得 UPDATE 所有图书”的尴尬答辩问第三范式时也能讲清楚。CREATE TABLE category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL UNIQUE, sort INT NOT NULL DEFAULT 0 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE book ( id INT PRIMARY KEY AUTO_INCREMENT, isbn VARCHAR(20) NOT NULL, title VARCHAR(200) NOT NULL, author VARCHAR(100) NOT NULL, publisher VARCHAR(100), category_id INT, total INT NOT NULL DEFAULT 1 COMMENT 馆藏总量, available INT NOT NULL DEFAULT 1 COMMENT 当前可借数量, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_book_category FOREIGN KEY (category_id) REFERENCES category(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE reader ( id INT PRIMARY KEY AUTO_INCREMENT, reader_no VARCHAR(20) NOT NULL UNIQUE COMMENT 学号/工号, name VARCHAR(50) NOT NULL, type TINYINT NOT NULL DEFAULT 1 COMMENT 1-学生 2-教师, max_borrow INT NOT NULL DEFAULT 5, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE borrow ( id INT PRIMARY KEY AUTO_INCREMENT, book_id INT NOT NULL, reader_id INT NOT NULL, borrow_time DATETIME NOT NULL, due_time DATETIME NOT NULL COMMENT 应还时间, return_time DATETIME DEFAULT NULL COMMENT 实际归还时间, status TINYINT NOT NULL DEFAULT 0 COMMENT 0-借出 1-已还, CONSTRAINT fk_borrow_book FOREIGN KEY (book_id) REFERENCES book(id), CONSTRAINT fk_borrow_reader FOREIGN KEY (reader_id) REFERENCES reader(id), INDEX idx_borrow_status (status), INDEX idx_borrow_reader_due (reader_id, due_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE admin ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password_hash VARCHAR(128) NOT NULL, role VARCHAR(20) NOT NULL DEFAULT operator ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;借阅表是整个设计的关键。book_id 和 reader_id 是两个外键分别关联图书和读者status 区分“借出中”和“已归还”。注意这里没有直接存“罚款金额”因为逾期费是业务计算结果用”DATEDIFF 规则“实时算比存字段更灵活。管理员表的 password_hash 用来存哈希而不是明文密码答辩时提到这一点会比别人显得细致。图书表里 total 和 available 是两件事total 是馆藏总量available 是当前可借数量。课设里很多人只留一个 total借出去就不知道还剩几本所以答辩必被问“你系统里怎么知道哪本书还有没有库存”。用 available 字段记录借书时减一还书时加一查询时直接 SELECT 即可。2.2 两处容易被忽略的约束库存字段不是装饰状态校验要进事务第一处约束是唯一键。reader 表的 reader_no、admin 表的 username、category 表的 name 都要 UNIQUE。这三处不建唯一索引程序里靠“先查再插”挡不住并发重复提交在 MySQL 里也几乎无法靠普通索引兜底所以必须从表结构层面约束。第二处约束在借阅表。同一本书同一个 book_id同一个读者不能出现两条 status0 的记录——不能在未归还的情况下重复借同一本书。MySQL 不支持“部分唯一索引”没法直接写一条 UNIQUE 约束覆盖“只对 status0 生效”这种场景。常见做法也不是硬加索引而是把检查放进借书事务先查该读者是否存在 status0 且 book_id 相同的记录有就拒绝再用事务保证整个检查插入的原子性。这个方案的 SQL 写法会在第四章展开。还有一种更贴近真实图书馆的设计book 表只存书目信息额外建一张 book_item 表每一行代表一本具体的物理书有自己的馆藏编号和状态。借书操作变成“把某一本书的 item 状态改成借出”天然不存在“同一本书重复借”的问题也不用维护 total/available 两个数字。课设里我用的是前一种汇总库存模式因为建表少、查询直观如果指导老师要求“贴近实际业务”就改成后者多一张表、多一两个 JOIN但业务模型更严谨。2.3 验证设计的三个联表查询写错 JOIN 类型会在答辩时暴露表建完后先别急着写界面用三条 SQL 验证关系是否正确。第一条规定“某读者当前借了哪些书”第二条规定“某本书的借阅历史和当前可借数”第三条规定“逾期未还清单”。-- 查询读者 20210001 当前借了哪些书 SELECT b.title, b.author, br.borrow_time, br.due_time FROM borrow br JOIN reader r ON br.reader_id r.id JOIN book b ON br.book_id b.id WHERE r.reader_no 20210001 AND br.status 0; -- 查询某本书的借阅历史 SELECT r.reader_no, r.name, br.borrow_time, br.return_time, br.status FROM borrow br JOIN reader r ON br.reader_id r.id JOIN book b ON br.book_id b.id WHERE br.book_id 1 ORDER BY br.borrow_time DESC; -- 逾期未还清单 SELECT r.reader_no, r.name, b.title, br.borrow_time, br.due_time, DATEDIFF(CURDATE(), br.due_time) AS overdue_days FROM borrow br JOIN reader r ON br.reader_id r.id JOIN book b ON br.book_id b.id WHERE br.status 0 AND br.due_time NOW() ORDER BY overdue_days DESC;三条查询都用 INNER JOIN因为借阅记录里的 book_id 和 reader_id 在外键约束下必然存在对应数据用 LEFT JOIN 反而会让“借阅记录对应到空读者”这种脏数据混进来。逾期查询里“br.due_time NOW()”判断的是时间点不要用“CURDATE() br.due_time”这种日期比较否则当天到期的晚上还书会被算成第二天。这三条 SQL 跑通后表结构基本就立住了。接下来是选技术栈和搭工程这决定你后面几周写代码的体验。3. 技术栈与连接层Java Swing MySQL 的课设路线以及连接池要不要上3.1 选型Swing、JavaWeb、PHP 怎么选图书管理系统在数据库课程设计里的技术路线有几种我见过最多的是 Java Swing MySQL写起来直接一个 JDBC 类就能跑通全部机房演示不需要装 Tomcat。JavaWebServlet JSP MySQL适合课程名称偏“Web 开发”的班级但本地要多配一套 Tomcat答辩现场环境出问题的概率更大。PHP MySQL 写起来最快但很多学生不熟悉 PHP 的语法出了错不好查。技术路线上手成本演示环境答辩风险Java Swing MySQL低本机直接跑双击 jar 或 IDE 启动界面土但数据库逻辑好讲Servlet JSP MySQL中需要 Tomcat端口和路径易出错容易被追问 Web 容器和会话PHP MySQL低需要 Apache/Nginx PHP 环境环境配置翻车率高C# WinForm SQLServer低Windows 本机但 SQLServer 安装重如果学校只教过 MySQL会被问数据库迁移我的建议是哪门语言熟就用哪条数据库课设不是 Web 课设不靠框架加分。选好后所有精力放在表设计和事务上别为了“炫技”去引入 Redis 或 MongoDB老师一眼就知道这不是数据库课程设计的考察点。3.2 工程骨架与 JDBC 工具类把连接配置和业务代码分开工程结构不需要复杂按“视图层、数据访问层、模型层、工具类”四块分目录越多老师越觉得你有工程意识。参考结构src/ ├── Main.java // 启动入口 ├── db/DBUtil.java // 数据库连接工具 ├── dao/BookDao.java // 图书表操作 ├── dao/ReaderDao.java ├── dao/BorrowDao.java ├── model/Book.java ├── model/Reader.java ├── model/Borrow.java └── view/LoginFrame.java // 登录界面后续跳转主界面连接配置单独放一个 db.properties不要把数据库地址和密码硬编码在 Java 类里。这样换数据库或改密码时不用重新编译答辩时被问“如果数据库换到另一台机器怎么办”也能直接回答“改配置就行”。public class DBUtil { private static String url; private static String username; private static String password; static { try (InputStream in DBUtil.class.getResourceAsStream(/db.properties)) { Properties props new Properties(); props.load(in); url props.getProperty(jdbc.url); username props.getProperty(jdbc.username); password props.getProperty(jdbc.password); // 显式注册驱动兼容 MySQL 5.x 的旧连接串写法 Class.forName(com.mysql.cj.jdbc.Driver); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, username, password); } }jdbc.urljdbc:mysql://localhost:3306/library_db?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue jdbc.usernameroot jdbc.password123456URL 里的参数每个都有用。characterEncodingutf8mb4 解决中文乱码serverTimezoneAsia/Shanghai 防止数据库时间比本地时间少 8 小时。allowPublicKeyRetrievaltrue 是 MySQL 8.0 用 caching_sha2_password 认证时第一次连接必需的不加会报“Public Key Retrieval is not allowed”这个坑我帮别人排查过好几次。Driver 类名注意是 com.mysql.cj.jdbc.Driver旧版的 com.mysql.jdbc.Driver 在 MySQL 8 下会打警告。3.3 连接池不是必须但值得写Druid 最小配置和一个常见误区课程设计的数据量很小直接用 DriverManager 每次 new 一个连接完全跑得动。但连接池值得写因为它能在答辩时展示你理解“连接生命周期”这个概念。我用的是阿里 Druid配置比 HikariCP 直观。driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/library_db?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/ShanghaiuseSSLfalse usernameroot password123456 initialSize3 minIdle3 maxActive10 maxWait60000public class DBManager { private static DruidDataSource dataSource; static { try (InputStream in DBManager.class.getResourceAsStream(/druid.properties)) { Properties props new Properties(); props.load(in); dataSource new DruidDataSource(); dataSource.setDriverClassName(props.getProperty(driverClassName)); dataSource.setUrl(props.getProperty(url)); dataSource.setUsername(props.getProperty(username)); dataSource.setPassword(props.getProperty(password)); dataSource.setInitialSize(Integer.parseInt(props.getProperty(initialSize, 3))); dataSource.setMaxActive(Integer.parseInt(props.getProperty(maxActive, 10))); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }initialSize 是启动时就建立的连接数maxActive 是连接池最多能同时打开的连接数maxWait 是拿不到连接时最长等待毫秒数。课设阶段 initialSize3、maxActive10 足够。用了连接池之后最大的误区是“close 变成了归还连接”。很多人用连接池后忘记在 finally 里 close最后把连接池耗空报 Too many connections。这个坑单独放到第五章讲。4. 把增删改查跑通图书管理系统的三个关键功能和一组事务代码4.1 添加图书PreparedStatement 与参数校验的位置数据库课程设计的“增删改查”里添加图书是第一个要写完整的功能。网上很多教程直接用字符串拼接 SQL这在课设里能跑但老师问“如何防止 SQL 注入”时就会卡壳。正确写法是用 PreparedStatement参数用 ? 占位由 JDBC 驱动负责转义。public int insert(Book book) throws SQLException { if (book null || book.getTitle() null || book.getTitle().isBlank()) { throw new IllegalArgumentException(书名不能为空); } if (book.getTotal() 0 || book.getAvailable() 0) { throw new IllegalArgumentException(库存数量不能为负数); } String sql INSERT INTO book (isbn, title, author, publisher, category_id, total, available) VALUES (?, ?, ?, ?, ?, ?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { ps.setString(1, book.getIsbn()); ps.setString(2, book.getTitle()); ps.setString(3, book.getAuthor()); ps.setString(4, book.getPublisher()); if (book.getCategoryId() null) { ps.setNull(5, Types.INTEGER); } else { ps.setInt(5, book.getCategoryId()); } ps.setInt(6, book.getTotal()); ps.setInt(7, book.getAvailable()); ps.executeUpdate(); try (ResultSet keys ps.getGeneratedKeys()) { if (keys.next()) { return keys.getInt(1); } } return 0; } }参数校验放在 DAO 层而不是界面层是因为 DAO 层之外的调用方比如以后加了一个批量导入 Excel 的功能也会走这个方法。total 和 available 的初始值在正常录入时是相等的但如果有“书籍上架前先登记总数、再分批入库”的业务这两个值就可以不同。4.2 借书是两条更新的组合事务、行锁和回滚借书功能是所有课设里最值得认真写的部分。表面上是 INSERT 一条借阅记录实际顺序是查读者是否超量查图书是否可借扣减 available插入借阅记录。这四个步骤中间任何一步失败前面都不能生效否则会出现“库存扣了但借阅记录没插上”或反过来“记录插了但库存没扣”的脏数据。这就是事务的典型场景。public void borrow(int bookId, int readerId) throws SQLException { Connection conn DBUtil.getConnection(); try { conn.setAutoCommit(false); // 1. 锁定读者行串行化对同一读者的并发借书 String sqlReader SELECT max_borrow FROM reader WHERE id ? FOR UPDATE; int maxBorrow; try (PreparedStatement ps conn.prepareStatement(sqlReader)) { ps.setInt(1, readerId); try (ResultSet rs ps.executeQuery()) { if (!rs.next()) { throw new SQLException(读者不存在); } maxBorrow rs.getInt(max_borrow); } } // 2. 统计当前未还数量 String countSql SELECT COUNT(*) FROM borrow WHERE reader_id ? AND status 0; int borrowingCount; try (PreparedStatement ps conn.prepareStatement(countSql)) { ps.setInt(1, readerId); try (ResultSet rs ps.executeQuery()) { rs.next(); borrowingCount rs.getInt(1); } } if (borrowingCount maxBorrow) { throw new SQLException(已达到最大借阅数量); } // 3. 锁定图书行并检查可借库存 String sqlBook SELECT available FROM book WHERE id ? FOR UPDATE; int available; try (PreparedStatement ps conn.prepareStatement(sqlBook)) { ps.setInt(1, bookId); try (ResultSet rs ps.executeQuery()) { if (!rs.next()) { throw new SQLException(图书不存在); } available rs.getInt(available); } } if (available 0) { throw new SQLException(图书已全部借出); } // 4. 扣减库存带 available 0 条件防并发超借 String updateBook UPDATE book SET available available - 1 WHERE id ? AND available 0; int rows; try (PreparedStatement ps conn.prepareStatement(updateBook)) { ps.setInt(1, bookId); rows ps.executeUpdate(); } if (rows 0) { throw new SQLException(库存已被抢占请重试); } // 5. 插入借阅记录应还时间 借出时间 30 天 String insertBorrow INSERT INTO borrow (book_id, reader_id, borrow_time, due_time, status) VALUES (?, ?, NOW(), DATE_ADD(NOW(), INTERVAL 30 DAY), 0); try (PreparedStatement ps conn.prepareStatement(insertBorrow)) { ps.setInt(1, bookId); ps.setInt(2, readerId); ps.executeUpdate(); } conn.commit(); } catch (SQLException e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(true); conn.close(); } }这段代码里的 FOR UPDATE 是行锁把读者行和图书行锁住让两个并发借书请求不能同时读到同一份可用库存避免超借。课设里讲解“为什么用 FOR UPDATE”是个很好的加分点。UPDATE 语句里带“available 0”再加受影响行数判断是第二重保险。finally 里的“conn.setAutoCommit(true)”很多人会漏。如果连接是借来的事务结束后恢复自动提交是必须的否则下一次拿到同一连接时事务边界就乱了。还有一点注意setAutoCommit(false) 必须在 try 之前调用如果放进 try 里一旦第一步就抛异常conn 回滚时可能还没开启事务。4.3 逾期统计DATEDIFF 在还书场景的准确用法逾期天数可以在 SQL 里算也可以在 Java 里算。如果只是展示统计报表SQL 里直接用 DATEDIFF 最省事如果要算逾期费并且打印到凭条上我建议在事务方法里算因为 SQL 的 DATEDIFF 比较的是日期部分会把“当天到期当天还”也算成 0 天规则更可控。SELECT br.id, r.name AS reader_name, b.title AS book_title, br.borrow_time, br.due_time, DATEDIFF(CURDATE(), br.due_time) AS overdue_days FROM borrow br JOIN reader r ON br.reader_id r.id JOIN book b ON br.book_id b.id WHERE br.status 0 AND br.due_time NOW() ORDER BY overdue_days DESC;还书方法的逻辑刚好反着先查借阅记录再算逾期天数更新借阅状态后回补库存。借阅记录的状态更新一定要带“WHERE id ? AND status 0”防止同一张借阅单被重复点击还书导致库存多加一次。public ReturnResult returnBook(int borrowId) throws SQLException { Connection conn DBUtil.getConnection(); try { conn.setAutoCommit(false); String querySql SELECT book_id, due_time FROM borrow WHERE id ? AND status 0 FOR UPDATE; int bookId; LocalDateTime dueTime; try (PreparedStatement ps conn.prepareStatement(querySql)) { ps.setInt(1, borrowId); try (ResultSet rs ps.executeQuery()) { if (!rs.next()) { throw new SQLException(借阅记录不存在或已归还); } bookId rs.getInt(book_id); dueTime rs.getTimestamp(due_time).toLocalDateTime(); } } long overdueDays Duration.between(dueTime, LocalDateTime.now()).toDays(); if (overdueDays 0) { overdueDays 0; } try (PreparedStatement ps conn.prepareStatement( UPDATE borrow SET status 1, return_time NOW() WHERE id ? AND status 0)) { ps.setInt(1, borrowId); ps.executeUpdate(); } try (PreparedStatement ps conn.prepareStatement( UPDATE book SET available available 1 WHERE id ?)) { ps.setInt(1, bookId); ps.executeUpdate(); } conn.commit(); return new ReturnResult(borrowId, overdueDays); } catch (SQLException e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(true); conn.close(); } }Duration.between(...).toDays() 是向下取整比如到期时间是昨天现在是今天凌晨算出来可能是 0 天。这不算 bug是规则问题。答辩时你要能说出“按自然日计算还是按整 24 小时计算我们系统采用了哪种”——别小看这个细节很多老师就爱问边界日期。5. 课设避坑指南五个让图书管理系统当场翻车的高频陷阱5.1 外键约束删不掉删除分类或图书总报约束失败现象界面上点“删除”时程序报错错误信息里有“Cannot delete or update a parent row: a foreign key constraint fails”。原因分类表被图书表的 category_id 外键引用图书表又被借阅表引用只要存在一条借阅记录相关图书就不能硬删。解决两个方向。一是改成逻辑删除给分类表和图书表加 is_deleted 字段TINYINT DEFAULT 0删除操作变成 UPDATE is_deleted 1查询时统一带“AND is_deleted 0”。二是物理删除前先删子表记录这会连带删掉借阅历史不适合需要保留日志的课设。常见做法是用逻辑删除答辩时还能顺带讲“为什么不能直接删历史借阅记录是审计数据”。5.2 中文乱码界面上显示的“计算机”变成“????”现象数据库里存的中文正常Java 界面里显示乱码或者反过来程序里写入数据库后变成问号。原因三种情况最容易混在一起。连接 URL 没带 characterEncodingutf8mb4数据库库表字符集是 latin1IDE 控制台的编码不是 UTF-8。解决先统一三处字符集。建库用“CREATE DATABASE library_db DEFAULT CHARSET utf8mb4”连接 URL 带上 useUnicodetruecharacterEncodingutf8mb4IDE 的 VM 参数加“-Dfile.encodingUTF-8”。如果表已经建错用“ALTER TABLE book CONVERT TO CHARACTER SET utf8mb4;”补救。排查顺序建议先用 Navicat 直接看库里数据确认数据库端正常后再查 Java 端避免两头猜。5.3 逾期天数差一天到期当天还书算不算逾期现象同一本书上午还和下午还逾期天数一个是 1 天一个是 0 天或者完全相反。原因DATEDIFF 按日期边界计算只比较“哪天”不比较“几点”。如果借书时间包含时分秒而应还时间也是时分秒那“到期当天”这个说法本身就有两种规则。解决提前定规则并写进代码注释。我一般用“超过应还时间点即算逾期按自然日向下取整”的规则即 Duration.toDays() 的结果为正数才收逾期费0 或负数都不收。这个规则要写进系统说明文档答辩时主动讲出来老师会觉得你考虑过边界。5.4 程序连不上表报“Table doesnt exist”但 Navicat 里明明能看到现象Navicat 打开同一个数据库能看到 book 表程序查询却说表不存在。原因最常见的是程序连的库不是你以为的库。MySQL 里大小写规则在 Linux 和 Windows 上不同Linux 下表名区分大小写Windows 不区分。如果建表脚本写的“Book”而查询写的“book”Windows 上没事换到 Linux 的服务器上就报错。解决统一用小写表名SQL 里字段名也全部小写。另外在 JDBC 连接成功之后执行一句“SELECT DATABASE()”确认当前库名很多同学本地装了两个 MySQL 实例程序连的 3306 端口和 Navicat 连的根本不是同一个库这种问题最容易让人怀疑人生。5.5 Too many connections运行一会儿就报连接数超限现象系统刚启动正常点几次借书还书后报“Too many connections”。用连接池时也偶发。原因写 DAO 时只关了 ResultSet 和 Statement忘了关 Connection。用 DriverManager 直连时连接用完不关就会一直占用用连接池时Connection.close() 是归还连接而不是关闭物理连接不调用 close 就等于把池里的连接全部占死。解决所有 DAO 方法都改成 try-with-resourcesConnection 放在 try 小括号里就能自动关闭。连接池场景下还可以在 Druid 的配置文件里加一条 testOnBorrowtrue保证每次拿到的连接可用。排查时用“SHOW PROCESSLIST;”看连接状态能直观看到哪些连接是 Sleep 状态一直没还。6. 答辩前的自测与演示数据三组用例和两段必会 SQL6.1 三组必测用例答辩现场最容易出的问题不是功能没有而是演示顺序乱了。我习惯提前把系统按三类用例过一遍每一类都有明确的预期结果。用例类型操作步骤预期结果正常借还选一本书点借出再点归还借出时 available 减一归还时 available 加一借阅记录 status 变为 1超量拒绝用一个达到 max_borrow 上限的读者继续借书系统提示“已达到最大借阅数量”不插入新记录逾期处理先跑逾期清单再对一条逾期记录点还书清单显示 overdue_days还书后返回逾期天数并扣回库存第一组用例随便做第二组和第三组才是让系统显得完整的关键。如果第二组只能靠改数据库数据才能复现说明你借书代码里漏了超量检查趁答辩前赶紧补上。6.2 演示数据与两段必会 SQL演示数据要故意构造一条逾期记录否则老师问“逾期逻辑在哪看”时你会现场造数据。推荐直接用下面这组 SQL 初始化INSERT INTO category (name) VALUES (计算机), (文学); INSERT INTO book (isbn, title, author, publisher, category_id, total, available) VALUES (9787111213826, 深入理解计算机系统, 兰德尔·E·布莱恩特, 机械工业出版社, 1, 3, 1), (9787020022688, 围城, 钱锺书, 人民文学出版社, 2, 2, 2); INSERT INTO reader (reader_no, name, type, max_borrow) VALUES (20210001, 张三, 1, 5), (20210002, 李四, 1, 5); INSERT INTO borrow (book_id, reader_id, borrow_time, due_time, return_time, status) VALUES (1, 1, NOW(), DATE_ADD(NOW(), INTERVAL 30 DAY), NULL, 0), (2, 2, DATE_SUB(NOW(), INTERVAL 40 DAY), DATE_SUB(NOW(), INTERVAL 10 DAY), NULL, 0);第二条借阅记录是故意做的逾期数据40 天前借出应还时间是 10 天前状态仍为 0。演示时先跑逾期清单能看到李四逾期 10 天再点还书系统会提示逾期天数并回补库存。老师现场让你“写一条 SQL 查逾期读者”是最高频的提问背熟这一段SELECT r.reader_no, r.name, COUNT(*) AS borrow_count FROM borrow br JOIN reader r ON br.reader_id r.id WHERE br.status 0 AND br.due_time NOW() GROUP BY r.reader_no, r.name;如果老师把题改成“查某本书被谁借过”SQL 换成SELECT r.name, br.borrow_time, br.return_time, br.status FROM borrow br JOIN reader r ON br.reader_id r.id JOIN book b ON br.book_id b.id WHERE b.title 深入理解计算机系统;答辩前我会把这两条 SQL 亲手跑一遍再对着系统把正常借还和逾期还书的流程各走一次整个过程不让代码报一个异常。这个动作帮我避过不少次现场翻车。数据库课程设计说到底考的是“你对自己建的表和写的事务有没有把握”把背熟的 SQL 换成自己真正理解的东西老师问多深都不虚。希望帮到你。本文还有配套的精品资源点击获取
返回列表