ARTICLE DETAIL

资讯详情

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

JSP图书管理系统源码实战:从环境搭建到借还书与罚款逻辑

JSP图书管理系统源码实战:从环境搭建到借还书与罚款逻辑 简介这是一套基于JSPMySQLServlet技术栈实现的图书馆图书管理系统完整源码面向正在学习Java Web开发、需要课程设计或毕业设计参考的学生与初级开发者。系统按角色划分权限管理员可完成登录、用户管理、图书管理与罚款缴纳学生可登录、借阅归还图书、查看借阅记录并修改密码游客则能浏览全部图书及详情覆盖了图书流通业务的主要环节。压缩包共112个文件约1.76MB其中45个Java文件承载Servlet、DAO与过滤器等后端逻辑24个JSP页面负责视图展示另有37个PNG与2个GIF图片资源、2个Markdown说明及1个PDF文档结构清晰、分层明确。目前已有263人学习下载。借助这套源码读者可直观理解MVC分层、登录过滤、借阅状态流转与数据库交互的实现思路适合作为二次开发与功能扩展的起点。1. 从一份 JSP 图书管理系统源码说起它到底能跑出什么很多人第一次接触 Java Web 课程设计都是从一份 JSP 图书管理系统源码开始的。你拿到手的这个压缩包里面是标准的 JSP Servlet MySQL 三层结构没有用 Spring 那一套重框架反而把最原始的请求流转暴露得很清楚。它解决的核心问题很具体管理员能登录后台管用户、管图书、处理罚款学生能注册登录、借书还书、查借阅记录、改密码游客不登录也能翻图书列表和详情。适合谁正在做课程设计、想找一个能跑通、能改、能讲清楚原理的 Java Web 入门项目的人。它不花哨但胜在每一行代码你都能看懂Servlet 怎么接请求、DAO 怎么查库、Filter 怎么拦登录全在明面上。2. 环境搭建与数据库初始化把 MySQL 和 Tomcat 先伺候好2.1 选型理由为什么这套老技术栈反而适合练手JSP Servlet MySQL 这套组合放在今天的企业开发里确实不新但作为学习载体它有不可替代的好处。Spring Boot 把太多东西自动化了你写个RestController就能返回 JSON但请求到底怎么从浏览器走到方法里、参数怎么绑定、会话怎么维持全被框架藏起来了。这份源码没有这层遮挡LoginServlet里你得自己request.getParameter自己session.setAttribute自己response.sendRedirect。每一步都是显式的出错了你能顺着调用链找到根因。另一个现实原因是环境好搭MySQL 5.7 或 8.0 都行Tomcat 8.5 或 9.0 都兼容不需要配 Maven 多模块不需要处理依赖冲突对新手友好。2.2 数据库建库建表与初始数据先确认 MySQL 服务已经起来。Windows 下如果用的是安装版服务名一般是MySQL80或MySQL57在服务管理器里能看到。用命令行连进去mysql -u root -p输入密码后进入 MySQL 控制台。接下来建库、建表、插初始数据。这份源码通常自带一个.sql文件但如果没有按下面这个最小结构来建字段名要和 DAO 里的 SQL 对得上-- 创建数据库字符集用 utf8mb4 避免中文乱码 CREATE DATABASE library_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE library_db; -- 用户表区分管理员和学生role 字段是关键 CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(50) NOT NULL, role VARCHAR(20) NOT NULL DEFAULT student, real_name VARCHAR(50), phone VARCHAR(20) ); -- 图书表stock 表示可借库存total 表示总册数 CREATE TABLE book ( id INT PRIMARY KEY AUTO_INCREMENT, book_name VARCHAR(100) NOT NULL, author VARCHAR(50), publisher VARCHAR(100), isbn VARCHAR(30), price DECIMAL(10,2), stock INT DEFAULT 0, total INT DEFAULT 0, cover_img VARCHAR(200) ); -- 借阅记录表status 标记是否已归还 CREATE TABLE borrow_record ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT, book_id INT, borrow_date DATETIME, return_date DATETIME, status VARCHAR(20) DEFAULT borrowed, fine DECIMAL(10,2) DEFAULT 0.00 ); -- 插入一个管理员账号密码明文仅用于演示 INSERT INTO user (username, password, role, real_name) VALUES (admin, admin123, admin, 系统管理员); INSERT INTO user (username, password, role, real_name) VALUES (student1, 123456, student, 张三);建完表后用SHOW TABLES;确认三张表都在。这里有个细节borrow_record表里的status字段源码里可能用的是0和1这种数字也可能是borrowed/returned字符串你得打开BorrowDaoImpl.java看一眼实际用的值两边必须一致否则借还逻辑会静默失败。2.3 Tomcat 部署与项目导入把源码解压后用 Eclipse 或 IDEA 以 Dynamic Web Project 形式导入。关键配置在WEB-INF/web.xml和src下的 Servlet 注解里。如果你用的是 Tomcat 9注意javax.servlet包名是对的如果换成 Tomcat 10包名变成了jakarta.servlet所有 Servlet 和 Filter 的 import 都得改这是最容易翻车的地方。数据库连接配置一般在src下的db.properties或某个工具类里找到url、username、password三项改成你本地的# 数据库连接配置注意时区和 SSL 参数 jdbc.urljdbc:mysql://localhost:3306/library_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse jdbc.usernameroot jdbc.password你的密码 jdbc.drivercom.mysql.cj.jdbc.DriverserverTimezone不写会报时区错误useSSLfalse在本地开发时能省掉证书警告。驱动类名如果是 MySQL 8.0 用com.mysql.cj.jdbc.Driver5.7 用com.mysql.jdbc.Driver写错了启动就报ClassNotFoundException。部署到 Tomcat 后访问http://localhost:8080/项目名/看到登录页就算通了。3. Servlet 请求流转与角色权限登录、借还书是怎么串起来的3.1 登录流程与 LoginFilter 拦截机制这份源码里LoginServlet和LoginFilter是两个核心类。登录的流程是浏览器 POST 用户名密码到LoginServletServlet 调LoginDao查库比对成功就把用户对象塞进session然后重定向到对应角色的首页失败就转发回登录页带错误信息。// LoginServlet 核心逻辑doPost 方法内 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username); String password request.getParameter(password); // 调用 DAO 层查询返回 User 对象或 null User user loginDao.findByUsernameAndPassword(username, password); if (user ! null) { HttpSession session request.getSession(); session.setAttribute(currentUser, user); // 根据角色跳不同页面 if (admin.equals(user.getRole())) { response.sendRedirect(admin/index.jsp); } else { response.sendRedirect(student/index.jsp); } } else { request.setAttribute(msg, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } }LoginFilter的作用是防止未登录直接访问后台页面。它拦截/admin/*和/student/*路径检查 session 里有没有currentUser没有就踢回登录页。这里有个常见坑Filter 的url-pattern如果写成/*会把登录页和静态资源也拦了导致死循环。正确做法是只拦需要权限的路径或者在 Filter 里放行登录相关的 URL。3.2 借书与还书的库存扣减逻辑借书不是简单插一条记录就完事它涉及库存扣减和并发问题。BorrowDaoImpl里通常有两个方法borrowBook(userId, bookId)和returnBook(recordId)。借书时要先查book表的stock是否大于 0大于才扣减并插入借阅记录。// BorrowDaoImpl 借书方法注意事务和库存判断 public boolean borrowBook(int userId, int bookId) { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 // 先查库存加行锁防止并发超借 String checkSql SELECT stock FROM book WHERE id ? FOR UPDATE; PreparedStatement ps1 conn.prepareStatement(checkSql); ps1.setInt(1, bookId); ResultSet rs ps1.executeQuery(); if (rs.next() rs.getInt(stock) 0) { // 扣库存 String updateSql UPDATE book SET stock stock - 1 WHERE id ?; PreparedStatement ps2 conn.prepareStatement(updateSql); ps2.setInt(1, bookId); ps2.executeUpdate(); // 插借阅记录 String insertSql INSERT INTO borrow_record (user_id, book_id, borrow_date, status) VALUES (?, ?, NOW(), borrowed); PreparedStatement ps3 conn.prepareStatement(insertSql); ps3.setInt(1, userId); ps3.setInt(2, bookId); ps3.executeUpdate(); conn.commit(); return true; } conn.rollback(); return false; } catch (Exception e) { // 出错回滚避免库存扣了记录没插 try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); return false; } finally { DBUtil.close(conn); } }FOR UPDATE这行是关键它给查询的行加了排他锁防止两个学生同时借最后一本书时都查到stock1然后都扣减导致库存变成 -1。还书逻辑反过来更新借阅记录状态为已归还同时book表stock 1。如果源码里没写事务你自己补上这是从「能跑」到「能用」的分水岭。3.3 角色菜单与页面跳转的对应关系三个角色的页面入口不一样。管理员登录后进admin/index.jsp左侧菜单有用户管理、图书管理、罚款管理学生进student/index.jsp菜单是图书浏览、我的借阅、个人信息游客不登录只能看bookList.jsp和bookDetail.jsp。这些 JSP 页面里用 JSTL 的c:if判断 session 里的角色来显示不同按钮。如果你发现登录后菜单不对先检查 session 里存的 role 值和你 JSP 里判断的值是否一致大小写敏感。4. 避坑排查从 404 到中文乱码的五个血泪现场4.1 启动报 ClassNotFoundException: com.mysql.jdbc.Driver现象是 Tomcat 启动时控制台抛异常项目直接起不来。原因通常是 MySQL 驱动 jar 没放进WEB-INF/lib目录或者驱动类名和 MySQL 版本不匹配。MySQL 8.0 的驱动类是com.mysql.cj.jdbc.Driver5.7 是com.mysql.jdbc.Driver。解决方法是确认mysql-connector-java的 jar 包在WEB-INF/lib下并且db.properties里的jdbc.driver和 jar 版本对应。用 Maven 的话检查pom.xml里依赖的 scope 是不是provided那会导致打包时驱动丢失。4.2 页面 404 但控制台无报错现象是访问http://localhost:8080/项目名/login.jsp返回 404Tomcat 日志里没有异常。原因一般是项目没有真正部署上去或者web.xml里welcome-file配置的首页文件名和实际文件对不上。解决方法是打开 Tomcat 的webapps目录看你的项目文件夹在不在不在就检查 Eclipse 的 Deployment Assembly 配置确保src和WebContent都映射到了部署路径。另外web.xml里url-pattern写错也会导致 Servlet 映射不上比如少写了斜杠。4.3 中文乱码从表单提交到数据库全链路现象是登录后页面显示问号或者插入数据库的中文书名变成乱码。原因有三层JSP 页面编码、请求编码、数据库连接编码。解决要三处一起改。JSP 文件头部加% page contentTypetext/html;charsetUTF-8 %Servlet 里在request.getParameter之前调request.setCharacterEncoding(UTF-8)数据库连接 URL 里带useUnicodetruecharacterEncodingutf8。三层缺一层都可能乱码。如果是 GET 请求传中文Tomcat 8 以后默认 URI 编码是 UTF-8一般没问题但 Tomcat 7 需要改server.xml的Connector加URIEncodingUTF-8。4.4 借书后库存没变或变成负数现象是学生借书成功但book表stock没减或者减成了负数。原因是 DAO 里没有用事务或者库存判断和扣减分了两条独立 SQL 且中间没有锁。解决方法是按 3.2 节的写法把查库存、扣库存、插记录包在一个事务里查询时加FOR UPDATE。如果源码里stock字段允许负数加个CHECK (stock 0)约束或者改字段类型为INT UNSIGNED让数据库层面兜底。4.5 Filter 拦截导致静态资源加载失败现象是登录页样式全丢CSS 和图片请求被LoginFilter拦下来重定向到登录页。原因是 Filter 的url-pattern配成了/*把.css、.js、.jpg也拦了。解决方法是在 Filter 的doFilter里判断请求路径如果是静态资源后缀就直接chain.doFilter放行或者把url-pattern改成只拦/admin/*和/student/*。更稳妥的做法是用FilterConfig读取白名单路径但简单项目里直接判断后缀就够了。5. 二次开发与验证把罚款逻辑补全并跑通一次完整借还5.1 罚款计算的实现思路源码里提到了罚款缴纳功能但很多版本只留了字段没写计算逻辑。补全的思路是在还书时判断是否超期超期天数乘以日罚金。日罚金一般设 0.2 到 0.5 元借期默认 30 天。在returnBook方法里加一段// 还书时计算罚款借期 30 天日罚金 0.5 元 public boolean returnBook(int recordId) { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 查借阅记录拿借出日期和图书 ID String querySql SELECT book_id, borrow_date FROM borrow_record WHERE id ? AND status borrowed; PreparedStatement ps1 conn.prepareStatement(querySql); ps1.setInt(1, recordId); ResultSet rs ps1.executeQuery(); if (rs.next()) { int bookId rs.getInt(book_id); java.util.Date borrowDate rs.getTimestamp(borrow_date); long days (System.currentTimeMillis() - borrowDate.getTime()) / (1000 * 60 * 60 * 24); double fine 0; if (days 30) { fine (days - 30) * 0.5; // 超期部分每天 0.5 元 } // 更新记录状态、归还时间、罚款金额 String updateSql UPDATE borrow_record SET status returned, return_date NOW(), fine ? WHERE id ?; PreparedStatement ps2 conn.prepareStatement(updateSql); ps2.setDouble(1, fine); ps2.setInt(2, recordId); ps2.executeUpdate(); // 库存加回 String stockSql UPDATE book SET stock stock 1 WHERE id ?; PreparedStatement ps3 conn.prepareStatement(stockSql); ps3.setInt(1, bookId); ps3.executeUpdate(); conn.commit(); return true; } conn.rollback(); return false; } catch (Exception e) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); return false; } finally { DBUtil.close(conn); } }days算的是借出到现在的天数超过 30 的部分才计费。fine字段在borrow_record表里已经有了直接更新就行。管理员端的罚款管理页面就是查fine 0且status returned的记录展示出来供缴纳操作。5.2 验证清单一次完整借还该看哪些表改完代码别急着说搞定按这个顺序验证一遍。第一步用student1登录借一本stock大于 0 的书看页面提示成功。第二步查borrow_record表应该多一条statusborrowed的记录borrow_date是当前时间。第三步查book表对应书的stock应该减了 1。第四步用管理员登录看借阅记录列表里有没有这条。第五步模拟还书把borrow_date手动改成 40 天前再走还书流程看fine字段是不是算出了 5 元10 天 × 0.5。第六步确认stock加回了 1记录status变成returned。这六步走完借还闭环才算真正通了。5.3 一个容易忽略的细节图片上传与显示路径源码里有ImgUtils.java和top.gif、BookServlet.java说明图书封面是支持上传的。常见问题是上传后图片存到了服务器临时目录重启就丢。正确做法是存到项目外的固定目录比如D:/library_upload/数据库里只存文件名JSP 显示时拼路径。ImgUtils里一般有生成缩略图或校验格式的逻辑注意它校验的后缀名列表是否包含你实际要传的格式。如果上传后图片不显示先看 Tomcat 控制台有没有FileNotFoundException再看数据库cover_img字段存的是绝对路径还是相对路径JSP 里img src拼的对不对。从那以后我每次拿到一份 JSP 源码都强制自己先跑一遍「建库 → 改配置 → 部署 → 登录 → 借还 → 查表」这条链路不跳过任何一步。很多坑不是代码写错了是环境和配置没对齐。希望这份笔记能帮你少走点弯路。本文还有配套的精品资源点击获取
返回列表