尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

JDBC+JSP+Servlet图书管理系统实战:从环境搭建到避坑全指南

JDBC+JSP+Servlet图书管理系统实战:从环境搭建到避坑全指南 简介基于JDBCJSPServlet架构的图书管理系统完整项目面向Java Web初学者及课程设计、毕业设计人群可用于快速实现图书信息管理、借阅归还等核心功能。项目内含完整源码、数据库脚本及项目说明文档按指引配置环境即可直接运行无需额外修改属于95分以上高分必过级别的完整方案。资源包共141个文件以Java类文件、JSP页面、SQL脚本、XML配置及少量JAR依赖为主同时包含说明文档和配置文件整体仅5.12MB便于部署与分享。已有100人学习浏览内容完整性高适合作为课程设计参考。通过项目可掌握JDBC数据库连接、Servlet请求处理、JSP页面渲染以及三层架构分层设计等关键技能也可作为课程答辩与功能扩展的起点适合需要快速完成高要求作业或入门Java Web开发实践的读者。1. 拿到压缩包先别急着导入这套 JDBCJSPServlet 图书管理系统帮你省掉的三类麻烦如果你正在做基于 JSP 的毕设选题或是刚学完 JavaWeb 想找一个完整项目练手这个压缩包给的正是最经典的一套组合JDBC 负责和 MySQL 打交道JSP 负责把页面画出来Servlet 在中间接收请求、调数据、跳页面。源码、数据库脚本、项目说明三样齐全意味着你不用再东拼西凑去拼一个能跑的 Demo——至少起步阶段别人踩过的坑已经被填平了大半。但我要先说句泼冷水的话直接「导入即运行」在这个项目上不太现实更常见的是导入后第一次启动就翻车。原因多半不在代码而在环境错位JDK 版本、Tomcat 版本、MySQL 版本、驱动 jar 版本四个东西只要有一个对不上整套就跑不起来。这篇文章就顺着「先懂架构、再配环境、后踩坑、最后验证」的顺序把从拿到 zip 到能上台演示的全过程拆给你看。2. JDBCJSPServlet 三层架构图书管理系统的骨架为什么这样搭2.1 把三层职责拆开Servlet 只做调度JSP 只做展示JDBC 只做存取很多初学者拿到一个 JavaWeb 项目第一反应是找「哪个文件是首页」然后跟着链接点。点着点着就迷路了因为请求会从 JSP 跳到 Servlet又从 Servlet 跳回另一个 JSP中间还夹着 DAO 和工具类。要真正读懂这个图书管理系统先别管具体代码先把三层各自管什么想清楚。这套结构对应的是 JSP Model 2 思想也就是 MVC 在 JavaWeb 里的落地形态。Servlet 是控制器接收浏览器发来的请求解析参数调用业务方法最后决定跳转到哪个页面JSP 是视图只负责把数据渲染成 HTML里面尽量不写 Java 逻辑JDBC 封装在 DAO 层和工具类里是模型的一部分负责对 MySQL 执行增删改查。三者各干各的活耦合度低改页面不用动数据库代码换数据库不用改 JSP。组件角色典型工作对应包/目录Servlet控制器接收请求、调 DAO、跳转页面com.xxx.servletJSP视图展示列表、接收表单输入WebContent 下的 .jspJDBC DAO模型Connection、PreparedStatement、结果集封装com.xxx.dao、com.xxx.util如果你发现某个 JSP 里直接写了Class.forName或者DriverManager.getConnection说明这个项目的分层并不彻底读起来会费劲一些。常见的做法是单独抽一个JDBCUtil工具类所有 DAO 都通过它拿连接这样连接参数只需要维护一份。2.2 一次「添加图书」请求的完整流转从表单到数据库再回到列表页理解这套系统最快的方式是跟着一个请求走完全程。以「管理员添加一本新书」为例流程是这样的浏览器打开addBook.jsp填写书名、作者、价格点击提交后表单以 POST 方式发到addBook这个地址AddBookServlet的doPost方法被触发。Servlet 里的逻辑很固定先设置请求编码再逐个取出表单参数组装成一个 Book 对象调用BookDao.insert(book)。DAO 方法内部通过 JDBCUtil 拿到 Connection用 PreparedStatement 执行 INSERT返回受影响行数。最后 Servlet 根据结果做跳转——成功就重定向到图书列表页失败就把错误信息塞进 request 域再 forward 到错误页。WebServlet(/addBook) public class AddBookServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 处理中文乱码POST 请求参数必须指定 UTF-8 request.setCharacterEncoding(UTF-8); // 2. 从表单取参数注意字段名要和 JSP 里的 name 一致 String bookName request.getParameter(bookName); String author request.getParameter(author); double price Double.parseDouble(request.getParameter(price)); // 3. 调用 DAO 层执行插入返回 1 表示成功 BookDao dao new BookDao(); Book book new Book(bookName, author, price); int rows dao.insertBook(book); // 4. 根据结果决定跳转成功重定向到列表页避免刷新重复提交 if (rows 0) { response.sendRedirect(request.getContextPath() /listBook); } else { request.setAttribute(msg, 添加失败); request.getRequestDispatcher(/error.jsp).forward(request, response); } } }这里有两个容易被忽略的细节。第一request.getContextPath()返回的是项目部署名写重定向路径时一定带上它否则换项目名后链接全部失效第二成功用sendRedirect失败用forward前者是浏览器重新发一次请求后者是服务端内部转发——搞混了会出现表单重复提交或地址栏和页面内容对不上的情况。再往下走BookDao内部大概长这样典型的 JDBC 增删改查模板闭眼都能背下来public int insertBook(Book book) { String sql INSERT INTO t_book (book_name, author, price, stock) VALUES (?, ?, ?, ?); try (Connection conn JDBCUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, book.getName()); ps.setString(2, book.getAuthor()); ps.setDouble(3, book.getPrice()); ps.setInt(4, 1); return ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); return 0; } }PreparedStatement的?占位符能防止 SQL 注入这是这套代码里最值得学的习惯。try-with-resources写法让 Connection 和 PreparedStatement 自动关闭不用手动写 finally省心不少。但要注意DAO 层把 SQLException 吞掉后只返回 0Servlet 拿到的信息有限排查问题时多半要靠e.printStackTrace()输出的日志。2.3 源码该按什么顺序读四个包加一份 SQL 脚本的阅读路线拿到压缩包解压后你看到的目录结构通常不是一堆文件乱放而是有规律的。读懂这个规律比逐行看代码更省时间。我一般建议先忽略 JSP从数据库和工具类读起由底向上看。推荐的阅读顺序是这样的第一份先看数据库脚本搞清楚有几张表、表之间什么关系、有哪些测试账号——这是整个系统的地基表结构没看明白后面所有 SQL 都是黑匣子第二份看JDBCUtil工具类确认连接哪个库、用户名密码是什么第三份看 DAO 层把每张表对应的增删改查方法列出来第四份看 Servlet 层对照WebServlet注解或 web.xml 里的映射理清每个 URL 对应哪个动作最后再打开 JSP此时页面上每个表单、每个链接指向哪个地址你已经能自己推断出来了。包名通常是com.xxx.bean实体类、com.xxx.dao数据访问、com.xxx.servlet控制器、com.xxx.util工具类有些项目还会加com.xxx.filter放编码过滤器。如果看到的是entity、mapper这类命名也只是习惯差异职责是一样的。这个阅读路线同样适用于其他 JavaWeb 课设项目——CRUD 的骨架几乎都长这样换的只是表和字段名。3. 本地跑通最小环境JDK、Tomcat、MySQL 的版本匹配与第一次启动3.1 版本匹配是第一道门槛JDK 8 Tomcat 9 MySQL 5.7/8.0 搭配表这套系统能不能跑起来第一道门槛不是代码是环境。我见过太多人卡在「源码没问题但就是 500」上查到最后是 Tomcat 10 的包名从javax.servlet变成了jakarta.servlet而项目里全是用javax写的直接编译报错。所以环境版本这事得放在所有操作前面。组件推荐版本说明JDK1.88u202 及之前最稳别用太高版本JDK 17 会有模块化报错风险Tomcat8.5 或 9.0不要用 Tomcat 10javax与jakarta包名不兼容MySQL5.7 或 8.05.7 用旧驱动8.0 用新驱动二者写法不同mysql-connector-java5.1.49 或 8.0.x关键JDBC 驱动版本必须和 MySQL 版本对得上Eclipse / IDEA任意较新版本IDE 版本不太影响运行影响的是 Tomcat 集成方式一个很现实的建议如果项目说明里写了环境要求先按它来如果没写默认 JDK 8 Tomcat 9 MySQL 5.7 驱动 5.1.49 是兼容性最好的组合。MySQL 5.7 对驱动要求宽松而 MySQL 8.0 必须要com.mysql.cj.jdbc.Driver这个新驱动类URL 参数也比 5.x 多要求两个后面避坑章节会细说。3.2 用 Eclipse/IDEA 导入源码两个必改配置与 jar 的放置位置Eclipse 用户的操作路径一般是File → Import → Existing Projects into Workspace → 选择解压后的目录。IDEA 用户则是 Open 选中解压目录等它识别成 Maven 或普通 Web 项目。导入后先别急着启动先改两个地方。第一个必改配置是编译级别。右键项目 → Properties → Java Compiler把 Compiler compliance level 调成和你本机 JDK 一致通常是 1.8再在 Java Build Path 里确认 JRE System Library 没有红色叉号。第二个必改配置是 Tomcat 运行时。项目上右键 → Properties → Targeted Runtimes勾选你配置好的 Tomcat 版本如果不勾Eclipse 不会把项目发布到 Tomcat 的 webapps 下。最容易被忽略的是驱动 jar 的放置位置。很多人把mysql-connector-java.jar加进了 Build Path在 IDE 里编译通过一启动 Tomcat 就报ClassNotFoundException。原因在于 Tomcat 运行时的类加载机制WEB-INF/lib 下的 jar 才会被加载Build Path 里的 jar 只对编译生效。常见做法是把 jar 复制到WebContent/WEB-INF/libEclipse或src/main/webapp/WEB-INF/libIDEA目录下。这个细节卡掉过无数新手属于典型的「环境对目录不对」问题。3.3 执行数据库脚本建库建表插测试账号的三个细节数据库脚本一般叫bookdb.sql、library.sql或者init.sql打开后你会发现里面有完整的建库、建表、插入语句。执行方式有两种一种是直接用 Navicat 或 MySQL Workbench 打开 sql 文件执行另一种是在命令行里用 source 命令。mysql -uroot -p Enter password: ******** source C:/booksystem/bookdb.sql;执行时注意三个细节。第一确认脚本开头有没有CREATE DATABASE IF NOT EXISTS如果没有你得手动先建库再执行否则会报「No database selected」第二脚本里如果用了utf8mb4字符集MySQL 5.5.3 以下版本不支持但现在基本不会遇到这么老的版本第三脚本里插入的测试账号密码通常是明文比如admin / 123456先记下来后面登录页面要用。执行成功后用SHOW TABLES;确认表都建出来了。常见的表有t_book图书、t_user用户、t_borrow借阅记录具体表名以脚本为准。如果发现表没建全大概率是脚本执行到一半报错中断了修复报错后重新 source 一遍即可——脚本通常写成可重复执行的形式里面会带DROP TABLE IF EXISTS。3.4 改 JDBC 连接参数driver、url、username、password 四个值怎么填数据库建好了接下来要把项目里的连接信息改成你本机的。连接参数一般集中在两类位置独立的db.properties配置文件或者JDBCUtil.java类开头的静态变量。如果是配置文件类里面会用Properties.load()读取——这也是更推荐的做法改参数不用重编译。drivercom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/bookdb?useSSLfalsecharacterEncodingutf8serverTimezoneAsia/Shanghai usernameroot password你的数据库密码这四个值逐个解释。driver是驱动类全名MySQL 8.0 用com.mysql.cj.jdbc.Driver5.7 用com.mysql.jdbc.Driver用错了会报ClassNotFoundException。url里localhost:3306是数据库地址和端口bookdb是库名后面的三个参数分别关闭 SSL、指定 UTF-8 编码、设置时区——MySQL 8.0 缺serverTimezone会直接报时区错误。username和password填你本机 MySQL 的登录凭据不是脚本里那个测试账号别搞混了。改完之后验证配置是否生效不需要启动 Tomcat写个简单的 main 方法或者用 JDBC 工具类直接跑一下连接测试。如果getConnection()不抛异常说明数据库这一环已经通了再启动项目就有了底气。很多人的思维误区是「项目启动报错才去查数据库配置」其实提前单独验证连接能把问题范围缩小一半。4. 避坑从编译报错到页面 500 的六个排查记录这一章的每条记录都是我在实际跑这类课设项目时反复遇到过的按「现象 → 原因 → 解决」的顺序写你照着排就行。4.1 ClassNotFoundException驱动 jar 没进 WEB-INF/lib现象Tomcat 一启动控制台报java.lang.ClassNotFoundException: com.mysql.jdbc.Driver但项目在 IDE 里编译完全正常。原因前面提过编译期和运行期的 classpath 不是一回事。IDE 的 Build Path 只保证代码能通过编译Tomcat 实际加载类时只会扫描 WEB-INF/lib 和 WEB-INF/classes。jar 没放对位置运行时自然找不到驱动类。解决把 mysql 驱动 jar 复制到 WEB-INF/lib 下然后在 IDE 里刷新项目重新发布重启 Tomcat。还没解决的话检查是不是复制到了 src 目录下、类路径被 IDE 缓存住——右键项目 → Clean 之后再重启基本能好。这个错误排掉后后面还有一长串和数据库相关的报错所以把它放在第一条。4.2 中文全是问号url、数据库排序规则、页面编码三层都要对齐现象页面显示正常但往数据库里插入中文后表里存的是??或者反过来页面上的中文全是乱码。原因字符集没对齐。这套链路里任何一环编码不一致中文就会出问题——JSP 页面的pageEncoding、Servlet 的请求编码、JDBC URL 里的characterEncoding、数据库表和字段的排序规则四个环节缺一不可。解决按三层逐一排查。数据库层面建库时指定DEFAULT CHARACTER SET utf8mb4已存在的表用ALTER TABLE t_book CONVERT TO CHARACTER SET utf8mb4;转换。JDBC 层面URL 里加上characterEncodingutf8。代码层面Servlet 顶部加request.setCharacterEncoding(UTF-8)JSP 顶部确认有pageEncodingUTF-8。另外注意用 Navicat 查看数据时也把连接编码设成 UTF-8否则数据库里是对的工具显示成乱码白排查半天。4.3 Servlet 路径 404注解映射与 web.xml 到底以哪个为准现象点页面上的链接地址栏 URL 看起来正确但 Tomcat 返回 404。检查WebServlet(/listBook)没问题web.xml 里也有对应的servlet-mapping。原因项目同时用了注解和 web.xml 两套配置或者 web.xml 中配置了一个旧路径注解配置的是新路径实际生效的是 web.xml 里那份。还有一种情况是链接地址写死了/listBook而项目部署名不为空比如/bookmanager/listBook才能访问少了项目名就是 404。解决统一用一种配置方式推荐注解因为省事且直观。访问路径统一用request.getContextPath()拼接或者在 JSP 里写${pageContext.request.contextPath}/listBook。如果确定用的是 web.xml就检查url-pattern的写法注意 Servlet 的servlet-name要和servlet-mapping里完全一致大小写都不能差。4.4 MySQL 8.0 连不上useSSLfalse 与 serverTimezone 缺一不可现象控制台报Establishing SSL connection without servers identity verification is not recommended警告紧跟着是The server time zone value Öйú±ê׼ʱ¼ä is unrecognized驱动类已经改成com.mysql.cj.jdbc.Driver了还是不行。原因MySQL 8.0 的 JDBC 驱动默认要求 SSL 校验和时区明确指定。本地开发连的是没有配置 SSL 的 MySQL 服务而时区没有指定时驱动会读取系统默认时区一旦不是驱动认识的格式就抛异常。这个报错两个坑叠在一起排查时容易顾此失彼。解决在 URL 里同时加上两个参数一个都不能少urljdbc:mysql://localhost:3306/bookdb?useSSLfalseserverTimezoneAsia/ShanghaiuseSSLfalse是本地开发的标准写法生产环境另说serverTimezoneAsia/Shanghai指定东八区。如果还是报错看下 MySQL 服务时区命令行执行SELECT global.time_zone;返回 SYSTEM 的话可以执行SET GLOBAL time_zone 08:00;再重启服务两者取其一即可。4.5 改了数据库连不上旧表脚本版本与库结构不同步现象系统能登录但点进某个功能页面报 SQL 语法错误或者Unknown column xxx in field list。你的数据库是重新导入的源码是最新的对不上。原因压缩包里的 SQL 脚本和源码不是同一个版本。源码里的 DAO 层写了某个字段而脚本里建的表还没有这个列。这类项目经常改版源码更新了脚本回溯不完整就会出现「代码跑得比表结构快」的情况。数据库同步工具只能保证数据一致保证不了表结构和代码版本的匹配尤其当你手动改过表之后更容易踩中。解决以源码为准反推表结构。打开报错对应的 DAO 类把里面用到的所有字段列出来对照数据库表结构缺哪列补哪列ALTER TABLE t_borrow ADD COLUMN return_time DATETIME DEFAULT NULL;改完之后把这张表的结构导出留存。以后每次跑新代码先对比一次表结构避免重复踩。做课设答辩前强烈推荐用新库重新执行一遍完整脚本跑通全流程——这是检验脚本完整性的唯一标准别拿改过的旧库当验证环境。4.6 Tomcat 启动端口被占用先杀进程还是先改端口现象启动 Tomcat 时控制台报Port 8080 required by Tomcat v9.0 Server at localhost is already in use服务起不来。原因8080 端口被占用了。占用它的可能是另一个 Tomcat 实例也可能是其他软件——之前遇到过一次是某些数据库同步工具的 Web 管理端默认占了 8080排查时根本想不到。解决先确定是谁占的再决定杀还是改。命令行执行netstat -ano | findstr 8080 taskkill /PID 占用的PID /F如果是残留的 Java 进程杀掉重开就行。要是这台机器上有多个项目经常要跑或者端口被系统服务占用不便杀就改 Tomcat 端口——在 IDE 的 Server 配置里改 HTTP port或者在 Tomcat 安装目录conf/server.xml里改Connector port8080。注意 IDE 里改的和 server.xml 里改的要保持一致否则 IDE 会报端口不一致的警告。5. 把「能跑」变成「能答辩」三条验证路径与一个备份习惯5.1 按「读者 管理员」双角色验证功能闭环图书管理系统通常有读者和管理员两种角色。不要只验证管理员能登录、能加书就收工要把读者的完整链路也走一遍。读者能做的事一般包括检索图书、查看图书详情、借书、还书、查看个人借阅记录——这里面「查看个人借阅记录」就是典型的 JSP 个人信息展示页面也是评委喜欢追问的地方。验证时按业务场景走而不是按菜单点读者 A 借了一本书 → 库存减一 → 读者 A 的借阅列表出现记录 → 还书后记录闭合库存加一。每一步都要回到数据库确认数据真的变了而不是只看页面提示「操作成功」。很多项目页面好看数据对不上一问细节就露馅。5.2 用一组 SQL 清单验证数据完整性与关联关系页面验证属于黑盒还得用 SQL 做白盒检查。设计好场景后执行下面这类查询来核对结果-- 检查每张表的记录数是否和页面显示一致 SELECT COUNT(*) FROM t_book; SELECT COUNT(*) FROM t_user; SELECT COUNT(*) FROM t_borrow; -- 检查借阅记录和图书/用户的关联是否完整 SELECT b.book_name, u.username, br.borrow_time, br.return_time FROM t_borrow br JOIN t_book b ON br.book_id b.id JOIN t_user u ON br.user_id u.id;重点看三件事关联查询不报错、没有冗余数据、时间字段格式正确。如果借阅表里出现了图书表不存在的 book_id说明借书时没有做外键约束或者代码里没校验这类逻辑漏洞答辩时很容易被问出来。提前用 SQL 查一遍比现场手忙脚乱翻代码强得多。5.3 每次改动都留「后悔药」版本化 SQL 脚本开发的习惯决定了项目能不能长期维护。我的做法是每次对数据库有结构变更就导出一份新的 SQL 脚本文件名带上版本号比如bookdb_v1.0.sql、bookdb_v1.1.sql。用的是 Navicat 或 mysqldump 都可以mysqldump -uroot -p --databases bookdb bookdb_v1.1.sql导出后在项目说明里追加一小段变更日志写上改了什么表、加了什么字段、为什么改。这样哪怕后面改崩了也能随时回滚到上一个可用版本。这个习惯在课设阶段看不出多大价值等代码量上来、要同时维护多套环境时它就是你的后悔药。这几条都做到之后这个项目就不只是「能跑」而是能经得起追问、能拿得出手。我每次做完一个 JavaWeb 项目都会强迫自己走一遍双角色流程、跑一遍 SQL 校验、导一份带版本号的脚本备份这套流程虽然琐碎但帮我避开过不少答辩现场的尴尬时刻。希望你也能在动手之前先把这几点沉淀成习惯希望帮到你。本文还有配套的精品资源点击获取
返回列表