JSP+Servlet+MySQL图书管理系统实战部署指南 简介这是一套面向Java Web初学者与课程设计实践者的JSP图书馆管理系统完整源码基于JSPServletMySQL三层架构实现覆盖用户权限分级、图书借阅全流程及后台管理核心功能。资源共112个文件含45个Java业务逻辑类如BookServlet、BorrowDaoImpl、LoginFilter等、24个JSP页面登录、图书列表、借阅记录等前端视图、37个PNG界面图标与2个GIF动态元素辅以PDF说明文档和MD项目说明整体压缩包仅1.76MB轻量易部署。已有263人学习下载适合高校Java Web实验课、毕业设计参考或Servlet基础项目实战。读者可直接导入IDE运行完整掌握MVC分层开发模式、数据库连接池配置、会话控制、表单验证及前后端交互逻辑代码结构清晰、注释规范关键模块如借阅状态管理、罚款计算与密码安全处理均有明确实现路径。1. 为什么还在用 JSP Servlet MySQL 做图书管理系统这不是“过时技术”而是教学闭环里最扎实的练手靶心你可能刚在某高校实验课上被要求交一个“基于 JSPMySQLServlet 的图书馆图书管理系统”也可能正为毕业设计找一个能跑通、能调试、能讲清每层逻辑的 Web 入门项目。别急着搜“Spring Boot 替代方案”——这个标题不是怀旧而是一套被反复验证过的、零框架依赖、全链路可控、错误可定位、部署无黑盒的教学级 Web 架构组合。它不追求高并发或微服务但能把 HTTP 请求怎么进 Servlet、怎么查 MySQL、怎么把 ResultSet 渲染成 HTML 表格、怎么用 session 管理登录态这些底层链条一环一环掰开给你看。对新手它是理解 MVC 分层的第一块磨刀石对熟手它是排查“页面空白却不报错”“中文乱码查了三小时”这类问题的黄金沙盒。本篇不讲 Spring、不碰 Tomcat 配置文件以外的容器管理只聚焦如何从一个 .zip 源码包出发在本地 Windows/macOS 环境下用 JDK 8、Tomcat 9、MySQL 5.7 三件套30 分钟内跑通增删改查全流程并亲手填平那几个让 80% 初学者卡住的深坑。2. 环境准备与源码解压确认 JDK/Tomcat/MySQL 版本兼容性是第一步提示别跳过这一步。JDK 11 与 Tomcat 9 默认不兼容 Servlet 3.1 规范下的部分注解写法MySQL 8.0 默认启用 caching_sha2_password 认证插件老版 JDBC 驱动会直接连接失败——这些不是代码 bug是环境契约。2.1 检查并锁定基础组件版本这是整个系统能跑起来的前提。打开终端Windows 用 cmdmacOS/Linux 用 Terminal逐条执行# 查看 JDK 版本必须为 1.8即 JDK 8 java -version # 正确输出应类似java version 1.8.0_361 # 查看 Tomcat 版本推荐 Apache Tomcat 9.0.x避免 10 因 Jakarta EE 命名空间变更导致类找不到 %CATALINA_HOME%\bin\version.bat # Windows $CATALINA_HOME/bin/version.sh # macOS/Linux # 正确输出应含 Apache Tomcat/9.0.xx # 查看 MySQL 版本必须 ≤ 5.7若已装 8.0请另装 5.7 实例或修改认证方式 mysql --version # 正确输出应类似mysql Ver 14.14 Distrib 5.7.42若版本不符请立即降级或重装。JDK 8 下载地址 Oracle Archive 选 jdk-8u361Tomcat 9 下载地址 Apache Tomcat 9 Archives 选 v9.0.83MySQL 5.7 下载地址 MySQL 5.7 Downloads 选 mysql-5.7.42。2.2 解压源码包并校验目录结构将JSP图书馆图书管理系统源码.zip解压到一个无中文、无空格、路径层级尽量浅的目录例如D:\library-system或/Users/xxx/library-system。解压后标准目录结构应如下这是后续所有配置的基准library-system/ ├── WebContent/ ← JSP 页面、CSS/JS、WEB-INF 目录所在 │ ├── index.jsp │ ├── login.jsp │ ├── book_list.jsp │ └── WEB-INF/ │ ├── web.xml ← 核心配置servlet 映射、filter、context-param │ └── lib/ ← 必须包含 mysql-connector-java-5.1.47.jar非 8.x ├── src/ ← Java 源码目录Servlet 类、DAO、Bean │ ├── servlet/ │ │ ├── LoginServlet.java │ │ ├── BookAddServlet.java │ │ └── ... │ ├── dao/ │ │ └── BookDao.java │ └── bean/ │ └── Book.java └── README.md ← 若有优先读它常含数据库建表语句注意WebContent/WEB-INF/lib/下的 MySQL 驱动必须是5.1.x 系列如mysql-connector-java-5.1.47.jar。若发现是mysql-connector-java-8.0.33.jar请立刻删除并替换为 5.1.47 —— 这是连接 MySQL 5.7 的唯一稳定选择。驱动下载地址 MySQL Connector/J 5.1 。3. 数据库初始化建库、建表、插测试数据三步不能少系统无法启动最常见的原因是数据库没建、表没创、账号没授权。别指望“自动建表”JSPServlet 项目几乎全是手动 SQL。3.1 创建数据库与用户MySQL 5.7用 MySQL 客户端如命令行、Navicat、DBeaver执行以下 SQL。注意不要用 root 用户直接连应用这是安全常识也是很多初学者被拒的原因。-- 1. 创建数据库字符集必须为 utf8不是 utf8mb4 CREATE DATABASE library_db CHARACTER SET utf8 COLLATE utf8_general_ci; -- 2. 创建专用用户用户名/密码可自定义但需与后续配置一致 CREATE USER lib_userlocalhost IDENTIFIED BY lib123; -- 3. 授权只给该库的全部权限 GRANT ALL PRIVILEGES ON library_db.* TO lib_userlocalhost; -- 4. 刷新权限 FLUSH PRIVILEGES;逻辑说明CHARACTER SET utf8是关键。MySQL 5.7 中utf8实际对应utf8mb3足够存中文而utf8mb4虽更现代但老版 JDBC 驱动解析易出错。lib_user是应用连接时用的账号不是 root。3.2 执行建表 SQL从源码中提取大多数此类源码包会在src/或WebContent/下附带.sql文件如library_db.sql或create_table.sql。若未找到请手动执行以下标准建表语句这是图书管理系统最简核心表USE library_db; -- 图书表 CREATE TABLE books ( id INT PRIMARY KEY AUTO_INCREMENT, isbn VARCHAR(20) NOT NULL, title VARCHAR(100) NOT NULL, author VARCHAR(50), publisher VARCHAR(50), publish_date DATE, price DECIMAL(8,2), stock INT DEFAULT 0, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8; -- 用户表用于登录 CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(20) NOT NULL UNIQUE, password VARCHAR(50) NOT NULL, -- 明文存储仅用于教学切勿用于生产 role ENUM(admin,user) DEFAULT user ) ENGINEInnoDB DEFAULT CHARSETutf8; -- 插入一条管理员测试数据用户名 admin密码 123456 INSERT INTO users (username, password, role) VALUES (admin, e10adc3949ba59abbe56e057f20f883e, admin); -- 注e10adc3949ba59abbe56e057f20f883e 是 123456 的 MD5 值多数老系统用此方式存密参数说明ENGINEInnoDB是必须的它支持事务和外键DEFAULT CHARSETutf8与数据库创建时保持一致MD5 密码值可在线工具生成确保与LoginServlet中比对逻辑匹配。3.3 验证连接用 JDBC URL 测试能否连通在正式部署前先用最简 Java 程序验证数据库连通性。新建一个TestDB.java// TestDB.java import java.sql.*; public class TestDB { public static void main(String[] args) { String url jdbc:mysql://localhost:3306/library_db?useUnicodetruecharacterEncodingutf8; String user lib_user; String pwd lib123; try (Connection conn DriverManager.getConnection(url, user, pwd)) { System.out.println(✅ 数据库连接成功); try (Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(SELECT COUNT(*) FROM books)) { if (rs.next()) { System.out.println( 当前图书表记录数 rs.getInt(1)); } } } catch (SQLException e) { System.err.println(❌ 连接失败 e.getMessage()); e.printStackTrace(); } } }编译运行javac -cp .;mysql-connector-java-5.1.47.jar TestDB.java java -cp .;mysql-connector-java-5.1.47.jar TestDB若输出 ✅ 和 说明数据库层已打通。否则根据错误信息回溯URL 端口错用户名密码错防火墙拦了—— 这是后续所有问题的前置检查点。4. Tomcat 部署与配置web.xml 是灵魂别让它沉默部署不是复制粘贴。web.xml是 Servlet 时代的“路由中枢”它的写法直接决定请求能否抵达正确 Servlet。4.1 将项目发布为 Tomcat 可识别的 Web 应用将解压后的library-system/WebContent/整个目录不是library-system/复制到 Tomcat 的webapps/目录下并重命名为library去掉空格和中文小写$CATALINA_HOME/webapps/library/ ├── index.jsp ├── login.jsp ├── book_list.jsp └── WEB-INF/ ├── web.xml └── lib/ └── mysql-connector-java-5.1.47.jar逻辑说明Tomcat 启动时会扫描webapps/下每个子目录作为独立 Web 应用。library是应用上下文路径Context Path访问地址将是http://localhost:8080/library/。若目录名含空格如library systemTomcat 会拒绝加载且不报错只在日志里写Invalid context path。4.2 关键校验 web.xml 中的 Servlet 映射是否完整打开webapps/library/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 !-- 1. 配置欢迎页 -- welcome-file-list welcome-fileindex.jsp/welcome-file /welcome-file-list !-- 2. 声明 LoginServlet -- servlet servlet-nameLoginServlet/servlet-name servlet-classservlet.LoginServlet/servlet-class /servlet servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login/url-pattern !-- 注意这是 form action 的目标 -- /servlet-mapping !-- 3. 声明 BookListServlet图书列表 -- 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 !-- 其他 Servlet 映射... -- /web-app参数说明servlet-class的值必须与src/servlet/LoginServlet.java的包声明完全一致如package servlet;url-pattern是浏览器地址栏中/library/login的后半段必须与 JSP 表单的action/login匹配。常见错误servlet-class写成LoginServlet漏包名、url-pattern写成/servlet/login多写了 servlet 前缀。4.3 启动 Tomcat 并观察日志双击$CATALINA_HOME/bin/startup.batWindows或./startup.shmacOS/Linux。打开浏览器访问http://localhost:8080/library/。此时不应看到 404而应看到index.jsp的首页内容通常是导航栏欢迎语。若失败请立即查看$CATALINA_HOME/logs/catalina.outLinux/macOS或catalina.yyyy-mm-dd.logWindows搜索关键词SEVERE严重错误如ClassNotFoundExceptionServlet 类没编译或路径错WARNING警告如No mapping found for HTTP request with URI [/library/login]web.xml映射缺失INFO正常启动日志含Starting ProtocolHandler [http-nio-8080]表示端口监听成功5. 避坑指南那些让开发者凌晨三点还在抓头发的典型问题这些问题不是“可能遇到”而是90% 的实操者必然踩中至少 2 个。它们不报红错却让功能静默失效是真正的“玄学翻车现场”。5.1 现象登录页面输入账号密码点击提交后页面刷新但无反应URL 变成/library/login后台无任何日志→ 原因web.xml中LoginServlet的url-pattern与login.jsp表单的action属性不一致。例如web.xml写的是/doLogin而login.jsp里写的是form action/login。→ 解决统一为/login。检查login.jsp第一行form action/login methodpost确保web.xml中url-pattern也为此值。注意action 值前面的/表示相对于当前应用根路径即/library不是服务器根路径。5.2 现象图书列表页显示“???”或方框中文标题、作者名全部乱码→ 原因三个环节任一缺失 UTF-8 声明①web.xml未设置全局编码过滤器②JSP页面未声明 pageEncoding③MySQL JDBC URL未加characterEncodingutf8。→ 解决三处同步修正。① 在web.xmlweb-app标签下添加过滤器放在所有 servlet-mapping 之前filter filter-nameencodingFilter/filter-name filter-classorg.apache.catalina.filters.SetCharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping② 在每个.jsp文件顶部添加% page languagejava contentTypetext/html; charsetUTF-8 pageEncodingUTF-8%③ 确保JDBC URL为jdbc:mysql://localhost:3306/library_db?useUnicodetruecharacterEncodingutf85.3 现象添加新书后页面跳转到book_list.jsp但列表为空数据库里却有记录→ 原因BookListServlet查询后将ListBook存入request但book_list.jsp未用c:forEach或脚本片段正确遍历或request.setAttribute()的 key 名与 JSP 中request.getAttribute()的 key 不一致。→ 解决检查BookListServlet.java中ListBook bookList bookDao.findAll(); request.setAttribute(bookList, bookList); // key 是 bookList request.getRequestDispatcher(book_list.jsp).forward(request, response);再检查book_list.jsp中是否用c:forEach items${bookList} varbook trtd${book.title}/tdtd${book.author}/td/tr /c:forEach若未引入 JSTL改用脚本片段% ListBook bookList (ListBook) request.getAttribute(bookList); for (Book book : bookList) { % trtd% book.getTitle() %/td/tr % } %5.4 现象修改图书信息后数据库更新成功但页面跳转回列表时新数据未显示仍是旧值→ 原因浏览器缓存了book_list.jsp的 GET 响应或BookListServlet未重新查询数据库而是用了旧的request属性如果它被错误地设为session作用域。→ 解决强制禁用缓存。在book_list.jsp顶部添加% response.setHeader(Cache-Control, no-cache, no-store, must-revalidate); // HTTP 1.1 response.setHeader(Pragma, no-cache); // HTTP 1.0 response.setDateHeader(Expires, 0); // Proxies %同时确认BookListServlet每次都调用bookDao.findAll()而非从session中取缓存数据。6. 进阶技巧用最小改动实现“记住我”与分页让系统脱离教学 Demo跑通 CRUD 只是起点。真正体现工程思维的是在不重构架构的前提下用几行代码解决真实痛点。下面两个技巧我已在多个模拟项目 X 中验证过无需引入新框架纯 JSPServletMySQL 可实现。6.1 “记住我”功能用 Cookie 存储加密 token替代明文密码LoginServlet中当勾选“记住我”时不存密码而生成一个随机 token 并写入 Cookie// LoginServlet.java 片段 if (on.equals(request.getParameter(rememberMe))) { String token UUID.randomUUID().toString().replace(-, ); // 将 token 与用户 ID 关联存入数据库新增 tokens 表 tokenDao.saveToken(userId, token); Cookie cookie new Cookie(login_token, token); cookie.setPath(/library); // 限定作用域 cookie.setMaxAge(60 * 60 * 24 * 7); // 7 天 response.addCookie(cookie); }在index.jsp或filter中拦截请求检查 Cookie// 自定义 LoginCheckFilter.java String token null; Cookie[] cookies request.getCookies(); if (cookies ! null) { for (Cookie c : cookies) { if (login_token.equals(c.getName())) { token c.getValue(); break; } } } if (token ! null tokenDao.isValid(token)) { User user tokenDao.findUserByToken(token); request.getSession().setAttribute(user, user); chain.doFilter(request, response); return; } // 否则跳转登录页 response.sendRedirect(request.getContextPath() /login.jsp);关键参数cookie.setPath(/library)确保 token 只在本应用有效setMaxAge(7*24*3600)是持久化 Cookie关闭浏览器不失效tokenDao表需有user_id,token,expire_time字段。6.2 简易分页不依赖 PageHelper手写 LIMIT OFFSETBookListServlet接收页码参数动态拼 SQLint page Integer.parseInt(request.getParameter(page)); // 默认 1 int pageSize 10; int offset (page - 1) * pageSize; String sql SELECT * FROM books ORDER BY id DESC LIMIT ? OFFSET ?; PreparedStatement ps conn.prepareStatement(sql); ps.setInt(1, pageSize); ps.setInt(2, offset); ResultSet rs ps.executeQuery();在book_list.jsp生成分页链接!-- 计算总页数需先查总数 -- % int totalCount bookDao.getTotalCount(); // SELECT COUNT(*) FROM books int totalPages (int) Math.ceil((double) totalCount / 10); % div classpagination c:forEach begin1 end${totalPages} varp a hrefbook/list?page${p}${p}/a /c:forEach /div表格对比两种分页方式的核心差异方式SQL 示例优点缺点适用场景OFFSET 分页SELECT * FROM books LIMIT 10 OFFSET 20实现简单MySQL 5.7 原生支持OFFSET值大时性能骤降需扫描前 N 行数据量 10 万教学/内部系统游标分页推荐进阶SELECT * FROM books WHERE id ? ORDER BY id DESC LIMIT 10性能恒定无 OFFSET 瓶颈需前端传上一页最大 ID逻辑稍复杂数据量 10 万需长期维护我一般会在第 3 个实际项目中切换到游标分页因为OFFSET在图书系统中当管理员翻到第 200 页时OFFSET 2000查询会明显变慢。而游标分页只需WHERE id 12345索引直达。最后说一句血泪经验每次改完web.xml或JSP务必重启 Tomcat每次改完DAO或Servlet务必重新编译src/并覆盖webapps/library/WEB-INF/classes/下的.class文件——别信“热部署”JSPServlet 时代没有后悔药。希望帮到你。本文还有配套的精品资源点击获取