ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JSP+Servlet+MySQL图书馆借阅系统源码详解与工程化改造

JSP+Servlet+MySQL图书馆借阅系统源码详解与工程化改造 简介一份面向Java Web初学者的完整毕业设计资料包内含基于JSP的图书馆借阅系统源代码与配套论文。系统覆盖用户登录、图书查询、借书还书、会话管理、权限控制等核心功能完整展现了JSP页面交互、Servlet业务处理、关系型数据库存取、会话跟踪与安全防护等Web开发关键环节适合需要完成类似课题或系统学习JSPServletMySQL开发流程的读者参考。压缩包共114个文件以57个jsp页面为界面层包含jar依赖与class文件构成的业务逻辑层以及SQL脚本和数据库文件组成的数据层另有30个gif和6个jpg等界面素材整体仅1.64MB层次分明导入开发环境即可运行。配套论文系统讲解了数据库表设计、后台交互逻辑、异常处理与性能优化思路结合源码能帮助读者完整跑通项目并理解从需求分析、功能设计到编码实现和测试维护的全过程。该资料包目前已有128人学习下载适合作为毕业设计题目的直接参考也适用于高校课程项目实训。1. 拿到这份JSP图书馆借阅系统源码先看懂它解决了什么问题打开这个压缩包里面是一套典型的Java Web课程设计/毕业设计工程基于JSP的图书馆借阅系统附带源代码和论文。这类项目在计算机专业的课设、毕设里出现频率极高技术栈固定为JSP Servlet JavaBean MySQL跑在Tomcat上。它要解决的是图书馆日常业务的信息化问题——管理员维护图书和读者档案读者在线查书、借书、还书系统记录每一笔借阅流水并处理超期罚款。这套东西值不值得你投入时间取决于你的身份。如果你是准备交课设/毕设的学生这份源码的价值在于它把「需求分析→数据库设计→前后端编码→论文撰写」整条链路补齐了而且技术栈老派但完整老师不会在答辩时追问太深如果你是刚接触Java Web的初级开发这套源码是一份很好的「三层结构」教学样本能看清JSP页面怎么和Servlet、JavaBean协作比看零散教程更成体系。但也要有个心理准备它不是一份拿来就能上生产的环境代码里的设计思路停留在课堂层面数据库、安全性、异常处理都有明显的年代感需要自己动手加固。下面我就按「系统拆解 → 核心代码 → 部署运行 → 踩坑排查 → 工程化改造」这条线把它的原理和落地路径讲透。2. 从借书到还书拆出功能模块与六张核心数据表2.1 两种角色两条流程管理员与读者的权限边界打开任意一套图书馆借阅系统的设计文档功能都逃不出两个角色、两条流程。读者角色能看到图书检索、个人借阅信息展示、密码修改管理员角色多出图书增删改、读者档案管理、借阅/归还操作、罚款登记和统计报表。这两条流程在数据上是一体的但权限边界必须清晰否则会出现读者直接访问admin页面改数据的安全漏洞。判断一套源码设计得好不好第一眼看它怎么处理角色权限。常见做法是在JSP页面顶部用session里的用户类型做判断或者在Servlet里做拦截器。课设级别的代码多用前者——在每个页面的头部写一段if(session.getAttribute(userType)null || !admin.equals(...))的重定向逻辑简单粗暴但够用。生产级系统会用Filter统一拦截这个改造点我在第6章会说。2.2 六张核心数据表从admin到borrow的外键关系数据库设计是这套系统的地基。常见的设计是六张表管理员表(admin)、读者表(reader)、图书表(book)、图书分类表(category)、借阅记录表(borrow)、罚款记录表(fine)。其中category和book是一对多reader和borrow是一对多book和borrow也是一对多。罚款记录可以挂在borrow表下也可以独立成表看你要不要统计「逾期未还」和「已缴罚款」两类数据。这里有个设计选择比表数量更关键借阅记录表里存的是「当前未还的借阅」还是「所有历史借阅」。课设源码一般只存当前借阅还书时直接删除对应记录但更合理的做法是加一个return_time字段还书时更新这个字段而不是删除记录这样能保留历史借阅流水统计借阅量和超期罚款时才有数据可用。我见过不少课设代码在这块偷懒导致「超期罚款」功能形同虚设因为还书后记录没了算不出谁曾经超期。2.3 建表SQL字段类型、默认值与外键约束下面是这套系统最核心的表结构我用MySQL 5.7的语法写兼容大部分课设环境。注意每个字段的注释和选型原因。-- 管理员表 CREATE TABLE admin ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 管理员ID自增主键, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录名唯一索引避免重复, password VARCHAR(64) NOT NULL COMMENT 密码课设通常存明文上线前必须改MD5盐, real_name VARCHAR(50) COMMENT 真实姓名用于页面欢迎语展示, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT管理员表; -- 读者表 CREATE TABLE reader ( id INT PRIMARY KEY AUTO_INCREMENT, reader_no VARCHAR(20) NOT NULL UNIQUE COMMENT 读者证号借书时扫描这个号, password VARCHAR(64) NOT NULL, name VARCHAR(50) NOT NULL, phone VARCHAR(20), email VARCHAR(100), max_borrow_count INT DEFAULT 5 COMMENT 最大可借数量业务规则参数, current_borrow_count INT DEFAULT 0 COMMENT 当前已借数量每次借/还都要更新, status TINYINT DEFAULT 1 COMMENT 1-正常 0-冻结冻结后不能借书, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT读者表; -- 图书分类表 CREATE TABLE category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 分类名如计算机/文学/历史, sort_order INT DEFAULT 0 COMMENT 排序权重越小越靠前 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT图书分类表; -- 图书表 CREATE TABLE book ( id INT PRIMARY KEY AUTO_INCREMENT, isbn VARCHAR(20) COMMENT ISBN号注意同一本书多副本时ISBN相同, name VARCHAR(100) NOT NULL, author VARCHAR(50), publisher VARCHAR(100), category_id INT COMMENT 外键关联category表, price DECIMAL(10,2) COMMENT 定价DECIMAL避免浮点误差, total_count INT DEFAULT 1 COMMENT 馆藏总量, available_count INT DEFAULT 1 COMMENT 可借数量借出时减1归还时加1, location VARCHAR(50) COMMENT 馆藏位置比如A区3排2层, cover_image VARCHAR(255) COMMENT 封面图路径存相对路径不存二进制, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (category_id) REFERENCES category(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT图书表; -- 借阅记录表 CREATE TABLE borrow ( id INT PRIMARY KEY AUTO_INCREMENT, reader_id INT NOT NULL COMMENT 外键关联reader表, book_id INT NOT NULL COMMENT 外键关联book表, borrow_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 借出时间, due_time DATETIME NOT NULL COMMENT 应还时间一般借出30天, return_time DATETIME DEFAULT NULL COMMENT 实际归还时间NULL表示未还, status TINYINT DEFAULT 0 COMMENT 0-借出中 1-已归还 2-已超期, FOREIGN KEY (reader_id) REFERENCES reader(id), FOREIGN KEY (book_id) REFERENCES book(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT借阅记录表; -- 罚款记录表 CREATE TABLE fine ( id INT PRIMARY KEY AUTO_INCREMENT, borrow_id INT NOT NULL COMMENT 关联borrow表一条借阅最多产生一条罚款, reader_id INT NOT NULL, fine_amount DECIMAL(10,2) DEFAULT 0 COMMENT 罚款金额按超期天数×每日费率算, is_paid TINYINT DEFAULT 0 COMMENT 0-未缴 1-已缴, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (borrow_id) REFERENCES borrow(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT罚款记录表;这段SQL的关键点在于borrow表的return_time字段和status字段——这正是我前面说的「保留历史流水」的设计而不是还书时删记录。book表的available_count字段是可用库存每次借书需要校验它大于0并且要同时更新reader.current_borrow_count和book.available_count这三个操作必须在同一个事务里完成否则会出现读者借了5本但库存没减的脏数据。2.4 为什么用DATETIME而不是TIMESTAMP以及外键的取舍字段类型选择的坑比较隐蔽。borrow_time、due_time用DATETIME因为DATETIME的范围是1000-9999年而TIMESTAMP只到2038年并且在MySQL 5.7里TIMESTAMP会自动更新容易产生「我没改这条记录但它时间变了」的诡异现象。金额字段用DECIMAL(10,2)而不用FLOAT/DOUBLE二进制浮点存金额会累积误差0.10.2不等于0.3这种问题在计算罚款时就是大事故。外键约束课设源码里经常懒得写。不写外键确实能让插入数据更随意但代价是程序里少了数据库层面的最后一道防线——比如删除一个仍有借阅记录的读者如果没有外键约束孤儿数据就这么躺进去了。我建议建表保留外键但在删除操作上走「逻辑删除」路线——就是给reader表加个status字段标记冻结而不是物理删除行这样避免外键阻塞删除操作的尴尬。3. 用JSPServletJavaBean跑通借阅主流程关键代码与参数说明3.1 三层结构为什么在课设里长这样JSP当视图Servlet当控制器JavaBean当模型标题里写的「JSP基于WEB的图书馆借阅系统」90%的课设都长成一个模样JSP页面直接写Java代码片段的叫Model 1引入Servlet做控制的叫Model 2MVC。这份源码大概率是Model 2因为它要配论文论文里必须画MVC架构图。理解这三层的关系是读懂所有代码的前提。JSP负责显示和收集输入它本质是一个被Tomcat编译成Servlet的HTML模板% %和${}表达式把Java对象的值渲染到页面上。Servlet负责接收请求、调用业务逻辑、决定跳转到哪个JSP它不直接写HTML。JavaBean准确的说是DAO类负责和数据库打交道封装JDBC操作。请求的走向是浏览器提交表单/链接 → Tomcat根据web.xml的映射关系找到对应Servlet → Servlet调用DAO完成数据库CRUD → Servlet把结果放进request/session域 → forward到JSP渲染页面。这种结构的优点是职责清晰课设答辩时好讲缺点是JSP里依然可能残留大量Java代码比如循环输出表格用% for(...) %而不是JSTL。如果你看到的源码是这种Mixed风格不用怀疑它就是典型的课设产物。3.2 数据库连接配置db.properties DBUtil封装连接配置是所有模块的地基。课设源码通常会有一个jdbc.properties或db.properties文件配好驱动、URL、账号密码再写一个DBUtil类提供getConnection()静态方法。下面是常见写法注意参数的坑。package com.library.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBUtil { // 加载驱动只需要一次放静态块里 static { try { Class.forName(com.mysql.jdbc.Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { // 注意MySQL 8.x的驱动类名改成了com.mysql.cj.jdbc.Driver // 且URL必须带serverTimezone参数否则报时区错误 String url jdbc:mysql://localhost:3306/library?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai; String user root; String password 123456; return DriverManager.getConnection(url, user, password); } public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { // 关闭顺序rs - ps - conn倒序关闭 try { if (rs ! null) rs.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (ps ! null) ps.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn ! null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }这里的三个参数决定你的连接能不能通。第一useUnicodetruecharacterEncodingutf8是中文不乱码的前提但光有它还不行还得配合JSP页面的pageEncoding和Tomcat的URIEncoding这个坑我在第5章详细说。第二useSSLfalse是为了避免MySQL 8的SSL握手警告刷屏。第三serverTimezoneAsia/Shanghai是MySQL 8独有要求不加就报The server time zone value Öйú±ê׼ʱ¼ä这样的乱码错误。课设源码里常见的偷懒是把连接参数硬编码在每个DAO的getConnection()里改密码要全文搜索替换。如果你打算改造第一步就是抽成db.propertiesDBUtil这是收益最高的重构。更好的方案是用连接池比如Druid或C3P0但课设答辩时连接池不是必考点所以源码里通常没有。3.3 借书核心逻辑事务、库存校验与超期计算借书是这套系统最核心的业务代码里必须做三件事校验读者和他能借的额度、校验图书库存、扣减库存并生成借阅记录。下面这段是BorrowServlet的doPost方法的核心逻辑我按「先校验、再扣减、事务提交」的顺序写。package com.library.servlet; import com.library.dao.BorrowDao; import com.library.dao.BookDao; import com.library.dao.ReaderDao; import com.library.entity.Borrow; import com.library.entity.Reader; import com.library.entity.Book; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; import java.io.IOException; import java.sql.SQLException; import java.util.Calendar; import java.util.Date; WebServlet(/borrowBook) public class BorrowServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); // 从表单获取读者ID和图书ID注意前端要先通过接口查证这两个ID真实存在 int readerId Integer.parseInt(request.getParameter(readerId)); int bookId Integer.parseInt(request.getParameter(bookId)); ReaderDao readerDao new ReaderDao(); BookDao bookDao new BookDao(); BorrowDao borrowDao new BorrowDao(); try { // 第1步校验读者状态和额度 Reader reader readerDao.findById(readerId); if (reader null || reader.getStatus() 0) { request.setAttribute(errorMsg, 读者不存在或已被冻结); request.getRequestDispatcher(/error.jsp).forward(request, response); return; } if (reader.getCurrentBorrowCount() reader.getMaxBorrowCount()) { request.setAttribute(errorMsg, 该读者已借满无法继续借阅); request.getRequestDispatcher(/error.jsp).forward(request, response); return; } // 第2步校验图书库存 Book book bookDao.findById(bookId); if (book null || book.getAvailableCount() 1) { request.setAttribute(errorMsg, 图书不存在或已无库存); request.getRequestDispatcher(/error.jsp).forward(request, response); return; } // 第3步构造借阅记录应还时间 当前时间 30天 Borrow borrow new Borrow(); borrow.setReaderId(readerId); borrow.setBookId(bookId); Calendar cal Calendar.getInstance(); cal.add(Calendar.DAY_OF_MONTH, 30); borrow.setDueTime(cal.getTime()); // 第4步三个写操作在一个事务里完成 // DBUtil里新增beginTransaction/commit/rollback三个方法 // 注意不能用默认的autoCommittrue否则第2步更新成功第3步失败时数据就对不上了 boolean success borrowDao.borrowBook(borrow); if (success) { response.sendRedirect(request.getContextPath() /listMyBorrow?readerId readerId); } else { request.setAttribute(errorMsg, 借阅失败请检查输入); request.getRequestDispatcher(/error.jsp).forward(request, response); } } catch (SQLException e) { // 生产环境要记日志课设至少打印堆栈 e.printStackTrace(); request.setAttribute(errorMsg, 系统异常请联系管理员); request.getRequestDispatcher(/error.jsp).forward(request, response); } catch (NumberFormatException e) { request.setAttribute(errorMsg, 参数格式错误); request.getRequestDispatcher(/error.jsp).forward(request, response); } } }这段代码里最关键的是Calendar.getInstance()计算应还时间的逻辑。cal.add(Calendar.DAY_OF_MONTH, 30)是按自然日累加30天而不是一个月——这两者在1月31号这种日期上会产生差异按自然日加30天会落到3月2日按月份加一个月则可能落到2月最后一天。业务上通常用自然日计费所以这个写法是对的。事务保证是另一个重点。课设源码里如果borrowDao.borrowBook()内部是一次性执行多条SQL且没有关闭自动提交那它八成没做事务会出现「扣了库存但借阅记录没生成」的幽灵数据。你要改造的话在BorrowDao.borrowBook()方法里手动conn.setAutoCommit(false)执行完更新库存和插入记录两条SQL后再提交任何一个失败就conn.rollback()。3.4 jsp个人信息展示页面读者登录后怎么看到自己的借阅列表读者登录后跳转的页面就是热搜词里的「jsp个人信息展示页面」。这个页面的作用不只是显示姓名和证号还要带出当前借阅列表和超期状态。JSP里显示列表的常见做法是用JSTL的c:forEach标签遍历但课设源码有时直接用% for(int i0; ilist.size(); i) { %。下面这段用JSTL注意items属性对应Servlet塞进request的键名。% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html headtitle我的借阅信息/title/head body h2欢迎${sessionScope.readerName}/h2 p当前已借 ${sessionScope.reader.currentBorrowCount} 本最多可借 ${sessionScope.reader.maxBorrowCount} 本/p table border1 cellpadding6 tr th书名/thth借出时间/thth应还时间/thth状态/th /tr c:forEach items${requestScope.borrowList} varb tr td${b.bookName}/td td${b.borrowTime}/td td${b.dueTime}/td td c:choose c:when test${b.status 0}借阅中/c:when c:when test${b.status 2}已超期/c:when c:otherwise已归还/c:otherwise /c:choose /td /tr /c:forEach /table /body /html这里有个隐蔽的坑${b.dueTime}直接输出Date对象时格式是Wed Jun 30 23:59:59 CST 2024这种英文格式非常难看。要在页面顶部用fmt标签格式化fmt:formatDate value${b.dueTime} patternyyyy-MM-dd /或者Servlet里直接把Date转成String塞进request。课设源码里如果没做格式化你可以自己补上这只是页面层的小改动。3.5 图片坐标定位与封面展示JSP输出img标签时的实用处理热搜词里有个「jsp图片如何对坐标定位」在图书管理模块里对应的是封面图上显示或者封面的位置摆放。JSP里图片定位并不特殊本质还是HTMLCSS的工作JSP只是负责把图片路径动态输出。常见场景是图书列表页每个封面图大小不一导致表格行高不齐。解决办法是在img标签上设定固定宽高并用style控制定位。img src${pageContext.request.contextPath}/upload/${book.coverImage} stylewidth:80px;height:100px;object-fit:cover;position:relative;top:4px;left:2px; alt${book.name}封面 /object-fit:cover让图片按比例填充且不拉伸变形这是处理尺寸不一致的封面图最简单的方式比在Java代码里做缩略图省事得多。${pageContext.request.contextPath}是动态获取项目根路径这样部署时改了项目名图片也不会裂。4. 把项目部署到Tomcat和MySQL最小可运行步骤与验证清单4.1 环境选型JDK 1.8 Tomcat 8.5 MySQL 5.7是课设的黄金组合这套系统跑起来需要的环境是JDK 1.8、Tomcat 8.5或9.0、MySQL 5.7或8.0、Eclipse IDE for Java EE或IntelliJ IDEA。为什么选这个组合而不是更新版本因为JDK 1.8仍然是Java Web课设的兼容性之王Tomcat 8.5对应Servlet 3.1规范支持WebServlet注解IDE里配置也简单MySQL 5.7的驱动类名是com.mysql.jdbc.Driver不用像8.0那样处理时区参数。有一个选型细节要特别注意如果你用的是JDK 9以上版本Tomcat 8.5会报IllegalAccessError这是模块化Java的兼容性问题。所以踩坑前先确认JDK版本不要用JDK 17直接跑老项目。Tomcat本身就是完全免费的WEB服务器下载解压就能用不需要额外授权这正是它成为课设首选的原因。4.2 把zip里的项目导入IDEEclipse和IDEA的两种导法压缩包解压后通常是一个标准的Web工程目录结构src/Java源码、WebContent/或webapp/JSP、WEB-INF、静态资源、sql/数据库脚本、doc/论文。Eclipse导法是File → Import → Existing Projects into Workspace选到解压目录即可。IDEA导法略有不同IDEA的New → Project from Existing Sources会弹出类型选择选Import project from external model里的Eclipse但IDEA对Eclipse项目的编译输出路径识别偶尔会错乱需要在Project Structure → Modules → Paths里把输出目录改成classes。导入常见的翻车现场是WEB-INF/lib下的jar包没有出现在Build Path里。Eclipse的Deploy Assembly和IDEA的Artifacts配置不一致会导致一个结果本地编译没问题但启动Tomcat后ClassNotFound。解决方法是检查Project Properties → Deployment Assembly里有没有WebContent这个源目录没有就Add → Folder加上。4.3 建库建表与初始化数据用命令行还是用Navicat拿到SQL脚本后最关键的一步是执行它。推荐先用命令行把库建起来再用Navicat做可视化确认因为命令行能看清楚脚本本身有没有报错。# 登录MySQL输入密码后进入mysql命令行 mysql -u root -p # 创建数据库注意字符集必须和连接串一致 CREATE DATABASE library DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 使用该数据库并执行源码自带的sql脚本 USE library; SOURCE /解压路径/sql/library.sql; # 验证表是否建好 SHOW TABLES;执行成功后还要检查脚本里有没有插入初始数据——管理员账号、测试读者、几本测试图书。很多课设的sql脚本只建表不插数据登录页面会变成一个黑匣子你不知道用户名密码是什么只能去脚本里找INSERT INTO admin语句或者自己补一条。常见做法是补一个admin/123456的账号进去这不算偷懒是让系统跑起来的最短路径。-- 如果脚本里没有管理员初始数据手动插入一条 INSERT INTO admin (username, password, real_name) VALUES (admin, 123456, 系统管理员);这里又回到了前面说的密码明文问题。课设阶段123456明文是常态答辩老师不会揪着说但如果这套系统要真正用起来密码存储改成MD5盐是第6章要做的事现在先记住这个点。4.4 启动与验证从Tomcat日志到页面操作的五步清单项目跑起来后的验证不能只看首页能不能打开。Tomcat启动本身是黑匣子启动成功和部署成功是两回事下面这个验证清单帮我揪出过多次「首页能开但登录就500」的隐藏问题。第一步在Eclipse/IDEA里配置Tomcat ServerAdd Local Tomcat并选择Tomcat 8.5的安装目录第二步把项目Add到Server的Deployed Resources里第三步启动Tomcat看控制台日志出现INFO: Deployment of web application archive ... has finished in X ms才算部署成功第四步浏览器访问http://localhost:8080/项目名/index.jsp能看到首页且没有500报错第五步用管理员账号登录接着操作一遍「添加图书 → 添加读者 → 借书 → 还书」全流程。只有第五步全部走通这个项目才算真的在你的机器上活着。验证过程中如果发现页面样式全丢了CSS/图片不加载第一反应检查项目访问路径里的上下文名是不是和部署名一致。Tomcat默认访问路径是http://localhost:8080/项目名/如果你把项目部署名改成了library但JSP里用的是/webapps/原来的名字全站资源都会404。5. 部署与运行避坑笔记四条高频翻车现场与排查思路5.1 404错误Servlet访问路径与web.xml映射对不上现象启动Tomcat后首页正常但点「借书」「管理图书」等带Servlet的链接浏览器地址栏的URL是对的却返回404。原因课设源码里Servlet有两种配置方式——WebServlet注解和web.xml里的servlet-mapping两种混用时经常出现对不上的情况。特别是从网上下载的代码IDE自动补全或手动修改web.xml时漏了url-pattern导致访问请求找不到对应的Servlet。另一个常见原因是我在4.4里说的部署名问题链接里写死了旧的项目名。解决先看请求URL比如/library/borrowBook再从web.xml里搜索borrowBook这个url-pattern确认servlet-name和servlet-class成对存在且class完整包名正确。如果是注解方式检查类上WebServlet(/borrowBook)的路径和JSP里表单的action是否大小写一致——/borrowBook和/borrowbook是两个不同的映射Linux/Mac上尤其敏感Windows上Tomcat不区分大小写反而会掩盖这个问题。5.2 ClassNotFoundExceptionmysql驱动jar包没进WEB-INF/lib现象Tomcat启动不报错但第一次点登录按钮就500日志里出现ClassNotFoundException: com.mysql.jdbc.Driver。原因驱动jar包的classloader问题。IDE里Build Path引用了mysql-connector-java但WEB-INF/lib目录下没有这个jar。Tomcat运行时加载类用的是Web应用的classloader不会去读IDE的Build Path配置所以必须把jar物理拷贝到WEB-INF/lib下。解决从Maven仓库或已安装的lib目录找到mysql-connector-java-5.1.x.jar对应MySQL 5.7或mysql-connector-java-8.0.x.jar对应MySQL 8.0复制到WebContent/WEB-INF/lib/下然后Project → Clean清理重新部署。注意不要同时放5.x和8.x两个版本的驱动classloader加载顺序不确定可能拿到旧版本去连MySQL 8导致连接失败。5.3 中文乱码从页面表单到数据库的全链路排查现象页面上输入中文书名提交后数据库里存的是????或者页面显示时中文全部变问号。原因乱码是这条链路上任何一环不一致导致的JSP页面保存编码 → Tomcat请求解码编码 → 数据库表字符集 → JDBC连接串的characterEncoding。常见的问题是表用了latin1字符集、JSP页面没写pageEncodingUTF-8、或者连接串缺少characterEncodingutf8。解决按顺序检查三个地方。第一MySQL表字符集改成utf8mb4ALTER TABLE book CONVERT TO CHARACTER SET utf8mb4;第二JSP文件第一行确认是% page contentTypetext/html;charsetUTF-8 pageEncodingUTF-8%第三Tomcat的server.xml里Connector加URIEncodingUTF-8处理GET请求路径里的中文参数。这三步全做了乱码会当场消失。如果你用的是Tomcat 8URIEncoding默认就是UTF-8这一步可以跳过把重点放在前两步。5.4 MySQL 8连接失败时区参数和驱动版本导致的黑匣子现象本地MySQL是8.x版本驱动用的还是5.x的jar启动报错Communications link failure或The server time zone value Öйú±ê׼ʱ¼ä is unrecognized。原因MySQL 8改了默认认证插件为caching_sha2_password旧的5.x驱动不兼容这个插件同时8.x要求客户端必须显式设置时区参数。这两个问题叠加让你看到的是「连接失败」这个笼统结果里面真正的原因被压在了堆栈深处。解决把驱动换成mysql-connector-java-8.0.33连接串改成jdbc:mysql://localhost:3306/library?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai。同时创建数据库用户时注意认证插件执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码;让老JDBC代码能顺利握手。5.5 Tomcat端口被占用8080被进程占住的快速定位现象Tomcat启动日志最后一行出现Port 8080 required by Tomcat v8.5 Server at localhost is already in use启动立即失败。原因要么上一次Tomcat没被正常关闭Eclipse里小红点停止是强杀进程还在要么系统里别的应用占用了8080。解决先确认占用者再决定杀进程还是改端口。Windows下用netstat -ano | findstr 8080查出PID然后taskkill /PID 那个PID /F或者选择不冲突直接改Tomcat的server.xml里Connector端口为8081。如果同时改了HTTP端口和AJP端口记住要一起改只改一个会出现启动时第二次端口占用报错。6. 把课设代码改造成能上线的Web项目三个必做的收尾动作前面五章解决的是「跑起来」这一章解决的是「敢用它」——如果你准备把这套JSP项目从答辩演示变成一个小型单位真正在用的系统以下三个改造是低成本高收益的必做项。第一个改造是密码加密与登录安全。源码里admin表和reader表的密码都是明文这是最扎眼的安全缺陷。改造时不需要引入Spring Security这种重型框架自己写一个MD5Util工具类用MessageDigest做MD5加盐哈希即可。注册和密码修改处调用这个工具类登录校验时把用户输入的密码加密后和库里的值比对。注意密码加盐不要用固定盐值每行用户的盐值用UUID随机生成存到用户表新增的salt字段里这样相同密码哈希结果也不同能防住了大部分最原始的拖库风险。配合一个LoginFilter实现统一登录拦截——所有/admin/*、/reader/*的请求都经过这个Filtersession里没有登录标记就重定向到登录页这就把「未登录直接查书」的裸奔路径堵死了这也是web服务器安全里最基本的访问控制。第二个改造是借阅清单的打印输出。热搜词里的「web页面pdf打印」正好对应这个需求——读者还书时需要签字确认管理员需要留底。最轻的方案是新增一个printBorrow.jsp页面布局调成A4宽度用浏览器的window.print()触发打印CSS里用media print隐藏导航栏和按钮只保留表格。不需要引入pdf生成库因为课设规模下没有人真的需要下载PDF文件。function printBorrowList() { // 打印前把当前页面的标题改成借阅清单打印完恢复 var originalTitle document.title; document.title 借阅清单- new Date().toLocaleDateString(); window.print(); document.title originalTitle; }第三个改造是源代码管理。解压后那堆源码如果只躺在zip里改坏了就没有后悔药了。花五分钟用git init建本地仓库写一个.gitignore忽略target/、classes/、.settings/这些IDE生成物把源码提交进去后续每修一个bug就commit一次。课设答辩时老师问「你是怎么迭代开发的」你有git log可以展示这是加分的。# 在项目根目录初始化git仓库 git init # 创建.gitignore文件忽略IDE和构建产物 echo target/ .gitignore echo .settings/ .gitignore echo .classpath .gitignore echo .project .gitignore # 首次提交 git add . git commit -m init: 图书馆借阅系统课设源码这套系统的边界也要心里有数。它没有做并发控制——两个管理员同时给同一本书办借书available_count可能出现负值它没有做分页——图书几百本时列表页会很长它没有做数据库连接池——并发一高连接数就会爆。这些都是课设源码的出厂设置不是缺陷而是你继续学习的路标。我经手过好几套类似的JSP课设源码印象最深的一次是帮一个学弟排查「借书成功但读者已借数量没变」的bug最后定位到是update reader set current_borrow_count current_borrow_count 1这条SQL的1被写成了 1把计数直接重置了。这种错误在代码review时一眼能看出来但在自己调试时往往要盯着看半天。这也是为什么我一直强调拿到源码先读核心业务的SQL别急着点运行按钮。希望帮到你。本文还有配套的精品资源点击获取
返回列表