ARTICLE DETAIL

资讯详情

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

JDBC+JSP+Servlet图书管理系统课设:原理、部署与进阶改造

JDBC+JSP+Servlet图书管理系统课设:原理、部署与进阶改造 简介面向Java Web初学者及课程设计/毕业设计场景这套基于JDBCJSPServlet的图书管理系统是一个可直接运行的完整项目内置数据库、源码和项目说明适合需要高分大作业或课设参考的读者。压缩包共141个文件、大小5.12MB其中包含24个Java源文件与48个class编译类并配有10个JSP页面、XML配置、jar依赖库、CSS样式及SQL数据库脚本整体结构清晰覆盖从建表到页面展示的完整链路属于规范的三层结构Java Web应用。从内容预览可见项目包含DAO、Service、Servlet控制层与实体层等典型组件对应数据访问、业务逻辑、实体建模与登录控制等关键模块。目前已有100人浏览学习适合正在完成图书管理系统类似题目的同学快速对照。资源另附项目说明文档下载后无需修改即可部署运行通过阅读源码可理解JDBC数据库操作、Servlet请求处理、JSP页面交互以及三层架构的协作方式也可作为扩展图书借阅、分类管理等功能的基线版本。1. 这个标题到底意味着什么课设高频项目的完整形态如果你在搜索引擎里输入“JDBCJSPServlet 图书管理系统”大概率会得到上百个同名项目。这个标题的核心并不在于“图书管理”这四个字而在于它用最经典的 Java Web 技术栈——JDBC 做数据库访问、JSP 做页面展示、Servlet 做请求控制——把一套增删改查业务完整地跑通。适合的人群非常明确正在做 Java Web 课程设计的学生、准备毕业设计但时间紧张的本科生以及刚学完 Servlet 和 JSP、想知道这三者如何协同工作的自学者。这套系统的价值在于“麻雀虽小五脏俱全”。它有登录认证、读者管理、图书管理、借书还书、逾期查询这些真实业务场景代码量不大但分层结构完整。拿到一个这样的源码包你要做的不是把代码看一遍就完事而是把它变成你能讲清楚、能改得动、能在答辩时回答老师追问的“自己的项目”。这就是本篇文章要帮你做的事从原理拆解到部署运行再到避坑和进阶改造一步步来。2. 先读懂系统骨架页面层、控制层、数据层怎么分工2.1 从 JSP、Servlet、JDBC 三个名词看系统分层很多初学者拿到源码包会先打开一个个 JSP 文件结果看到满屏的% %Java 代码和 HTML 混在一起立刻觉得头晕。然后他又打开 Servlet 文件发现有doGet、doPost方法又发现里面调用了某一个BookDao或BookService的类。再往下翻看到一个DBUtil类里面写着DriverManager.getConnection(...)。到这一步整个系统其实已经在眼前了。JSP、Servlet、JDBC 在这个项目里的分工是这样的JSP 负责“给人看的东西”也就是表格、表单、按钮这些界面元素Servlet 负责“接收请求并决定下一步做什么”比如用户点了“借书”按钮请求会先到达一个 BookServletJDBC 负责“真正操作数据库”比如执行一条 UPDATE 语句把图书库存减一。这三者构成了一个最典型的 MVC 变体JSP 相当于 ViewServlet 相当于 Controller而 DAOData Access Object配合 JDBC 承担了 Model 层的职责。需要特别提醒的是很多课设项目的 JSP 页面里直接写了 JDBC 代码虽然能跑但算不上好的分层。如果你拿到的源码在 JSP 里直接查数据库建议按照下文的 DAO 模式做一次小重构——这一步在答辩时非常加分因为它展示了你对“职责分离”的理解。2.2 一条借书请求在两个层级间的流转过程为了让你能讲清楚系统逻辑我们来走一遍借书流程。整个过程是这样的读者在borrow.jsp页面输入图书编号点击“借阅”按钮。浏览器以 POST 方式向BookServlet?actionborrow发送请求。容器Tomcat根据web.xml或注解配置找到对应的 Servlet 实例调用其doPost方法。Servlet 从请求中取出图书编号和当前登录用户的 ID封装成一个BorrowRecord对象。Servlet 调用BorrowDao.borrowBook(record)方法。DAO 方法内部通过 JDBC 获取数据库连接执行两条操作检查图书可借数量、插入借阅记录并更新库存。操作完成后Servlet 跳转到某个 JSP 页面显示“借阅成功”或“库存不足”的提示。这条链路里有一个容易被忽略但非常重要的地方第 6 步的两条数据库操作必须放在同一个事务里。如果只执行了插入借阅记录而库存更新失败数据就错乱了。源码包里如果用的是 JDBC 裸连接那么事务控制一般写成conn.setAutoCommit(false)然后执行多条语句最后conn.commit()任何一个异常都conn.rollback()。这部分代码你在阅读源码时值得重点标注因为它既是考试的高频考点也是老师最爱追问的点。2.3 拿到 zip 压缩包后先看哪几个目录不管你从哪里获得压缩包解压后大概率能看到以下结构名称稍有差异但逻辑一致目录/文件作用需要重点关注什么src/Java 源码目录找servlet、dao、entity或bean、util四个包WebContent/或web/Web 根目录找WEB-INF/web.xml它是整个 URL 映射的核心WebContent/WEB-INF/lib/依赖 jar 包确认是否有mysql-connector-java驱动没有的话后面的连接会直接失败sql/或database/数据库脚本.sql文件里有建库建表语句别急着跑先看是什么版本的 MySQL项目说明.doc或README.md文档看作者建议的 JDK/Tomcat/MySQL 版本这个信息能救你的命在这里提一个实操习惯解压后先不要急着导入 IDE先打开.sql文件和DBUtil.java或db.properties确认数据库账号密码、端口号这两个信息。九成运不起来的项目都死在这两处配置上。3. 数据库与 JDBC让系统真正“记住”数据的两个关键点3.1 图书管理系统最少需要几张表核心表设计与建表语句图书管理系统听起来业务很多但落到表结构上核心只有三类数据谁借的书读者、什么书图书、什么时候借还借阅记录。至于管理员表很多简化版系统会直接写死一个 admin 账号在代码里。如果你拿到的源码是这种写法建议自己补一张管理员表这是工作量低但答辩收益高的改动。下面给出最常见的四表设计方案你可以对照源码包里的.sql文件看是否一致。如果源码里的表名不同不要急着改先理解字段的语义再决定是否迁移。-- 读者表 CREATE TABLE reader ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, real_name VARCHAR(50), phone VARCHAR(20), max_borrow_num INT DEFAULT 5, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 图书表 CREATE TABLE book ( id INT PRIMARY KEY AUTO_INCREMENT, book_name VARCHAR(100) NOT NULL, author VARCHAR(50), publisher VARCHAR(100), isbn VARCHAR(20) UNIQUE, total_stock INT DEFAULT 1, current_stock INT DEFAULT 1, category VARCHAR(50) ); -- 借阅记录表 CREATE TABLE borrow_record ( id INT PRIMARY KEY AUTO_INCREMENT, reader_id INT NOT NULL, book_id INT NOT NULL, borrow_time DATETIME DEFAULT CURRENT_TIMESTAMP, due_time DATETIME, return_time DATETIME, status TINYINT DEFAULT 0 COMMENT 0-借阅中 1-已归还 2-逾期未还, KEY idx_reader (reader_id), KEY idx_book (book_id) );这张表设计有三个值得注意的参数考量max_borrow_num限制了单读者最大借阅量业务逻辑里借书前要查这个值做校验current_stock表示当前可借数量每次借出减一、归还加一这个字段比每次 COUNT 借阅记录要高效due_time是borrow_time加 30 天或 60 天计算出来的计算时机可以在插入记录时由 Java 代码算好也可以让数据库用DATE_ADD函数生成两种方式都可用但要注意时区一致性。3.2 JDBC 连接 MySQL驱动加载、URL 参数和连接复用的取舍JDBC 连接是整套系统能跑起来的命门。在 Java 6 之后Class.forName()这一行其实是可选的但大部分课设源码里还保留着留着也无妨。真正需要认真核对的是驱动类名和 URL 参数因为 MySQL 5.7 和 MySQL 8.x 的写法不一样环境驱动类名JDBC URL 中常见的附加参数MySQL 5.7 mysql-connector-java 5.xcom.mysql.jdbc.DriveruseUnicodetruecharacterEncodingutf8MySQL 8.0/8.4 mysql-connector-java 8.xcom.mysql.cj.jdbc.Driver需要追加serverTimezoneAsia/Shanghai下面给出一个常见的DBUtil.java核心代码用 try-with-resources 规避连接泄漏public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/library?useUnicodetrue characterEncodingutf8serverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(MySQL 驱动加载失败检查 lib 目录); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }这段代码的逻辑很清楚static块在类加载时执行一次驱动注册getConnection()每次调用都新建一条连接。这是课设项目最简做法不是最优做法——因为每次getConnection都建立 TCP 连接在并发场景下性能很差但在单机课设场景下完全够用。如果你后续想优化第 6 章会讲怎么换成连接池。关于 URL 参数useUnicodetruecharacterEncodingutf8这两项负责解决中文写入乱码serverTimezoneAsia/Shanghai负责解决 MySQL 8 的时区报错。很多人连接失败都是因为少了时区参数日志里会出现The server time zone value Öйú±ê׼ʱ¼ä is unrecognized看到这行字直接加参数即可。3.3 PreparedStatement 与事务借书还书里的 SQL 注入防线源码包里如果用的是Statement拼字符串方式那有一个值得立刻改掉的隐患——SQL 注入。比如登录查询写成下面的形式就非常危险// 反面教材千万不要抄 String sql SELECT * FROM reader WHERE username username AND password password ;正确的做法是使用PreparedStatement它在 SQL 语句预编译后通过占位符传参既安全又对数据库友好。下面给出借书 DAO 里最关键的“事务 预编译”代码片段public boolean borrowBook(int readerId, int bookId) { String checkStockSql SELECT current_stock FROM book WHERE id? FOR UPDATE; String updateStockSql UPDATE book SET current_stockcurrent_stock-1 WHERE id? AND current_stock0; String insertRecordSql INSERT INTO borrow_record(reader_id, book_id, due_time) VALUES(?,?,?); Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 PreparedStatement ps1 conn.prepareStatement(checkStockSql); ps1.setInt(1, bookId); ResultSet rs ps1.executeQuery(); int stock 0; if (rs.next()) { stock rs.getInt(1); // 取出当前库存 } rs.close(); ps1.close(); if (stock 0) { conn.rollback(); return false; // 库存不足直接回滚 } PreparedStatement ps2 conn.prepareStatement(updateStockSql); ps2.setInt(1, bookId); int rows ps2.executeUpdate(); ps2.close(); PreparedStatement ps3 conn.prepareStatement(insertRecordSql); ps3.setInt(1, readerId); ps3.setInt(2, bookId); ps3.setTimestamp(3, new Timestamp(System.currentTimeMillis() 30L*24*3600*1000)); ps3.executeUpdate(); ps3.close(); conn.commit(); // 全部成功才提交 return rows 0; } catch (SQLException e) { try { if (conn ! null) conn.rollback(); } catch (SQLException ignored) {} e.printStackTrace(); return false; } finally { try { if (conn ! null) { conn.setAutoCommit(true); conn.close(); } } catch (SQLException ignored) {} } }这个片段有三个值得展开说明的细节。第一SELECT ... FOR UPDATE是一把行级锁。在事务里先锁定图书行防止另一个并发请求同时读到同样的库存数造成超借。课设答辩时能说出这一句老师会认为你有实际的并发意识。第二UPDATE ... WHERE id? AND current_stock0这种写法是乐观锁思路的简化版——通过受影响行数判断是否真的执行成功避免先查后改之间出现数据窗口。第三finally块里手动close()是因为这个borrowBook方法没有使用 try-with-resources 包裹连接和声明连接必须显式归还。代码里对每个PreparedStatement和ResultSet都做了close()这一步的繁琐程度会因为加了连接池而降低但在课设阶段宁可啰嗦也不要泄漏连接。4. 把项目跑起来环境匹配、导入步骤与 Tomcat 部署4.1 先搭环境再做别的JDK、Tomcat、MySQL、驱动四件套的兼容组合很多人在这一步翻车。不是代码不行是环境组合不行。下面给出最稳妥的课设环境组合按稳定程度排序组合方案JDKTomcatMySQL驱动 jar推荐指数方案 AJDK 8Tomcat 8.5MySQL 5.7mysql-connector-java 5.1.49最稳妥方案 BJDK 8Tomcat 9MySQL 8.0mysql-connector-java 8.0.33最常用方案 CJDK 11Tomcat 9MySQL 8.4mysql-connector-java 8.0.33需注意依赖为什么首选 JDK 8因为这套技术栈本来就是 2015 年前后流行的组合JDK 8 对 JSP/Servlet 的支持最成熟不会有模块化导致的javax.servlet找不到问题。如果你装了 JDK 17会遇到动态加载驱动和反射访问受限的问题虽然能配通但对新手不友好。Tomcat 8.5 和 Tomcat 9 的差别在于前者默认支持 Servlet 3.1后者为 Servlet 4.0对于这种课设项目几乎没有功能区别。如果你拿到的代码里用了WebServlet注解两个版本都能跑如果用了web.xml映射也都能跑。真正决定生死的往往是 JDK 版本和驱动 jar 版本之间的配合。4.2 从解压 zip 到浏览器能访问完整操作步骤下面是一份我在本地反复验证过的操作路径无论你用的是 Eclipse 还是 IDEA逻辑相同第一步解压并把sql目录下的脚本导入数据库。在命令行或 Navicat 中执行mysql -u root -p library library.sql如果还没有数据库先创建mysql -u root -p CREATE DATABASE library DEFAULT CHARACTER SET utf8mb4;注意这里顺手指定了utf8mb4字符集比utf8更好支持中文和生僻字。如果你的.sql文件里已经带了建库语句那直接执行整个脚本即可。第二步修改数据库配置。找到src/util/DBUtil.java或db.properties把 URL、USER、PASSWORD 改成你本机的值。改完之后有一个验证技巧直接用 JUnit 或写一个 main 方法调用DBUtil.getConnection()能连上再继续部署这样能提前隔离数据库问题。第三步导入 IDE。IDEA 中选择File - New - Project from Existing Sources选中解压后的根目录一路 Next。导入后要确认两件事src目录被标记为 Sources RootWebContent或webapp被标记为 Web 资源目录。这两步如果没做对IDEA 会把 JSP 当纯文本处理编译时根本不报错但运行时 404。第四步配置 Artifact。在Project Structure - Artifacts中新建 Web Application Exploded把依赖的 jar 包加进去。这里有一个血泪经验IDEA 默认不会把WEB-INF/lib下的 jar 打包到部署产物里你必须确认 Artifact 的可用元素里包含了所有依赖 jar。否则就会出现启动时一切正常、一访问数据库就报 ClassNotFoundException 的情况。第五步配置 Tomcat 并启动。点击 Run 之后IDEA 会在底部输出 Tomcat 日志看到Server startup in [xxxx] milliseconds字样说明启动成功访问http://localhost:8080/项目名/如果没配置 context path默认是根路径大多数课设项目会带一个/Library或/bookmanage前缀注意看控制台或项目说明文档。4.3 跑通之后的自测清单别只查登录功能我把一套自测清单写在这里每一步都对应这个系统里的一条核心链路。你只需要按顺序操作任何一个环节报错就对应着某一块代码需要修自测项目操作方式预期结果登录使用项目说明中的管理员账号登录跳转后台首页图书列表在图书管理页查询全部表格显示种子数据新增图书填入书名、作者、ISBN列表中出现新记录借书选择读者和图书点击借阅库存减一生成借阅记录还书点击还书库存加一记录状态改为已归还超期查询手动改数据库 due_time 为昨天列表显示逾期记录你可能会说这还不够覆盖所有功能但重点是顺序——登录是 JSP Servlet JDBC 三层的首次全链路打通图书列表验证了读链路新增图书验证了写链路借书还书验证了事务和状态流转。这五条链路走通这个项目你就可以说“运行无误”了。5. 避坑这些年我见到的 JavaWeb 课设最容易翻车的五个地方5.1 现象启动后访问首页直接 404原因绝大多数情况不是代码问题而是 Artifact 部署名和访问路径不一致。IDEA 中 Application context 默认是http://localhost:8080/项目名_war_exploded/而你访问时可能带了别的路径。另一个原因是 JSP 文件不在 Web 根目录下被放到了src/main/java里。解决打开 Run Configuration把 Deployment 里的 Application context 改成/或与项目说明一致的路径然后重新启动。如果 JSP 位置不对把它移动到WebContent下再刷新 Artifact。5.2 现象运行报ClassNotFoundException: com.mysql.cj.jdbc.Driver原因这几乎是课设项目里出现频率最高的报错之一。两种情况——要么是驱动 jar 没放在WEB-INF/lib目录下要么是 IDEA Artifact 没有把这个目录里的 jar 打包进去。很多学生会把 jar 放在 External Libraries 里但 Tomcat 运行时不认这个位置。解决先确认WebContent/WEB-INF/lib/下是否真的有mysql-connector-java-x.x.x.jar没有就手动复制进去。然后在Project Structure - Artifacts - Available Elements勾选该 jar重新构建。这两步做完后重启 Tomcat注意不是点重新部署而是把 Tomcat 停掉再启动否则有时会加载旧 classpath。5.3 现象能连上数据库但中文数据全是问号原因三个环节都可能出问题。JSP 页面本身的编码不是 UTF-8Servlet 里request或response没设置编码JDBC URL 缺少characterEncodingutf8。这三个问题互相独立任何一个出错都导致乱码。解决一次性把三个地方都改掉。JSP 顶部加% page contentTypetext/html;charsetUTF-8 languagejava %Servlet 的doPost和doGet开头写request.setCharacterEncoding(UTF-8); response.setContentType(text/html;charsetUTF-8);JDBC URL 里带上useUnicodetruecharacterEncodingutf8。如果你在建库时用的是 utf8mb4连接 URL 也可以写成characterEncodingutf8mb4可靠性更好。5.4 现象本地跑得好好的换成别人的电脑就连不上数据库原因数据库的 host 写死为localhost而对方电脑上的根账号密码和你不一样。此外MySQL 默认只允许本地登录如果换一台电脑访问你的 MySQL需要授权远程访问。解决把DBUtil.java中的 IP 从localhost改为127.0.0.1并确认目标机器 MySQL 账号密码对应。如果确实是需要跨机器访问在 MySQL 中执行GRANT ALL PRIVILEGES ON library.* TO root% IDENTIFIED BY 你的密码; FLUSH PRIVILEGES;不建议对 root 做全权远程授权如果一个课设需要多人共享数据库更合适的做法是创建一个受限账号比如只授予SELECT, INSERT, UPDATE, DELETE权限到一个单独的库。5.5 现象Tomcat 启动正常但页面只显示静态内容动态数据全部空白原因JSP 编译失败。查看C:\Users\xxx\AppData\Local\JetBrains\IDEA\your-version\tomcat\logs下的日志往往能看到Unable to compile class for JSP。最常见的原因是 Servlet API 版本不匹配——Tomcat 8.5 与 Tomcat 9 自带的 Servlet API 不同IDE 中引入的 servlet-api.jar 与运行时冲突。解决删除项目里不必要的 servlet-api.jar 和 tomcat-servlet-api.jar因为 Tomcat 自带这些依赖重复引入会造成类加载器冲突。保留 MySQL 驱动 jar 即可其他 Tomcat 相关 jar 一律不要放在WEB-INF/lib下。修改后重启 TomcatJSP 会重新编译。6. 不要停在“能跑”从这套系统出发的三个进阶改造方向既然项目已经能在本地跑通下一步不是写论文而是把这套系统改造成一个能写进简历的“有亮点”项目。我建议你在时间有限的情况下优先做以下三个改造。第一个改造把 JDBC 裸连接换成 Druid 连接池。做法是删掉DBUtil里的DriverManager.getConnection()换成 Druid 的配置方式DruidDataSource dataSource new DruidDataSource(); dataSource.setUrl(URL); dataSource.setUsername(USER); dataSource.setPassword(PASSWORD); dataSource.setInitialSize(5); dataSource.setMaxActive(10); dataSource.setMinIdle(2);换完之后数据源只初始化一次后续调用dataSource.getConnection()就能复用物理连接。这一步在简历上可以写“解决了原生 JDBC 连接频繁创建导致的性能开销”在答辩时老师几乎必问连接池原理你只要答出“复用连接、控制最大连接数、避免连接泄漏”这三句话就够了。第二个改造用 Filter 处理所有页面的编码和登录拦截。你现在手头这个系统的 Servlet 里大概率到处写着setCharacterEncoding(UTF-8)这属于重复代码。写一个EncodingFilter实现javax.servlet.Filter在doFilter里统一设置编码然后在web.xml里配置映射从此删掉所有 Servlet 里的编码语句。第三个改造给借阅记录加超期判断。现在的系统可能在查询时用 SQL WHERE 判断逾期但更稳妥的做法是每次读者登录后检查其所有status0的记录如果due_time小于当前时间更新为status2。这一步既用到 JDBC 更新又锻炼了批量处理逻辑代码量很小但业务完整性提升很大。我自己的习惯是拿到任何一个新项目先把数据库脚本导入然后在纸上画出表关系和页面跳转图最后才开始看代码。这套方法让我避开了很多“看了一个小时源码还是不知道从哪改起”的问题。希望帮到你——现在你可以解压那个 zip试着把这套系统跑起来再一点点让它变成你自己的作品。本文还有配套的精品资源点击获取
返回列表