简介:这是一套基于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. 从一份 jsp+jdbc+mysql+servlet 图书管理项目源码说起:这套老技术栈为什么还值得跑一遍
很多人第一次接触 JavaWeb,就是从一份 jsp+jdbc+mysql+servlet 图书管理项目源码.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?useUnicode=true&characterEncoding=utf8"; Connection conn = DriverManager.getConnection(url, "root", "你的密码");// 8.0 驱动写法 Class.forName("com.mysql.cj.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/library?useUnicode=true&characterEncoding=utf8" + "&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true"; Connection conn = DriverManager.getConnection(url, "root", "你的密码");四个参数解释一下:serverTimezone不写会报时区错误;useSSL=false是本地开发关掉 SSL,避免“mysql ssl连接错误”;allowPublicKeyRetrieval=true是 8.0 驱动连本地库常需要的;characterEncoding=utf8不加,中文书名会变问号。这几个参数就是热词里“jdbc连接mysql”最容易踩的坑。
3.2 把连接封装成工具类,别在每个 Servlet 里重复写
图书管理项目里如果每个 Servlet 都写一遍DriverManager.getConnection,代码会很难看,连接也容易泄漏。常见做法是抽一个DBUtil工具类,静态代码块加载驱动,提供getConnection()和close()。
public class DBUtil { private static final String URL = "jdbc:mysql://localhost:3306/library?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false"; 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 是这套项目的入口。浏览器访问/bookList,Tomcat 根据web.xml或注解找到对应 Servlet,执行doGet,里面调 JDBC 查数据,把结果塞进 request,再转发给 JSP。
@WebServlet("/bookList") public class BookListServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { List<Book> 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 prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <table border="1"> <tr><th>ID</th><th>书名</th><th>作者</th><th>价格</th><th>库存</th></tr> <c:forEach items="${bookList}" var="b"> <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存进去的;var="b"是循环变量。参数说明: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,连接串加characterEncoding=utf8,JSP 页面顶部加<%@ page contentType="text/html;charset=UTF-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 机制也串进来。
最后说个血泪经验:这类老项目源码,别一上来就想着重构。先原样跑通,再动一行测一行,改坏了还能回退。我见过太多人拿到源码先改包名、换框架,结果连登录都跑不起来,最后连问题出在哪都不知道。跑通、理解、再改,这个顺序别反。希望帮到你。
本文还有配套的精品资源,点击获取