ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Java课程设计:图书管理系统从数据库设计到借还书事务与答辩全攻略

2026/10/7 21:49:50 拓冰建站 浏览量
Java课程设计:图书管理系统从数据库设计到借还书事务与答辩全攻略 Java课程设计选图书管理系统算是每年最经典的保留题目。这个题覆盖面确实广Java基础、面向对象、JDBC操作、MySQL设计、Web前端全都串起来了但也正因为串的东西多零基础的人最容易卡在半路教程存了一堆代码抄了一屏结果一运行就报错根本不知道问题出在哪。这篇文章我会按课程设计的完整流程来写——从技术选型、表结构设计、后端借还书逻辑到前端页面、部署避坑、答辩准备尽量讲得细一点让你照着走就能做出一套能演示、能交差、还能拿高分的系统。适合第一次做Java课程设计的同学也适合代码已经写了半截但思路还有点糊想回头理一理的人。1. 课设思路拆解先想清楚再动手1.1 技术选型JavaWeb老路子为什么更适合课程设计图书管理系统开发方案有很多但课程设计场景下技术栈不是越新越好而是越“能讲清楚”越好。两种主流方案对比一下方案技术栈优点缺点AJSP Servlet JDBC MySQL原理透明分层清晰答辩容易讲代码量相对大配置偏手工BSpring Boot MyBatis MySQL开发快接近企业实践封装重初学者难解释内部原理我的建议很明确优先选择方案A。理由有两点——第一大多数学校的Java课程设计大纲要求的就是Servlet/JSP/JDBC这套东西跟着要求走最稳妥第二这套技术栈里JDBC连接、事务控制、分页查询都是自己手写的老师问到任何一步你都能对答如流。Spring Boot虽然能少写一堆代码但事务怎么控制、SQL怎么传递、连接池怎么管理都被框架包住了答不上来反而扣分。另外还有一种做法是桌面版Swing MySQL。如果学校不要求Web这么做也不是不行但管理系统听起来还是Web端更合适而且网页演示起来更直观评分的观感也会好一些。1.2 功能模块拆解从用户故事出发很多人拿到题目就开始建表写代码这是最容易翻车的做法。正确思路是先盘一遍业务场景站在使用者的角度问自己这个系统要给谁用他进来之后要做什么图书管理系统的角色很清晰就是管理员。核心业务流程也不复杂管理员登录系统身份保存到Session图书模块新增图书、编辑信息、删除图书、按书名/作者/分类模糊查询读者模块登记读者信息、修改资料、注销读者借书模块选择读者和图书生成借阅记录同时扣减库存还书模块归还图书更新记录状态回补库存计算超期罚金统计模块按分类统计藏书数量、借阅排行榜把流程走一遍你就会发现这个项目的关键不在“增删改查”而在借书和还书这两个动作。它们涉及多张表的联动任何一步出错数据就对不上。后面我会专门用一节来拆。1.3 项目目录结构分包命名有讲究结构清晰是评审老师的第一印象。建议按经典的三层架构分包src/main/java ├── com.library.entity 实体类Book, Reader, BorrowRecord ├── com.library.dao 数据访问层BookDAO, ReaderDAO, BorrowRecordDAO ├── com.library.service 业务逻辑层BookService, BorrowService ├── com.library.servlet Servlet控制层BookServlet, BorrowServlet, LoginServlet ├── com.library.util 工具类DBUtil ├── com.library.filter 过滤器EncodingFilter, LoginFilter webapp ├── index.jsp ├── book/listBook.jsp ├── book/addBook.jsp ├── reader/listReader.jsp ├── borrow/borrow.jsp分层的逻辑很简单Servlet只负责接收请求和跳转页面不写SQLDAO只负责操作数据库不写业务判断Service只负责业务规则比如判断库存够不够、读者能不能借。这样做的好处是出了问题好定位而且答辩的时候你有东西可聊——老师最喜欢问的就是“为什么分层”。你回答“为了降低耦合、方便复用”一听就是正经写过项目的。2. 数据库表结构设计三张表撑起整个系统2.1 核心表字段设计每张表都是面向业务的数据库名字就叫library_db字符集建议用utf8mb4兼容性好中文存储不出乱子。整个系统核心是三张表图书表book、读者表reader、借阅记录表borrow_record。如果要做登录再加一张管理员表admin就够了。先说图书表。以图书为单位每本书有唯一的主键但真实场景里同一种书有多本库存所以“图书信息”和“库存数量”要放在同一张表里用字段区分。我的字段设计参考如下字段类型说明idbigint主键自增isbnvarchar(20)ISBN号唯一namevarchar(100)书名authorvarchar(50)作者publishervarchar(80)出版社publish_datedate出版日期categoryvarchar(30)分类如文学/计算机pricedecimal(8,2)定价stockint当前库存可借数量total_stockint总库存入库总量locationvarchar(30)存放位置如A区-3排create_timedatetime创建时间这里有同学会问为什么要有stock和total_stock两个字段只留一个“总库存减借出数”不行吗当然可以但分开存有两个好处第一查询剩余库存不需要每次都聚合计算第二统计损耗、盘点的时候有个基准值可以对照。课程设计用不上那么复杂的盘点但这个设计思想在面试里很加分。读者表相对简单核心标识是读者编号reader_no建议设置唯一索引。字段不外乎姓名、性别、电话、邮箱、状态。状态字段很重要比如读者挂了或者有未还书没处理可以置为禁用。注意不是删除保留历史记录是管理系统的基本素养。借阅记录表是全项目的核心表。它记录一次完整的借书行为包含书的id、读者的id、借出时间、应还时间、实际归还时间、当前状态、罚金。状态字段我用整数表示0代表借出中1代表已归还2代表超期未还。这样在列表页直接判断数值切换颜色方便得很。2.2 外键和索引要克制加对地方才有效外键在课程设计里是加分项但不要滥用。我建议只在borrow_record表的book_id和reader_id上建立外键指向图书表和读者表的主键。这样在数据库层面就能保证借阅记录不会关联到不存在的书或读者体现你对数据完整性的理解。索引方面图书表的name和author建议加普通索引。因为查询场景最多的是按书名或者作者模糊查找没有索引的话数据多了会全表扫描。borrow_record表建议建一个联合索引(book_id, status)因为“查看某本书是否还有未还记录”是高频操作联合索引能直接覆盖这个查询。这里提醒一句外键和索引不是越多越好。外键会影响插入删除的性能索引会占用空间。课程设计数据量小不纠结性能把最合理的几个加上就是标准答案。2.3 建表SQL与测试数据直接可用的参考建表SQL给你一份可以直接跑的版本CREATE DATABASE IF NOT EXISTS library_db DEFAULT CHARACTER SET utf8mb4; USE library_db; CREATE TABLE book ( id BIGINT PRIMARY KEY AUTO_INCREMENT, isbn VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(100) NOT NULL, author VARCHAR(50), publisher VARCHAR(80), publish_date DATE, category VARCHAR(30), price DECIMAL(8,2), stock INT NOT NULL DEFAULT 0, total_stock INT NOT NULL DEFAULT 0, location VARCHAR(30), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_name(name) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE reader ( id BIGINT PRIMARY KEY AUTO_INCREMENT, reader_no VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(50) NOT NULL, gender TINYINT, phone VARCHAR(20), email VARCHAR(50), status TINYINT DEFAULT 1, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE borrow_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, book_id BIGINT NOT NULL, reader_id BIGINT NOT NULL, borrow_time DATETIME DEFAULT CURRENT_TIMESTAMP, due_time DATETIME, return_time DATETIME, status TINYINT DEFAULT 0, fine DECIMAL(8,2) DEFAULT 0, CONSTRAINT fk_borrow_book FOREIGN KEY (book_id) REFERENCES book(id), CONSTRAINT fk_borrow_reader FOREIGN KEY (reader_id) REFERENCES reader(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE admin ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; INSERT INTO admin(username, password) VALUES(admin, admin123);注意MySQL 8和5.7在建表语法上没有大差别但驱动配置有差别这个下一章详细讲。插入几条测试数据时尽量模拟真实场景比如两本书、两位读者、一条借出记录这样你后面调试借书还书逻辑时就有现成环境了。3. 后端核心代码从JDBC到事务控制3.1 JDBC连接工具几个关键的配置参数不管页面写得多花哨数据最终都要落到数据库里。JDBC连接这一步看似基础实际上坑特别多。一个可用的DBUtil长这样package com.library.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/library_db? useUnicodetruecharacterEncodingUTF-8 serverTimezoneAsia/ShanghaiuseSSLfalse; 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); } }有几个地方必须提醒MySQL 8以上的驱动类名是com.mysql.cj.jdbc.Driver不是老的com.mysql.jdbc.Driver。很多人用着MySQL 8却写老驱动名启动直接抛ClassNotFoundException。连接地址里一定要带serverTimezoneAsia/Shanghai否则报时区错误。useSSLfalse是关掉SSL告警课程设计本地连库用不到加密传输关掉省心。密码不要用我这里的占位符改成你自己数据库的密码。我在实操中建议做一个小封装把连接获取放在静态块里这样第一次访问类时会自动注册驱动避免在每次获取连接时重复加载。3.2 DAO层增删改查PreparedStatement是底线有的教程还在用Statement拼接SQL我强烈建议不要这么干。拼接字符串不仅麻烦而且会把系统暴露在SQL注入风险下。比如下面这种写法就是典型的反面教材// 错误示例拼接用户输入 String sql SELECT * FROM book WHERE name name ;如果name里传一个 OR 11后果不堪设想。正确做法是用PreparedStatement它相当于先把SQL模板发给数据库预编译再用参数占位符填充值用户可以输入的内容只会被当作数据不会被当作SQL执行。以新增一本图书为例public int insertBook(Book book) throws SQLException { String sql INSERT INTO book(isbn, name, author, publisher, publish_date, category, price, stock, total_stock, location) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, book.getIsbn()); ps.setString(2, book.getName()); ps.setString(3, book.getAuthor()); ps.setString(4, book.getPublisher()); ps.setDate(5, new java.sql.Date(book.getPublishDate().getTime())); ps.setString(6, book.getCategory()); ps.setBigDecimal(7, book.getPrice()); ps.setInt(8, book.getStock()); ps.setInt(9, book.getTotalStock()); ps.setString(10, book.getLocation()); return ps.executeUpdate(); } }注意到我把连接和语句放入try-with-resources结构了吗这样能自动关闭资源避免连接泄漏。课程设计虽然是小项目但这个习惯最好一开始就养成。模糊查询的写法也有讲究。推荐用CONCAT拼接通配符String sql SELECT * FROM book WHERE name LIKE CONCAT(%, ?, %) OR author LIKE CONCAT(%, ?, %);不要在Java代码里拼%而是通过setString传保持参数化查询的完整性。3.3 借书还书逻辑多表联动的精髓借书的完整业务流程是根据书ID查询图书拿到当前库存如果库存小于等于0抛出业务异常“库存不足”根据读者ID查询该读者未归还的借阅记录数量超过上限则拒绝执行扣库存SQLUPDATE book SET stock stock - 1 WHERE id ? AND stock 0插入一条借阅记录状态为借出中应还时间为当前时间加30天全部成功才提交事务任何一步失败都回滚这里最容易被忽视的就是“事务”。为什么这几步必须在一个事务里因为如果你只插入了借阅记录但扣库存的SQL失败那么系统里就会有一条“借出了书但库存没减少”的记录账实不符。反之库存扣了但记录没插上书就凭空消失了。只有把这几个操作绑成一个整体要么全部成功要么全部失败数据才不会乱。代码骨架是这样的Connection conn DBUtil.getConnection(); conn.setAutoCommit(false); try { // 1. 查询图书库存 Book book bookDAO.getById(conn, bookId); if (book null || book.getStock() 0) { throw new RuntimeException(库存不足); } // 2. 查询读者在借数量 int borrowingCount borrowDAO.countBorrowingByReader(conn, readerId); if (borrowingCount MAX_BORROW_COUNT) { throw new RuntimeException(借阅数量已达上限); } // 3. 扣减库存条件更新防止超借 int rows bookDAO.decreaseStock(conn, bookId); if (rows 0) { throw new RuntimeException(库存不足); } // 4. 插入借阅记录 borrowDAO.insert(conn, bookId, readerId); conn.commit(); } catch (Exception e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(true); if (conn ! null) conn.close(); }这里有一个并发细节值得讲一下decreaseStock里的WHERE stock 0不是画蛇添足。假如两个管理员同时借最后一本书都先查询到库存是1然后各自执行stock - 1不加条件就会把库存扣成-1。加上AND stock 0之后数据库层面就保证了只有一个人能成功另一个人更新的行数为0说明库存没了。这种“条件更新”的手法比单纯的先查后改安全得多答辩时能主动说出来老师会立刻觉得你有并发意识。还书流程相对简单但也别大意。先查借阅记录确认是“借出中”状态然后更新记录的回还时间和状态同时把图书库存加回去。超期罚金可以做成一个计算函数DATEDIFF(CURDATE(), due_time)得到超期天数再乘以每日罚金比如0.5元更新到罚金字段。罚金只算到还书当天所以放在还书操作里计算最合理。3.4 手写分页LIMIT的offset别忘了校验列表页数据一多一次性查出来全部返回给前端页面会卡内存也浪费。分页是管理系统的标配。手写分页其实就是两条核心语句-- 查询总记录数 SELECT COUNT(*) FROM book WHERE name LIKE CONCAT(%, ?, %); -- 查当前页数据 SELECT * FROM book WHERE name LIKE CONCAT(%, ?, %) LIMIT ?, ?;LIMIT后的两个问号第一个是偏移量offset第二个是每页条数size。偏移量的计算公式是offset (currentPage - 1) * size。比如每页10条查第一页就是LIMIT 0, 10查第二页就是LIMIT 10, 10。这里要强调一个前端传来的currentPage参数绝对不能直接拿来当偏移量。第一要判断是不是数字否则SQL拼接时类型转换会炸第二要判断是否为0或负数否则偏移量会变成负值。我一般这样处理int page 1; try { page Integer.parseInt(request.getParameter(page)); } catch (NumberFormatException e) { page 1; } if (page 1) page 1; int offset (page - 1) * size;回答“为什么不用框架的分页插件”这种问题时你可以说课程设计是为了理解底层原理手写分页能更清楚SQL执行过程。当然如果以后做企业项目用PageHelper之类的插件效率更高这是两手准备。4. 前端页面与交互好用比花哨更重要4.1 JSP页面布局用JSTL和EL代替小脚本JSP页面最忌讳的写法就是满屏的% %小脚本。代码全塞在HTML里既乱又难维护还容易出现转义问题。建议用EL表达式加JSTL核心标签库来展示数据。比如图书列表页的核心片段c:forEach items${pageBean.list} varbook tr td${book.id}/td td${book.isbn}/td td${book.name}/td td${book.author}/td td${book.stock}/td td a hrefBookServlet?actioneditid${book.id}编辑/a a hrefBookServlet?actiondeleteid${book.id} onclickreturn confirm(确认删除)删除/a /td /tr /c:forEach使用JSTL需要把jstl.jar和standard.jar放到WEB-INF/lib或者用Maven引入依赖。页面上引入% taglib prefixc urihttp://java.sun.com/jsp/jstl/core %就可以了。页面布局建议统一风格。做一个公共的header.jspf包含导航栏和CSS引入每个页面% include fileheader.jspf %这样整体看起来就是一套完整系统而不是东一块西一块的拼凑页。界面用Bootstrap的CDN也方便能省去手写CSS的时间把精力留给后端逻辑。4.2 借书还书页面的前后端协作借书页面输入读者编号和图书ID或ISBN点提交后走BorrowServlet。前端可以先做一层基础校验比如空值检查function submitBorrow() { var readerNo document.getElementById(readerNo).value.trim(); var bookId document.getElementById(bookId).value.trim(); if (readerNo || bookId ) { alert(读者编号和图书ID不能为空); return; } if (!confirm(确认借出这本书)) { return; } document.getElementById(borrowForm).submit(); }注意前端校验只是用户体验层面的辅助真正的校验必须放在后端Service层。前端可以绕过后端不能丢。前端JS校验用途是拦截明显错误后端校验才是安全底线。后端处理完业务后结果反馈尽量不要用alert()弹窗体验很差。更好的做法是把提示信息放到request.setAttribute(msg, 借书成功)然后在JSP页面的固定提示区域展示。同时判断msg是否以“失败”开头可以用CSS把提示文字标红。这一步细节很能反映工程素养。还书页面同样思路输入读者编号查出所有“借出中”的记录列表每条记录旁边放一个“归还”按钮。还书时提交记录ID后端执行回补库存加更新状态。4.3 体验细节决定印象分的小地方外观不需要多惊艳但几个细节一定要做到位评分老师一眼就能看出来图书列表的库存状态用颜色标识。库存为0时标红并显示“不可借”大于0时显示绿色“可借”。借阅记录的状态字段用三种颜色区分借出中是橙色已归还是绿色超期未还是红色。这种小细节不需要多少代码量但给人的感觉是“这个系统真的考虑过使用场景”。删除功能注意二次确认而且后端要判断被删除的图书是否存在未还记录。有未还记录的书不能直接删否则历史数据就悬空了。读者删除时同理有未还书或未处理罚金不能直接删除。分页导航要保留搜索条件和当前页码。很多人做分页时会遇到一个问题搜索结果在第3页点下一页之后搜索关键词丢了列表全变成空查。解决办法是在分页链接里把当前的keyword参数带上。这个坑几乎人人都会踩提前避开就是赢在起跑线。5. 部署运行与常见bug排查从一台电脑到能演示5.1 环境配置版本组合选对了少踩一半坑做课程设计建议用一套经过验证的环境组合JDK 8或11Tomcat 8.5或9MySQL 5.7或8.0IDEA社区版够用。这套组合兼容性最稳。三个关键配置点要记住MySQL驱动jar包从官网下载后放到WEB-INF/lib目录下IDEA部署时要确认这个目录被识别为Web应用的类目录。如果日志提示找不到驱动多半是jar没打进Artifact。IDEA的Artifact配置很多人不熟悉。项目配置里选择“Web Application: Exploded”然后“Build”菜单重新构建不然每次改了代码重启Tomcat还是旧版本。这个坑叫“改了没生效”不是代码问题是部署产物没更新。Tomcat启动后访问项目是http://localhost:8080/项目名/xxx这个默认上下文路径可能带一段很长的名字。可以在Tomcat配置里的Application context改成/library演示时输入短地址更好记也显得更专业。5.2 常见报错速查表和排查思路下面这个表基本覆盖了课程设计阶段九成以上的报错现象可能原因解决方案ClassNotFoundException: com.mysql.cj.jdbc.Driver驱动类名写错或驱动jar没有放入WEB-INF/lib确认MySQL版本使用对应驱动类名ClassNotFoundException: com.mysql.jdbc.DriverMySQL 8还用了旧驱动类名改成com.mysql.cj.jdbc.DriverAccess denied for user root用户名或密码不对检查DBUtil中的用户名密码Unknown database library_db数据库没建或库名拼错先执行建表SQL创建库Communications link failureMySQL服务没启动或端口不是3306启动MySQL服务检查端口The server time zone value ... is unrecognizedJDBC URL没带时区参数URL添加serverTimezoneAsia/Shanghai页面中文全部乱码JSP编码不一致或数据库连接没带字符集JSP加UTF-8URL加useUnicodetruecharacterEncodingUTF-8加CharacterEncodingFilterPort 8080 was already in use8080端口被占用关掉占用进程或修改Tomcat端口修改了代码但页面没变化IDEA部署产物没重建Build Rebuild Project重启Tomcat遇到报错的第一反应不是复制粘贴问题描述而是先看控制台异常栈的第一行异常类型再往上翻看你自己项目里的类。这一步能快速区分是环境问题、SQL问题还是业务逻辑问题。排查麻烦问题时把执行的SQL复制到Navicat里手动跑一遍能立刻确认SQL本身有没有问题。这套流程虽然朴素但比盲目改代码高效十倍。6. 答辩准备与项目扩展从“能跑”到“优秀”6.1 老师最爱问的十二个问题提前准备好答案答辩环节决定最终评分的上限。老师其实不太关心你代码里每一行是什么而是想确认两点系统是不是你自己写的以及你懂不懂背后的原理。下面这些问题出现频率极高JDBC和MyBatis有什么区别答JDBC是底层规范需要手动处理连接、结果集映射MyBatis是ORM框架封装了这些过程语句写在Mapper里。为什么用PreparedStatement而不是Statement答预编译、防止SQL注入、更安全。你的事务是怎么控制的答手动setAutoCommit(false)业务成功commit异常rollback借书涉及扣库存和插入记录必须保证原子性。库存并发扣减怎么处理答使用条件更新SQLUPDATE book SET stock stock - 1 WHERE id ? AND stock 0数据库层面控制不会超扣。这个系统满足第几范式答至少第三范式没有冗余字段借阅记录通过外键关联图书和读者。分页是怎么做的答LIMIT offset, size先查COUNT再查当前页页面参数需要校验。如果图书数据量很大分页性能如何优化答大offset会有深度分页问题可以改用子查询或“上次查到的最大ID”方式优化还可以给查询条件加索引。登录密码安全吗答课程设计里是明文生产环境必须用MD5加盐或BCrypt等算法做哈希存储。Session和Cookie理解吗答登录状态存SessionCookie用于保持会话可以提到Session跟踪机制。前端校验和后端校验有什么区别答前端校验提升体验后端校验保证安全后端不可省略。为什么不直接用框架答课程设计要求掌握底层原理JDBC/Servlet能更清晰展示请求响应和数据访问全流程。如果让你加一个新功能比如预约借书你怎么设计答先说新增表的字段再说业务流程展现出“我会思考扩展”的能力。这些问题不是要你背标准答案而是提前梳理答辩时用自己的话讲越自然越有理有据。6.2 性价比高的扩展功能建议如果时间充裕建议从下面几个方向里选一个做扩展能让课设和别人的拉开差距借阅排行榜最简单也最讨喜。一条SQL就能拿到结果SELECT book.name, COUNT(*) borrow_count FROM borrow_record r JOIN book ON book.id r.book_id GROUP BY book_id ORDER BY borrow_count DESC LIMIT 10;再写一个页面展示配个简单的柱状分布效果视觉冲击力就很强还能顺势讲一下GROUP BY和JOIN的知识。逾期罚金统计也很实用。在还书逻辑里已经计算了罚金额外做一个“罚金汇总”页面按读者分组统计未缴罚金这属于业务完整性上的补充。数据导出Excel。使用Apache POI写一个导出接口把图书列表输出到xls文件。这个功能在演示的时候会让人觉得系统很“完整”而且完全是加分项之外的惊喜。密码加密可以放在管理员登录模块。用MessageDigest做MD5加盐存储虽然实际安全和主流标准有差距但比明文强太多也显得你关注安全。如果还有精力可以尝试把系统迁移到Spring Boot MyBatis当成技术对比在答辩时展示。这不是必须的但能体现出你的学习能力。6.3 一些真心话我在指导学弟学妹做这个课设时发现最大的坑不是技术难而是总想着“一步到位”。一上来就想做一堆花哨功能结果主线的借书还书逻辑反而没调通最后演示翻车。我的建议很朴素先把核心链路跑通也就是“新增图书 → 新增读者 → 借书 → 还书”这一整条流程顺畅走完系统就已经及格了。在这个基础上再去做弹窗美化、做排行榜、做导出每一个都是加分项。做课程设计的过程其实也是整理知识体系的过程。你把它当成一个任务应付它就是一个负担你把它当成一次完整的项目实践那JDBC、事务、分层、前后端协作这些零散知识点就会在这一套系统里串成线对后面做毕业设计和面试都有实打实的帮助。希望这篇能帮你少踩几个坑也祝你答辩顺利拿个高分。