资讯详情

资讯详情

JSP图书管理系统实战:从零搭建可部署的Java Web项目

简介本资源是一份面向高校计算机专业本科生的毕业设计文档聚焦基于JSP与MySQL实现的B/S架构图书管理系统解决高校图书馆信息化管理中借阅效率低、人工操作繁重等实际问题。文档完整覆盖系统需求分析、BS架构设计原理、JSP动态页面开发要点、MySQL数据库建表逻辑含图书、用户、借阅记录三类核心表、三层权限控制机制管理员/教师/学生及关键功能实现说明适合作为课程设计参考或毕设开题与答辩支撑材料。资源为单文件docx格式共1个438KB文档内容结构清晰含摘要、关键词、目录、开发环境介绍JSPMyEclipseMySQL、技术对比分析及中英文摘要等标准毕业论文模块。目前已有284人学习下载读者可直接获取规范化的系统设计思路、可复用的权限划分方案、BS架构落地要点及完整技术栈整合说明快速掌握Web应用开发全流程。1. 为什么还在用 JSP 做图书管理系统不是过时而是“够用”和“可控”你点开这个标题大概率正面临一个现实任务课程设计要交、毕设要答辩、实训项目要验收或者某高校实验室需要一套能跑在本地 Tomcat 上、带借阅记录、用户权限、图书增删改查的轻量级系统。它不追求高并发、不对接微服务、不需要响应式前端——但必须三天内能部署、五天内能调通、老师能现场打开网页操作、答辩时能讲清楚每一层怎么串起来。这时候“基于 JSP 的图书管理系统”不是技术怀旧而是一条被反复验证过的最小可行路径Servlet 控制流程清晰、JDBC 直连数据库无抽象层遮蔽、HTMLJSTL 模板可读性强连报错堆栈都指向具体行号。它不炫技但胜在每一步都可追溯、每个 request 都可打断点、每个 session 属性都能在调试器里展开看。如果你是刚学完《Java Web 编程基础》的学生或带实训课的某导师这套方案不是“将就”而是把有限精力聚焦在理解 MVC 分层本质、掌握 HTTP 请求生命周期、建立前后端数据流转直觉上的务实选择。2. 从零搭起 JSP 图书管理系统的最小可行骨架2.1 环境准备只装这 4 个东西别碰 Maven 多模块这不是企业级项目不需要 Spring Boot 自动装配、不用 MyBatis 动态 SQL。我们回归最原始的 Java Web 开发链路JDK Tomcat 数据库 IDE。常见翻车点在于版本错配我按实测稳定组合列明JDK 8u291非 JDK 11Tomcat 9 默认支持 JDK 8JSP 编译器对泛型推导更宽容避免java.lang.ClassNotFoundException: javax.servlet.jsp.JspFactory这类玄学报错Apache Tomcat 9.0.83非 10.xTomcat 10 启用 Jakarta EE 9 命名空间jakarta.servlet.*所有 import 包名全变而教材、参考文档、老代码全是javax.*强行升级等于重写半个项目MySQL 5.7.36非 8.0驱动用mysql-connector-java-5.1.47.jar放在WEB-INF/lib/下8.0 默认启用 caching_sha2_password 认证插件老 JDBC 驱动不兼容连不上就是白忙IDE 推荐 Eclipse IDE for Enterprise Java and Web Developers2021-09 版比 IntelliJ 对传统 Dynamic Web Project 支持更傻瓜新建项目时勾选 “Generate web.xml deployment descriptor” 是关键否则后续 Servlet 映射全失效提示所有安装包统一解压到无中文、无空格路径例如D:\dev\tomcat9否则 JSP 编译时org.apache.jasper.JasperException: Unable to compile class for JSP报错会卡死在路径解析环节。2.2 项目结构拒绝“src/main/webapp”用经典 Dynamic Web ProjectEclipse 新建项目时选Dynamic Web Project名称填BookMgmtSystemTarget runtime 选刚配好的 Tomcat 9Dynamic web module version 必须选 3.1对应 Servlet 3.1 规范。生成后目录结构必须长这样BookMgmtSystem/ ├── WebContent/ ← 不叫 src/main/webapp │ ├── index.jsp │ ├── login.jsp │ ├── admin/ │ │ └── book_list.jsp │ └── WEB-INF/ │ ├── web.xml ← 手动确保存在不是空文件 │ └── lib/ │ └── mysql-connector-java-5.1.47.jar ├── src/ │ ├── servlet/ │ │ ├── LoginServlet.java │ │ ├── BookListServlet.java │ │ └── AddBookServlet.java │ └── dao/ │ ├── BookDAO.java │ └── UserDAO.java └── build/ └── classes/ ← 编译输出目录Eclipse 自动生成关键动作右键项目 → Properties → Project Facets → 确保Dynamic Web Module 3.1,Java 1.8,JavaScript 1.0全部打钩。若web.xml不存在手动在WEB-INF/下新建内容必须包含web-app根节点和version3.1属性否则 Tomcat 启动时直接忽略所有 Servlet 映射。2.3 web.xml 配置3 行代码定生死少一行就 404这是整个系统路由的总开关也是新手最容易漏写的配置。WEB-INF/web.xml内容精简到极致只保留必要项?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd version3.1 !-- 登录入口 -- servlet servlet-nameLoginServlet/servlet-name servlet-classservlet.LoginServlet/servlet-class /servlet servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login/url-pattern /servlet-mapping !-- 图书列表入口 -- servlet servlet-nameBookListServlet/servlet-name servlet-classservlet.BookListServlet/servlet-class /servlet servlet-mapping servlet-nameBookListServlet/servlet-name url-pattern/book/list/url-pattern /servlet-mapping /web-app逻辑说明servlet块声明 Servlet 类的全限定名包名类名servlet-class必须与.java文件实际路径严格一致大小写敏感servlet-mapping将 URL 路径如/book/list绑定到指定 Servlet浏览器访问http://localhost:8080/BookMgmtSystem/book/list时Tomcat 才会实例化BookListServlet并调用其doGet()方法没有servlet-mappingURL 就永远 404没有servlet声明映射就找不到目标类——这是 90% 的“页面打不开”问题根源。3. 核心功能实现3 个 Servlet 2 个 DAO撑起全部业务3.1 数据库建表5 张表足矣字段命名拒绝驼峰MySQL 中执行以下建表语句注意字符集必须为utf8mb4否则中文存入变问号-- 用户表管理员与普通读者共用一张表用 type 字段区分 CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, realname VARCHAR(30), type ENUM(admin, reader) DEFAULT reader, created_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 图书表ISBN 作主键更符合业务实际避免同书多版重复 CREATE TABLE books ( isbn VARCHAR(17) PRIMARY KEY, title VARCHAR(200) NOT NULL, author VARCHAR(100), publisher VARCHAR(100), publish_year YEAR, stock INT DEFAULT 0, created_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 借阅记录表外键关联 users 和 booksstatus 记录借/还状态 CREATE TABLE borrow_records ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, isbn VARCHAR(17) NOT NULL, borrow_time DATETIME DEFAULT CURRENT_TIMESTAMP, return_time DATETIME NULL, status ENUM(borrowed, returned) DEFAULT borrowed, FOREIGN KEY (user_id) REFERENCES users(id), FOREIGN KEY (isbn) REFERENCES books(isbn) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;参数说明VARCHAR(17)存 ISBN-13含分隔符足够覆盖所有标准 ISBNENUM类型比VARCHAR更安全数据库层强制取值范围避免代码里写错adminn导致权限失控FOREIGN KEY必须显式声明否则删除图书时可能残留借阅记录造成数据不一致DEFAULT CURRENT_TIMESTAMP减少 Java 代码中手动 set 时间的出错概率。3.2 JDBC 工具类1 个DBUtil.java封装连接获取与关闭src/util/DBUtil.java是整个数据访问层的基石必须手写不依赖任何框架package 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/bookdb?useSSLfalseserverTimezoneUTCcharacterEncodingutf8mb4; private static final String USER root; private static final String PASSWORD 123456; // 实际使用请改密 static { try { Class.forName(com.mysql.jdbc.Driver); // 注意是 com.mysql.jdbc.Driver不是 com.mysql.cj.jdbc.Driver } catch (ClassNotFoundException e) { throw new RuntimeException(MySQL Driver not found!, e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void closeConnection(Connection conn) { if (conn ! null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); // 生产环境应记日志此处仅调试 } } } }逻辑说明Class.forName()必须显式调用JDK 6 虽支持自动加载但 Tomcat 类加载器隔离机制下常失效不写这行getConnection()会抛SQLException: No suitable driver foundURL 中useSSLfalse是 MySQL 5.7 必加参数否则连接超时serverTimezoneUTC避免时区转换错误导致时间字段写入异常com.mysql.jdbc.Driver是 5.1.x 驱动的类名若误写成com.mysql.cj.jdbc.Driver8.0 驱动类名启动时直接ClassNotFoundException。3.3 BookDAO用 PreparedStatement 防 SQL 注入不是 Statementsrc/dao/BookDAO.java实现图书 CRUD重点看参数化查询写法package dao; import util.DBUtil; import java.sql.*; import java.util.ArrayList; import java.util.List; public class BookDAO { // 查询全部图书带分页 public ListBook findAll(int offset, int limit) throws SQLException { String sql SELECT isbn, title, author, publisher, publish_year, stock FROM books LIMIT ?, ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, offset); // 第一个 ? 设为 offset ps.setInt(2, limit); // 第二个 ? 设为 limit try (ResultSet rs ps.executeQuery()) { ListBook list new ArrayList(); while (rs.next()) { Book book new Book(); book.setIsbn(rs.getString(isbn)); book.setTitle(rs.getString(title)); book.setAuthor(rs.getString(author)); book.setPublisher(rs.getString(publisher)); book.setPublishYear(rs.getInt(publish_year)); book.setStock(rs.getInt(stock)); list.add(book); } return list; } } } // 根据 ISBN 查询单本 public Book findByIsbn(String isbn) throws SQLException { String sql SELECT isbn, title, author, publisher, publish_year, stock FROM books WHERE isbn ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, isbn); // 参数 1 对应 ?严格按顺序 try (ResultSet rs ps.executeQuery()) { if (rs.next()) { Book book new Book(); book.setIsbn(rs.getString(isbn)); book.setTitle(rs.getString(title)); book.setAuthor(rs.getString(author)); book.setPublisher(rs.getString(publisher)); book.setPublishYear(rs.getInt(publish_year)); book.setStock(rs.getInt(stock)); return book; } return null; } } } // 新增图书 public int add(Book book) throws SQLException { String sql INSERT INTO books (isbn, title, author, publisher, publish_year, stock) VALUES (?, ?, ?, ?, ?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, book.getIsbn()); ps.setString(2, book.getTitle()); ps.setString(3, book.getAuthor()); ps.setString(4, book.getPublisher()); ps.setInt(5, book.getPublishYear()); ps.setInt(6, book.getStock()); return ps.executeUpdate(); // 返回影响行数1 表示成功 } } }参数说明PreparedStatement的?占位符必须按 SQL 中出现顺序用setXxx()设置索引从 1 开始不能跳过或错位try-with-resources自动关闭Connection、PreparedStatement、ResultSet避免连接泄漏——这是 Tomcat 运行几小时后突然报Cannot create PoolableConnectionFactory的主因executeUpdate()用于 INSERT/UPDATE/DELETE返回整数表示影响行数executeQuery()用于 SELECT返回ResultSet。4. JSP 页面开发用 JSTL 替代 scriptlet让 HTML 干净可维护4.1 引入 JSTL2 个 jar 包 1 行 taglib 声明JSP 中禁止写% java code %scriptlet全部改用 JSTL 标签。需下载两个 jar 包放入WEB-INF/lib/jstl-1.2.jarstandard-1.1.2.jar然后在每个 JSP 顶部声明% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % % taglib prefixfmt urihttp://java.sun.com/jsp/jstl/fmt %提示uri值必须一字不差复制粘贴即可。若写成http://jstl.sourceforge.net/jstl/core或漏掉http://java.sun.com/页面会直接报According to TLD or attribute directive in tag file, attribute xxx does not accept any expressions。4.2 图书列表页book_list.jspJSTL 循环 EL 表达式取值% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html head title图书列表/title style table { width: 100%; border-collapse: collapse; } th, td { border: 1px solid #ccc; padding: 8px; text-align: left; } .btn { background: #007bff; color: white; padding: 4px 8px; text-decoration: none; } /style /head body h2全部图书/h2 a hrefadd_book.jsp classbtn新增图书/a brbr table tr thISBN/th th书名/th th作者/th th出版社/th th出版年/th th库存/th th操作/th /tr c:choose c:when test${empty bookList} trtd colspan7暂无图书/td/tr /c:when c:otherwise c:forEach items${bookList} varbook tr td${book.isbn}/td td${book.title}/td td${book.author}/td td${book.publisher}/td tdfmt:formatNumber value${book.publishYear} pattern#//td td${book.stock}/td td a hrefbook_detail.jsp?isbn${book.isbn} classbtn详情/a a hrefdelete_book.jsp?isbn${book.isbn} classbtn onclickreturn confirm(确定删除)删除/a /td /tr /c:forEach /c:otherwise /c:choose /table /body /html逻辑说明${bookList}是 Servlet 中request.setAttribute(bookList, bookList)传入的 List EL 表达式自动调用 getter 方法getIsbn()→${book.isbn}c:forEach替代% for(...) { %varbook在循环体内生成每个 Book 对象的引用c:choosec:whenc:otherwise替代% if...else %语义清晰且无 Java 语法干扰 HTML 结构fmt:formatNumber格式化数字避免publishYear为null时页面显示0YEAR 类型默认值。4.3 登录校验Session 管理权限不是 Cookie 存密码LoginServlet.java中关键逻辑protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username); String password request.getParameter(password); UserDAO userDAO new UserDAO(); User user userDAO.findByUsername(username); if (user ! null password.equals(user.getPassword())) { // 登录成功存用户信息到 Session不存密码 HttpSession session request.getSession(); session.setAttribute(currentUser, user); // 存整个 User 对象 session.setMaxInactiveInterval(1800); // 30 分钟无操作自动失效 // 根据角色跳转 if (admin.equals(user.getType())) { response.sendRedirect(admin/book_list.jsp); } else { response.sendRedirect(reader/my_borrows.jsp); } } else { request.setAttribute(errorMsg, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } }参数说明request.getSession()获取当前会话首次调用创建新 Session后续请求自动复用session.setAttribute(currentUser, user)将用户对象存入 Session后续所有页面可通过${sessionScope.currentUser.username}取值setMaxInactiveInterval(1800)设定 Session 最大空闲时间秒防止长期未登出导致内存泄漏绝不把密码存入 Session 或 Cookie只存必要身份标识如 user.id 或 user.type密码仅用于本次校验。5. 避坑指南这 4 个错误占了调试时间的 70%5.1 现象启动 Tomcat 后访问http://localhost:8080/BookMgmtSystem/显示 404但http://localhost:8080/能看到 Tomcat 首页原因项目未成功部署到 Tomcat。常见有三① Eclipse 中项目右键 → Run As → Run on Server 未勾选该项目②WebContent/目录未被识别为 Web Root在项目 Properties → Deployment Assembly 中未添加WebContent/到/③web.xml文件名大小写错误如Web.xml或WEB.XMLWindows 下不敏感Linux 服务器上直接失败。解决右键项目 → Properties → Deployment Assembly → 点击 Add → Folder → 选择WebContent/→ Deploy Path 填/→ Finish确认web.xml文件名全小写。5.2 现象登录后跳转到book_list.jsp页面空白查看源码发现c:forEach标签原样输出未解析原因JSTL 标签库未正确加载。要么jstl.jar和standard.jar未放入WEB-INF/lib/要么taglib声明的uri值拼写错误要么 JSP 文件编码不是 UTF-8 导致声明行乱码。解决检查WEB-INF/lib/下两个 jar 包是否存在复制标准uri值用 Notepad 将 JSP 文件另存为 UTF-8 无 BOM 格式。5.3 现象点击“新增图书”提交后页面报HTTP Status 405 – Method Not Allowed原因Servlet 中只写了doPost()方法但表单 methodget或只写了doGet()但表单 methodpost方法不匹配。解决统一约定——表单提交一律用 POSTServlet 中只实现doPost()若需 GET 支持如分页链接在doGet()中调用doPost(request, response)复用逻辑。5.4 现象图书列表中中文显示为??数据库里却是正常的原因JDBC URL 缺少characterEncodingutf8mb4或 Tomcat 的conf/server.xml中 Connector 未设置URIEncodingUTF-8。解决修改server.xml中Connector port8080 ... /为Connector port8080 protocolHTTP/1.1 URIEncodingUTF-8 ... /重启 Tomcat确认web.xml中% page contentTypetext/html;charsetUTF-8 %存在。6. 验证与交付3 步自测清单 1 个答辩必答问题6.1 本地自测清单不靠老师自己跑通这 3 条路径测试路径操作步骤预期结果关键检查点登录流程1. 访问http://localhost:8080/BookMgmtSystem/login.jsp2. 输入已知管理员账号如admin/1234563. 点击登录跳转至admin/book_list.jsp页面显示图书表格查看浏览器地址栏是否变为/book_list.jspF12 检查 Network 选项卡确认login请求返回 302 重定向数据写入1. 在book_list.jsp点“新增图书”2. 填 ISBN978-7-04-050694-6、书名Java Web 开发实战、作者张三3. 提交页面跳回book_list.jsp新书出现在第一行登录 MySQL 执行SELECT * FROM books WHERE isbn978-7-04-050694-6;确认记录存在且字段值完全匹配权限隔离1. 用读者账号如reader/123456登录2. 手动在地址栏输入http://localhost:8080/BookMgmtSystem/admin/book_list.jsp页面显示 404 或跳转回登录页检查admin/book_list.jsp顶部是否有权限校验代码% if (session.getAttribute(currentUser) null6.2 答辩高频问题如何回答“为什么不用 Spring Boot”这个问题本质是考察技术选型意识不是比谁用的框架新。我的标准回答是“Spring Boot 适合快速构建云原生应用但本次设计目标是透彻理解 Web 应用底层交互机制。用原生 Servlet我能清晰看到HttpServletRequest如何解析表单参数、HttpServletResponse如何写入 HTML、HttpSession如何通过 Cookie 维持状态、JDBC 如何建立物理连接——这些在 Spring Boot 的自动配置下被封装成黑匣子。比如当NullPointerException发生在bookDAO.findAll()时我立刻知道是Connection为空进而排查DBUtil.getConnection()是否抛异常而在 Spring 中同样的错误可能隐藏在Transactional代理或连接池配置里初学者难以定位。这不是拒绝高级框架而是先打好地基。”6.3 我的血泪经验打包交付前必做的 3 件事清理build/classes/下的.class文件Eclipse 有时编译残留旧字节码导致 Tomcat 加载了过期的 Servlet行为与源码不符。交付前手动删除整个build/classes/目录再 Clean Project 重建。把WEB-INF/web.xml中的注释全删掉某些老旧 Tomcat 版本解析 XML 注释会出错删掉!-- 这是登录映射 --这类注释只留纯标签。在index.jsp里加一行跳转% response.sendRedirect(login.jsp); %这样用户访问根路径时自动进登录页避免看到空白页以为系统没跑起来。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →