JSP+JDBC+MySQL+Servlet图书管理项目实战:从环境搭建到进阶优化
简介这是一套基于JSP、JDBC、MySQL与Servlet技术栈实现的图书管理系统完整源码面向Java Web初学者与课程设计、毕业设计需求者帮助其理解MVC模式在真实项目中的落地方式。压缩包共95个文件约1.7MB以37张jpg与4张png图片、17个java源文件、13个html页面、11个css样式、5个xml配置、3个js脚本及1个sql建表脚本为主涵盖视图页面、业务逻辑、数据库脚本与静态资源。项目按web-INF、jsp、servlet、lib、css、js等模块组织包含登录注册、图书列表、添加删除等典型功能Servlet负责请求控制JSP承担视图展示JDBC完成对MySQL的增删改查。已有275人学习下载适合通过阅读与修改源码掌握数据库连接、请求处理与页面交互等核心技能为后续Java Web开发打下基础。1. 从一份 jspjdbcmysqlservlet 图书管理项目源码说起这套老技术栈为什么还值得跑一遍很多人第一次接触 JavaWeb就是从一份 jspjdbcmysqlservlet 图书管理项目源码.zip 开始的。它不新甚至有点“土”但它是把 Java 后端最核心的几条链路——请求怎么进来、SQL 怎么发出去、结果怎么渲染回页面——压缩在一个能跑起来的小系统里。你把它跑通等于把 Servlet 生命周期、JDBC 连接、MySQL 建表和 JSP 页面渲染这四件事一次性串起来了。适合谁适合刚学完 Java 语法、想找一个完整案例练手的人也适合工作几年但一直用框架、想回头补一补底层细节的人。这一篇不讲空话就按“拿到源码后怎么落地”的顺序把环境、建库、连接、页面、排错一条条拆开。2. 环境与数据库先落地MySQL 安装配置和建库建表2.1 选 MySQL 5.7 还是 8.0先看驱动和连接串这套项目源码里通常带的是老版本mysql-connector-java常见是 5.1.x。这个驱动连 MySQL 5.7 最稳连 8.0 也能连但连接串要改。很多人卡在第一步不是代码问题是版本对不上。我的建议是如果你只是跑通这个图书管理项目装 MySQL 5.7.44 最省事如果你机器上已经有 8.0也不用卸改连接串和驱动即可。MySQL 5.7.44 是 5.7 系列最后一个版本官方在 5.7.43 之后又发了 5.7.44属于收尾维护版本拿来跑教学项目完全够。Windows 上安装时记住三个点端口默认 3306 别改、字符集选utf8mb4、root 密码设一个你记得住的。装完在命令行验证mysql -uroot -p # 输入密码后能看到 mysql 提示符就说明服务起来了如果提示net start mysql服务正在启动但一直没反应多半是 my.ini 路径或 data 目录权限问题先去看 MySQL 安装目录下的错误日志别急着重装。建库这一步项目里一般会带一个.sql文件。常见做法是先用命令行建库再执行脚本CREATE DATABASE library DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE library;mysql -uroot -p library library.sql这里有个容易翻车的点.sql文件如果是在 Windows 记事本里存过可能带 BOM 头导入时报语法错误。用 Notepad 或 VS Code 转成 UTF-8 无 BOM 再导。导入完用SHOW TABLES;确认表在图书管理项目一般至少有三张表图书表、用户表、借阅记录表。2.2 表结构和字段类型别照抄网上随便一份图书表常见字段是id、book_name、author、price、stock、type_id。这里有两个参数值得说price用DECIMAL(10,2)而不是FLOAT金额别用浮点stock用INT并设默认值 0对应热词里提到的“mysql设置默认值为0”库存为负是业务 bug不是数据库问题。CREATE TABLE book ( id INT PRIMARY KEY AUTO_INCREMENT, book_name VARCHAR(100) NOT NULL, author VARCHAR(50), price DECIMAL(10,2) DEFAULT 0.00, stock INT DEFAULT 0, type_id INT );建完表先手动插两条数据别等页面跑起来再查空结果。INSERT INTO book(book_name,author,price,stock) VALUES(JavaWeb实战,张三,59.00,10);这一步是给自己留后路后面 JDBC 查不出数据时你能立刻判断是数据库没数据还是代码没连上。3. JDBC 连接 MySQL驱动、URL 和连接池的最小可用写法3.1 驱动加载和连接串的四个关键参数JDBC 连接 MySQL 的核心就三行加载驱动、拿连接、执行 SQL。老项目里常见写法是Class.forName(com.mysql.jdbc.Driver)这是 5.1.x 驱动的类名如果你用的是 8.0 驱动类名变成com.mysql.cj.jdbc.Driver连接串也要加时区和 SSL 参数。// 5.1.x 驱动写法 Class.forName(com.mysql.jdbc.Driver); String url jdbc:mysql://localhost:3306/library?useUnicodetruecharacterEncodingutf8; Connection conn DriverManager.getConnection(url, root, 你的密码);// 8.0 驱动写法 Class.forName(com.mysql.cj.jdbc.Driver); String url jdbc:mysql://localhost:3306/library?useUnicodetruecharacterEncodingutf8 serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue; Connection conn DriverManager.getConnection(url, root, 你的密码);四个参数解释一下serverTimezone不写会报时区错误useSSLfalse是本地开发关掉 SSL避免“mysql ssl连接错误”allowPublicKeyRetrievaltrue是 8.0 驱动连本地库常需要的characterEncodingutf8不加中文书名会变问号。这几个参数就是热词里“jdbc连接mysql”最容易踩的坑。3.2 把连接封装成工具类别在每个 Servlet 里重复写图书管理项目里如果每个 Servlet 都写一遍DriverManager.getConnection代码会很难看连接也容易泄漏。常见做法是抽一个DBUtil工具类静态代码块加载驱动提供getConnection()和close()。public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/library?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse; private static final String USER root; private static final String PWD 你的密码; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PWD); } public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { try { if (rs ! null) rs.close(); } catch (SQLException e) {} try { if (ps ! null) ps.close(); } catch (SQLException e) {} try { if (conn ! null) conn.close(); } catch (SQLException e) {} } }逻辑说明静态块只执行一次驱动只加载一次close按 ResultSet、Statement、Connection 的顺序关反过来关会报错。参数说明URL 里的库名library要和建库时一致密码换成你自己的。这个工具类不引入连接池够教学项目用如果要做“mysql高并发解决方案”那得上 Druid 或 HikariCP但那是另一个话题先把单连接跑通。4. Servlet 与 JSP 串起来请求怎么进、页面怎么出4.1 Servlet 的 doGet 和 doPost图书列表怎么查出来Servlet 是这套项目的入口。浏览器访问/bookListTomcat 根据web.xml或注解找到对应 Servlet执行doGet里面调 JDBC 查数据把结果塞进 request再转发给 JSP。WebServlet(/bookList) public class BookListServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ListBook list new ArrayList(); String sql SELECT id, book_name, author, price, stock FROM book ORDER BY id DESC; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { Book b new Book(); b.setId(rs.getInt(id)); b.setBookName(rs.getString(book_name)); b.setAuthor(rs.getString(author)); b.setPrice(rs.getBigDecimal(price)); b.setStock(rs.getInt(stock)); list.add(b); } } catch (SQLException e) { e.printStackTrace(); } req.setAttribute(bookList, list); req.getRequestDispatcher(/bookList.jsp).forward(req, resp); } }逻辑说明用 try-with-resources 自动关连接比手动 close 更安全SQL 里ORDER BY id DESC对应热词“mysql排序”新书排前面。参数说明rs.getBigDecimal(price)对应 DECIMAL 字段别用getFloat。查完用forward而不是redirect因为 request 里的bookList要传给 JSP。4.2 JSP 页面渲染JSTL 比脚本片段干净JSP 里如果写一堆% %脚本片段页面会很难维护。常见做法是引入 JSTL用c:forEach遍历。% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % table border1 trthID/thth书名/thth作者/thth价格/thth库存/th/tr c:forEach items${bookList} varb tr td${b.id}/td td${b.bookName}/td td${b.author}/td td${b.price}/td td${b.stock}/td /tr /c:forEach /table逻辑说明${bookList}就是 Servlet 里setAttribute存进去的varb是循环变量。参数说明JSTL 需要jstl.jar和standard.jar放进WEB-INF/lib少一个就报NoClassDefFoundError。这一步跑通图书列表就出来了剩下的增删改就是照着这个模式复制。5. 避坑与排查这套项目最容易翻车的 5 个地方5.1 现象页面 404控制台没报错原因Servlet 的WebServlet(/bookList)路径和浏览器访问路径不一致或者web.xml里url-pattern写错。解决先看 Tomcat 启动日志里有没有注册这个 Servlet再核对访问地址注意项目 context path比如/library/bookList而不是/bookList。5.2 现象中文书名显示成问号原因数据库、表、连接串三处字符集不一致。解决建库用utf8mb4连接串加characterEncodingutf8JSP 页面顶部加% page contentTypetext/html;charsetUTF-8 %。三处都对了才不会乱码。5.3 现象ClassNotFoundException: com.mysql.jdbc.Driver原因驱动 jar 没放进WEB-INF/lib或者放的是 8.0 驱动但代码里写的是 5.1 的类名。解决确认 jar 在WEB-INF/lib下不是只加到了 IDE 的 build path类名和驱动版本对应上。5.4 现象连接一会儿就报Too many connections原因每次请求都新建连接且没关连接数堆满。解决确认DBUtil.close被调用或者改用 try-with-resources。教学项目并发低但反复刷新页面也能把连接耗光。5.5 现象Unknown system variable query_cache_size原因8.0 驱动连 5.7 库或者反过来驱动和数据库版本不匹配。解决统一版本5.7 库配 5.1 驱动8.0 库配 8.0 驱动连接串参数按对应版本写。6. 进阶技巧把这份源码改成能写进简历的项目跑通只是第一步真正让它有价值的是你能说清楚你改了什么。我的习惯是拿到这类源码后做三件事第一把 JDBC 裸连接换成 Druid 连接池配置文件放druid.properties这一步能让你在面试里讲“连接复用”和“mysql性能调优”的入门思路第二给图书表的book_name加索引用EXPLAIN看查询计划对应热词“mysql创建索引”数据量小看不出差别但你能讲清楚原理第三把借阅记录表的增删改包进事务用conn.setAutoCommit(false)加commit/rollback对应“mysql事务处理”借书和扣库存要么都成功要么都回滚。Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 扣库存 // 插借阅记录 conn.commit(); } catch (SQLException e) { if (conn ! null) conn.rollback(); e.printStackTrace(); } finally { if (conn ! null) try { conn.close(); } catch (SQLException e) {} }这段代码的价值不在跑通而在你能解释为什么需要事务。图书管理项目本身简单但“借书扣库存”这个场景天然适合讲事务面试时比背概念强得多。另外如果你想让项目看起来更完整可以加一个个人信息展示页面对应热词“jsp个人信息展示页面”从 session 里取登录用户渲染到 JSP 上这一步能把 Servlet 的 session 机制也串进来。最后说个血泪经验这类老项目源码别一上来就想着重构。先原样跑通再动一行测一行改坏了还能回退。我见过太多人拿到源码先改包名、换框架结果连登录都跑不起来最后连问题出在哪都不知道。跑通、理解、再改这个顺序别反。希望帮到你。本文还有配套的精品资源点击获取