JavaWeb课程设计:图书管理系统从建库到答辩全攻略
发布时间:2026/9/28 15:07:55来源:尧图网络
简介湖科大javaweb课程设计图书管理系统项目包围绕MyBatis、JSP、Servlet、jQuery、ECharts与Bootstrap等核心技术完整呈现一个基于MVC分层架构的Web应用开发全过程适合高校JavaWeb课程设计、期末项目及入门学习者参考。资源共179个文件压缩包仅4.44MB涵盖43个编译类文件、28个JSP页面、32个XML配置、20个JS脚本及18个CSS样式另有14个Java源文件、SQL数据库脚本、jar依赖库等目录结构清晰便于按模块研读。目前已有2158人学习项目内容覆盖图书搜索、借阅归还、借阅统计图表与响应式界面等典型功能既能帮助理解Servlet控制器与JSP视图的协作机制也可参考MyBatis持久层映射、ECharts数据展示及Bootstrap布局的具体实现是快速走通JavaWeb开发流程的实用素材。1. 课程设计不是“会动的 PPT”先看清图书管理系统要交什么湖科大的 javaweb 课程设计里图书管理系统是出镜率最高的一道题。网上能搜到黑马javaweb笔记、开源源码、头歌实训答案里的 JSP 片段但真正到了提交前那一周很多人发现自己下载的“现成项目”压根跑不起来。这个课程设计的本质不是做出一个能点按钮的页面而是把 Servlet、JSP、MySQL、Tomcat 这条完整链路串成一个闭环并且在答辩时能讲清楚每一步为什么这么写。这篇笔记写给马上要交课设的同学也写给想用最短路径把项目跑通的人。我会按课程设计的常规要求给你一份能落地的数据库脚本、IDEA 运行 javaweb 项目的配置、核心功能代码以及五条最容易翻车的踩坑记录。2. 把数据库这张底牌打好图书管理系统的四张表设计与初始化 SQL2.1 为什么按“管理员 / 读者 / 图书 / 借阅”拆四张表写代码之前先把表定好后面能少改一半。课程设计阶段最常见的问题是把所有字段塞进一张表借阅记录直接写在 book 表里增加两个字段。这样演示时看起来能跑但老师追问“一个读者借了三本书怎么存”就会卡住。按职责拆分是常规做法admin 表管管理员登录user 表存读者book 表存图书库存与可借数量borrow 表存每一条借阅记录。借阅本质上是一个多对多关系的中转表一本书可以被多个读者借过一个读者也可以借多本书。拆开之后有两个实际好处。第一统计好写想知道一本书被借过多少次一句 select count(*) from borrow where book_id? 就出来了不需要在 book 表里翻各种冗余字段。第二容易演示权限管理员和读者走两个登录入口进入系统后看到的功能不同这是答辩时现成的“项目亮点”。如果你的课程设计文档里只写了“图书管理系统”五个字按管理员端加读者端的常规结构来做基本不会偏离要求。2.2 完整建表 SQL字段长度、字符集与边界下面这套脚本我一般直接丢到 Navicat 或 MySQL Workbench 里执行字符集统一 utf8mb4避免书名里出现特殊符号时报错。MySQL 5.7 和 8.0 都兼容这份脚本但连接驱动的写法有区别这个留到第 3 章讲。CREATE DATABASE IF NOT EXISTS library DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE library; DROP TABLE IF EXISTS borrow; DROP TABLE IF EXISTS book; DROP TABLE IF EXISTS user; DROP TABLE IF EXISTS admin; CREATE TABLE admin ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL COMMENT md5 加密后存储, real_name VARCHAR(50) COMMENT 管理员姓名 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, real_name VARCHAR(50), phone VARCHAR(20), status TINYINT DEFAULT 1 COMMENT 1 正常0 禁用 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE book ( id INT PRIMARY KEY AUTO_INCREMENT, isbn VARCHAR(20) COMMENT ISBN 可重复同一本书有多册, title VARCHAR(100) NOT NULL, author VARCHAR(50), publisher VARCHAR(100), category VARCHAR(30), total_count INT DEFAULT 1 COMMENT 馆藏总数, available_count INT DEFAULT 1 COMMENT 当前可借数量, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE borrow ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, book_id INT NOT NULL, borrow_time DATETIME DEFAULT CURRENT_TIMESTAMP, due_time DATETIME COMMENT 应还时间默认借期 30 天, return_time DATETIME COMMENT 实际归还时间, status TINYINT DEFAULT 0 COMMENT 0 借出中1 已归还, CONSTRAINT fk_borrow_user FOREIGN KEY (user_id) REFERENCES user(id), CONSTRAINT fk_borrow_book FOREIGN KEY (book_id) REFERENCES book(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; INSERT INTO admin (username, password, real_name) VALUES (admin, MD5(123456), 系统管理员); INSERT INTO user (username, password, real_name, phone) VALUES (stu001, MD5(123456), 张同学, 13800000000); INSERT INTO book (isbn, title, author, publisher, category, total_count, available_count) VALUES (978-7-111-00000-0, Java 核心技术, 张三, 机械工业出版社, 编程语言, 5, 5), (978-7-121-00000-0, MySQL 必知必会, 李四, 电子工业出版社, 数据库, 3, 3);这份脚本里有三个边界要提前知道。第一user 是 MySQL 关键字直接写 CREATE TABLE user 会报语法错误所以表名必须用反引号包起来后面所有涉及 user 表的 SQL 都建议带反引号。第二available_count 必须小于等于 total_count这个约束要靠代码控制数据库本身不会帮你检查后面借书事务里会专门做校验。第三borrow 表的外键是物理外键删除被借阅过的书会被外键挡住这是第 5 章第一个坑建议先保留外键课设演示时别删有借阅记录的书。2.3 初始化数据与删除策略避免每次重跑脚本报错课程设计期间一定会反复改表、重跑脚本。为了让重跑不难受脚本里把 DROP TABLE 放在最前面并且按外键依赖倒序删除先删 borrow再删 book最后删 admin。如果反着删会在删 book 时被外键拦住弹出一长串错误这是新手最常遇到的“玄学”。如果你不想删数据可以把 DROP TABLE IF EXISTS 换成 DELETE 语句但实际操作中课程设计阶段直接重跑更省事不用担心丢什么重要数据。初始化数据里管理员和读者的密码都写成了 MD5(123456)比明文存密码更符合答辩时的“安全意识”提问。注意 MD5 只是课程设计级别的演示真实系统要加盐哈希。加初始数据的另一个作用是老师第一次打开你的系统时有现成账号可用而不是对着空数据库发呆。答辩演示就按这两个账号走admin / 123456 进管理端stu001 / 123456 进读者端。3. 用 IDEA 运行 JavaWeb 项目的配置Artifact、Tomcat 与依赖落地3.1 课程设计的技术线怎么选ServletJSP 还是 Spring Boot搜“javaweb项目完整案例”时结果一半是 JSPServlet一半是 Spring Boot。如果你正在上 javaweb 这门课建议主线用 ServletJSP。理由很实际课程大纲讲的是 Servlet、JSP、JDBC、Tomcat答辩老师也是按这套体系出题。直接上 Spring Boot开发是快了但老师问“Servlet 生命周期”时你容易接不住而且很多课程设计任务书里明确写了要求使用 JSP。如果你已经系统学过 Spring Boot也可以拿它做但要把登录拦截、数据源配置这些点讲透别让老师觉得你在炫技。技术线定下来之后依赖不是越多越好。最小可跑的课设只要四样东西Servlet API、JSP API、JSTL、MySQL 驱动。前两个由 Tomcat 提供不需要打进项目后两个要放在 WEB-INF/lib 目录下。很多人下载的源码缺 lib 目录一打开 IDEA 全是红色报错就是依赖没落地。JSTL 是 JSP 页面用 c:forEach 渲染列表必需的库没有它页面一访问 list 就报 500。3.2 IDEA 运行 JavaWeb 项目的配置Artifact 与 Tomcat 部署IDEA 里跑 javaweb 项目核心是三个地方配置一致Project Structure 里的 Facet 与 Artifact、Run Configuration 里的 Tomcat Server、浏览器访问的 Application context。我按步骤走一遍假设你本机已经装好 Tomcat 9。新建工程File → New → Project → Java Enterprise勾选 Web Application版本选 4.0。配置 ArtifactFile → Project Structure → Artifacts → 点 → Web Application Exploded。课设选 Exploded 而不是 War因为 Exploded 支持热部署改 JSP 不用重启 Tomcat。配置 TomcatRun → Edit Configurations → 点 → Tomcat Server → Local。Server 页选择本机 Tomcat 解压目录JRE 选 JDK 8 或 11。部署项目在同一个配置窗口切到 Deployment 页签点 → Artifact选择刚才那个带 _war_exploded 的 ArtifactApplication context 改成 /book。启动点击右上角运行按钮默认打开 http://localhost:8080/book/index.jsp。如果看到的是 Tomcat 默认首页而不是你的页面回去检查 Deployment 里的 Application context。这里最容易翻车的是 Tomcat 版本。Tomcat 10 把包名从 javax.servlet 换成了 jakarta.servlet网上大量老代码、老教程都是 javax 开头装 Tomcat 10 后项目能编译但启动时直接报 ClassNotFoundException 或 NoClassDefFoundError。课程设计用 Tomcat 9 最稳JDK 用 8 或 11这个组合我踩坑最少。另外 IDEA 里配置本地 Tomcat 不需要手动配 CATALINA_HOME 环境变量只要在 Run Configuration 里选中它的解压目录就行。3.3 连接 MySQL 的 JDBC 工具类把写数据库这件事先固化下来项目结构里先写 util 包后面所有 DAO 都要复用它。下面这个 DBUtil 类负责加载驱动、获取连接、释放资源三件事。注意 MySQL 8.0 的驱动类名是 com.mysql.cj.jdbc.Driver老教程里的 com.mysql.jdbc.Driver 在 MySQL 8.0 下会报 ClassNotFoundException。package com.library.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class DBUtil { // 四个参数建议从 db.properties 读取课程设计直接写常量便于演示 private static final String URL jdbc:mysql://localhost:3306/library ?useSSLfalsecharacterEncodingutf8serverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD root; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(MySQL 驱动加载失败检查 WEB-INF/lib 下是否有 mysql-connector-java); } } public static Connection getConnection() throws SQLException { // 每次调用都新建连接课设够用高并发场景才需要连接池 return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { // 关闭顺序rs - stmt - conn先开后关 if (rs ! null) { try { rs.close(); } catch (SQLException ignored) {} } if (stmt ! null) { try { stmt.close(); } catch (SQLException ignored) {} } if (conn ! null) { try { conn.close(); } catch (SQLException ignored) {} } } }URL 里三个参数值得背下来。useSSLfalse 关闭 SSL 握手否则 MySQL 8.0 会输出大量告警日志连接速度也明显变慢characterEncodingutf8 解决中文乱码serverTimezoneAsia/Shanghai 是 MySQL 8.0 的必填项不加会报时区错误。USER 和 PASSWORD 改成你本机 MySQL 的实际账号这里写成 root/root 只是示例。如果你用的是 MySQL 5.7驱动类名换回 com.mysql.jdbc.Driver 也能跑URL 去掉 serverTimezone 也可以但统一用 8.0 的写法更省心换电脑部署时不用改代码。3.4 用“分层包名”给答辩留好说法包结构建议这样分com.library.entity 放实体类com.library.dao 放 JDBC 数据访问com.library.service 放业务逻辑com.library.servlet 放前端控制器com.library.util 放工具类。很多同学把 JDBC 代码全写在 Servlet 里一个类两三百行功能能跑但答辩时老师翻代码会皱眉。分层不是形式主义它的实际作用是出问题时知道去哪一层找代码。数据库字段改了只动 entity 和 dao页面改了只动 JSP业务规则改了只动 service。实体类不要偷懒不写。Book.java 里每个字段对应 book 表一列写私有属性再生成 getter、setter。写 DAO 时你会发现实体类能把 ResultSet 的一行映射成一个对象避免在 JSP 页面里到处写 rs.getString()。这个习惯在后续 Spring Boot、MyBatis 里是同一套思路课设写完不浪费。4. 把核心功能串起来登录、分页列表、借还与新增图书的代码落点4.1 登录是门面ServletSession 怎么把权限撑起来登录要解决的不仅是“查得到密码”还有“登录后不能绕过页面直接访问”。我的做法是登录成功后把用户名和角色放进 Session再写一个过滤器拦截所有非登录请求。下面这段 LoginServlet 是常规写法代码量不大但答辩时一定会被问到。package com.library.servlet; import com.library.entity.User; import com.library.service.UserService; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; WebServlet(/login) public class LoginServlet extends HttpServlet { private final UserService userService new UserService(); Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 编码设置必须放在读取参数之前否则用户名里的中文直接乱码 request.setCharacterEncoding(UTF-8); String username request.getParameter(username); String password request.getParameter(password); User user userService.login(username, password); if (user ! null) { HttpSession session request.getSession(); session.setAttribute(loginUser, user); // 用重定向避免刷新页面时重复提交表单 response.sendRedirect(request.getContextPath() /book/list); } else { request.setAttribute(error, 用户名或密码错误); // 转发到登录页错误信息能显示在页面上 request.getRequestDispatcher(/login.jsp).forward(request, response); } } }两个容易被问到的细节。第一doPost 里先 request.setCharacterEncoding(UTF-8)而且必须在读取第一个参数之前执行否则 post 提交的中文用户名会乱码。第二登录成功后用 sendRedirect 而不是 forward因为 forward 会导致 F5 刷新时重复提交表单这是课程设计演示时很尴尬的翻车现场。Service 层 login 方法内部做两步把密码用 MD5 加密后去库里比对返回 null 表示失败返回 User 对象表示成功。建议再写一个过滤器对所有 /book/* 的请求检查 Session 里有没有 loginUser没有就重定向到 login.jsp这一步能让你的项目比同组同学多一个“权限控制”的亮点。4.2 分页列表pageNo/pageSize 两个参数背后的 SQL 与前端逻辑图书列表不写分页数据一多页面又长又卡写了分页答辩时能多讲三分钟。分页的核心是下面这段 DAO 方法注意它同时处理了查询关键字和 LIMIT 参数。public ListBook findPage(int pageNo, int pageSize, String keyword) throws SQLException { String sql SELECT * FROM book WHERE title LIKE ? LIMIT ?, ?; ListBook list new ArrayList(); // 计算偏移量第一页 pageNo1offset 从 0 开始 int offset (pageNo - 1) * pageSize; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, % keyword %); ps.setInt(2, offset); ps.setInt(3, pageSize); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Book book new Book(); // 一行一列对应实体类字段实际代码可以抽成一个 bookRow 私有方法 book.setId(rs.getInt(id)); book.setTitle(rs.getString(title)); book.setAuthor(rs.getString(author)); book.setAvailableCount(rs.getInt(available_count)); list.add(book); } } } return list; }分页参数控制有三个点。pageSize 课程设计里固定 8 或 10 都行写 5 显得页面太窄pageNo 前端传过来时要做越界处理小于 1 按 1 算大于总页数按最大页算keyword 必须用 PreparedStatement 的 ? 和 LIKE 拼接直接拼字符串会踩 SQL 注入的坑答辩老师几乎一定会问这个。总页数需要再查一条 count(*)前端 JSP 用 JSTL 遍历页码按钮当前页高亮的逻辑在 4.4 里一起写。提示pageNo 和 pageSize 最好由 Servlet 统一转换成 int 并做边界校验后再传给 DAO不要直接在 DAO 里对字符串强转否则用户请求参数里写负数会导致 SQL 偏移量异常。4.3 新增和借还图书事务边界怎么划借书是整个系统里唯一必须用事务的地方。流程是先检查书可借数量大于 0然后扣减可借数量再插入一条借阅记录。这三步必须在一个事务里否则会出现“借阅记录插进去了但库存没扣”的脏数据。下面是 BorrowService 里借书方法的骨架。public boolean borrowBook(int userId, int bookId) throws SQLException { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 关闭自动提交手动控制事务边界 // 扣减可借数量where 里的 available_count 0 是最后的防线 String updateSql UPDATE book SET available_count available_count - 1 WHERE id ? AND available_count 0; try (PreparedStatement ps conn.prepareStatement(updateSql)) { ps.setInt(1, bookId); if (ps.executeUpdate() 0) { conn.rollback(); return false; // 影响行数为 0说明没借到 } } // 插入借阅记录应还时间用 SQL 里的 DATE_ADD 计算避免 Java 与 MySQL 时间格式不一致 String insertSql INSERT INTO borrow (user_id, book_id, due_time) VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 30 DAY)); try (PreparedStatement ps conn.prepareStatement(insertSql)) { ps.setInt(1, userId); ps.setInt(2, bookId); ps.executeUpdate(); } conn.commit(); return true; } catch (SQLException e) { if (conn ! null) { try { conn.rollback(); } catch (SQLException ignored) {} } throw e; } finally { if (conn ! null) { conn.setAutoCommit(true); // 还原自动提交避免连接复用后状态错乱 conn.close(); } } }这个方法的逻辑顺序是刻意的先扣库存扣不到就回滚返回 false扣到了再插入记录全部成功才 commit。值得注意的一点是执行 UPDATE 之后要判断影响行数因为 where 条件里带了 available_count 0如果库存已经为 0executeUpdate 返回 0JDBC 本身不会报错。归还流程正好反过来先 update borrow set return_timeNOW(), status1再 update book set available_count available_count 1同样用事务包住。新增图书相对简单用 PreparedStatement 执行一条 INSERT字段从页面表单取唯一要校验的是 total_count 和 available_count 都必须是正整数前端后端各校验一次更稳妥。4.4 JSP 页面的 JSTL 与 EL课设页面怎么减少重复代码JSP 页面里尽量不要写 Java 小脚本。用 EL 表达式 ${book.title} 取值用 JSTL 标签 c:forEach 循环列表代码量少一半可读性也好。页面头部统一引入这两行% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % % page contentTypetext/html;charsetUTF-8 languagejava %列表页的核心循环体大概长这样c:forEach 遍历 request 里的 bookList表格每一行渲染一本书。分页部分用 c:if 判断上一页、下一页是否可点c:forEach 生成页码再给当前页的 a 标签加一个高亮的 class。这里最省事的细节是页码直接用后端传过来的 totalPage 循环不要在前端用 JS 反复计算总页数否则很容易出现“最后一页只有一条数据但页码显示正常”的边界 bug。黑马javaweb笔记里对 JSTL 和 EL 的讲解很全照着敲一遍比自己从零写快得多。但注意不要把网上 JSP 片段原样复制进项目至少要改表名、字段名和包名否则代码风格不统一老师一眼能看出是拼的。JSTL 标签库需要 WEB-INF/lib 下有 jstl.jar 和 standard.jar没有这两个 jar页面一访问 list 就报 500这也是导入别人项目后最常见的第一个报错。5. 课程设计最容易翻车的 5 个坑404、乱码、驱动与依赖血泪排查5.1 启动后浏览器 404Artifact 没挂到 Tomcat 上现象Tomcat 能启动控制台也显示 Server startup但访问 http://localhost:8080/book/index.jsp 返回 404访问 http://localhost:8080/ 却能打开 Tomcat 默认首页。原因IDEA 里 Tomcat 的 Deployment 页签没加 Artifact或者 Application context 和实际访问路径不一致。Tomcat 本身是好的只是项目没发布上去。这个问题出现频率最高很多人以为是代码写错了其实是配置没到位。解决打开 Run → Edit Configurations → 选中当前 Tomcat Server → Deployment 页签 → 点 → Artifact → 选你的 _war_exploded。然后在 Server 页确认 On Update Action 选 Update classes and resources这样改 JSP 不用重启。最后把 Application context 统一改成 /book重启后按 /book 开头访问。判断是否生效看启动日志里有没有一行类似 Deployment of web application archive ... has finished有就说明挂载成功。5.2 连接数据库报错驱动、时区、SSL 三个原因一起查现象项目跑起来后一登录就报 500控制台出现 Communications link failure 或 Access denied for user也可能直接报 ClassNotFoundException。原因三种原因混在一起时最头疼。Communications link failure 大概率是 URL 缺 serverTimezone或者 MySQL 8.0 没加 useSSLfalseAccess denied 是账号或密码错误ClassNotFoundException 是 mysql-connector-java 的 jar 没进 WEB-INF/lib或者驱动类名写成了老版的 com.mysql.jdbc.Driver。解决按顺序排查。第一步确认 lib 目录里有 jarIDEA 里通过 Project Structure → Modules → Dependencies 能看到它。第二步URL 照抄第 3 章那份useSSLfalse、characterEncodingutf8、serverTimezoneAsia/Shanghai 一个都不能少。第三步先用 Navicat 或命令行连一下数据库确认账号密码正确。第四步核对驱动类名MySQL 8.0 用 com.mysql.cj.jdbc.DriverMySQL 5.7 老驱动用 com.mysql.jdbc.Driver。这条之所以说是“玄学”是因为一次报错日志里可能同时出现两三个原因修完一个还剩两个。5.3 页面中文乱码三个地方编码必须统一现象列表页里中文书名全是问号或者登录输入中文用户名后查不到数据也可能刷新一遍页面乱码形式还不一样。原因JSP 页面编码、请求编码、数据库连接编码、数据库表编码四个环节只要有一个不一致就乱。常见的是 JSP 文件里漏了 pageEncoding或者 request.setCharacterEncoding 写晚了再或者建表时用了默认的 latin1。Tomcat 8.5 之后服务器层面的 URIEncoding 默认是 UTF-8但请求 body 里的编码依然要靠 Servlet 自己设置。解决第一JSP 首行写全 % page contentTypetext/html;charsetUTF-8 languagejava pageEncodingUTF-8 %第二确认 JSP 文件本身以 UTF-8 保存IDEA 右下角可以查看第三数据库连接 URL 里带 characterEncodingutf8第四第 2 章建表统一用了 utf8mb4如果表已经建成 latin1执行 ALTER TABLE book CONVERT TO CHARACTER SET utf8mb4; 可以补救。如果这些都检查完还乱再查 Tomcat 的 server.xml 里 URIEncoding。最省事的手段是写一个 CharacterEncodingFilter 拦截所有请求强制 UTF-8这也是正规项目的标准做法放在课设里属于加分项。5.4 导入别人的源码后报错一片JDK、Tomcat、lib 三连查现象从网盘下载的“javaweb项目完整案例”IDEA 打开后整个项目飘红或者能编译但启动时报缺少 Tomcat 相关类。原因源码用的 JDK、Tomcat 版本和你本机不一致。最常见的两种源码基于 Tomcat 10 的 jakarta 命名空间写你在 Tomcat 9 上跑源码的 WEB-INF/lib 没有一起打包下载下来只有一个空目录。解决导入源码后先做三件事。第一Project Structure → Project 里把 SDK 改成 1.8 或 11第二Project Structure → Modules → Dependencies 里添加 Tomcat 库点 → Library → Tomcat第三检查 WEB-INF/lib 下是否有 mysql 驱动、jstl、standard 这些 jar没有就手动下载对应版本放进去。如果项目是 Maven 结构就在 pom.xml 里确认依赖坐标然后 Maven → Reload。很多新手把 jar 加到 classpath 后发现没生效最后才发现没放到 Tomcat 能看到的 WEB-INF/lib 下。5.5 删除图书时外键报错借阅表里的“历史包袱”怎么处理现象管理员想删一本书点击删除后页面报错 Cannot delete or update a parent row: a foreign key constraint fails。原因borrow 表里有这本书的借阅记录物理外键禁止直接删父表记录。这其实是外键在保护你的数据但课设阶段管理端确实需要能删书两个需求就冲突了。解决推荐做逻辑删除不真正 DELETE。在 book 表加一个 status 字段1 表示上架0 表示下架删除操作变成 UPDATE book SET status0 WHERE id?列表查询默认只查 status1。这样既不会和外键冲突也能在答辩时解释“图书从馆藏下架但保留借阅历史”比写复杂的外键解除 SQL 更安全。如果你硬要物理删就先 DELETE FROM borrow WHERE book_id? 再 DELETE FROM book WHERE id?但这样借阅历史就没了老师问“这本书之前被谁借过”时少了关键数据。外键不是坏东西别因为嫌麻烦就把外键全部删掉保留它并在答辩时说出“用逻辑删除避开外键冲突”这个设计决策比“我把外键取消了”强很多。6. 答辩前给项目上个保险两分钟演示清单与 Spring Boot 改造思路6.1 答辩演示清单从启动到功能过一遍答辩前半小时按这个顺序过一遍不要跳着点。第一用 IDEA 冷启动一次项目确认 Tomcat 不报错数据库服务已启动。第二用 admin / 123456 登录管理端新增一本测试书然后到列表页搜索到它。第三切换到读者端用 stu001 / 123456 登录借走刚新增的书回管理端看到库存减一。第四演示归还库存加回。第五最后演示分页和搜索顺便把“库存不足借不了”也演示一次这反而是亮点。这个顺序把登录、新增、借还、搜索、库存变化串在一条故事线上老师不需要你解释就能跟上逻辑。容易翻车的是借书后忘记演示库存变化或者数据库里没有可借数据页面空白。所以演示前务必检查 book 表里至少有三条以上数据且有一条 available_count 大于 0。关闭浏览器重开一次再登录确认 Session 过滤器的拦截效果真实存在这个动作能堵住“绕开登录直接访问”的追问。6.2 给课设留一条升级路径Servlet 版怎么迁到 Spring Boot如果你准备拿这个项目当简历作品或者课程设计允许用 Spring Boot迁移路径是清晰的。JDBC 换成 spring-boot-starter-jdbc 的 JdbcTemplateServlet 换成 ControllerFilter 换成 HandlerInterceptor页面暂时还是 JSP加一个 spring-boot-starter-tomcat 就能兼容。数据库表完全不用改DAO 里的 SQL 可以直接挪进 JdbcTemplate事务那部分改用 Transactional 注解比手写 setAutoCommit 简单得多。只迁登录和借阅两块就能讲清楚 MVC 和依赖注入不必整个重写。我的习惯是课设提交前把数据库脚本、IDEA 启动截图、核心代码注释三样单独打包一份答辩时老师要看哪一块都能秒开。这个项目做完之后你会发现借书事务和分页这两段代码在 Spring Boot、MyBatis、甚至换一门语言时思路都是通用的。希望这些踩过的坑能帮你少熬两个通宵。本文还有配套的精品资源点击获取
网站建设高端定制企业官网