JavaWeb增删改查全流程解析:从源码到部署与排错 简介适合初学Web开发者的JavaWeb增删改查项目源码以MVC分层结构演示JSP、Servlet与数据库交互的完整流程帮助理解商品管理、用户管理等场景下的数据插入、删除、更新与查询实现覆盖从页面表单提交到后台业务处理再到数据持久化的完整链路。压缩包共40个文件包括9个JSP页面、9个Java类及对应class文件另有SQL建表脚本、XML配置和项目工程文件源码目录清晰可直接导入IDE运行调试。包体仅920KB轻量易用已有3753人学习下载。通过该项目可掌握JDBC操作MySQL、Servlet请求转发与重定向、JSP标签使用等核心技能同时理解MVC模式如何分离视图、控制器与数据模型并附带SQL脚本与基础安全验证思路是快速上手JavaWeb开发的实用参考。1. 拿到「javaweb增删改查含源码.zip」之前先想清楚它解决什么问题「javaweb增删改查含源码.zip」是课程设计和入门资源里最常见的一类工程包一个用 JSP Servlet JDBC MySQL 写好的完整增删改查案例解压后能直接导入 IDE 运行。它的价值不在代码多高级而在于把「浏览器发请求 → Servlet 接收 → DAO 拼 SQL → MySQL 返回 → JSP 渲染」这条全链路用最小依赖摊开给你看。适合三类人要交 Web 课程设计的在校生、刚从 JavaSE 转 Web 想找完整案例参照的新人、需要一两天内搭出内部数据维护后台的从业者。反直觉的是先把它跑通再去看 Spring Boot控制器、Service、Mapper 这些概念基本都是「换名字」黑匣子会少很多。2. 拆开源码包之前先知道这类项目由哪几层组成请求是怎么走完一条链路的2.1 三层结构不是设计癖是为了让你改起来不牵一发动全身拿到这类 javaweb 完整案例的源码包你会发现代码几乎没有堆在一个类里而是按 JSP、Servlet、DAO 分了三层。浏览器不直接连数据库而是先请求 ServletServlet 拿到参数后调用 DAO 层的方法DAO 内部用 JDBC 访问 MySQL查完结果放回 ListServlet 把 List 塞进 request 作用域再转发给 JSP 渲染成表格。这个链路虽然绕但每一层改起来都相对独立JSP 只管展示调整页面样式不碰 JavaDAO 只管 SQL换表名字段名只改这一处Servlet 只管参数解析和跳转新增一个查询条件也无需动另外两层。为什么后来又出现 Spring Boot 让人舒服是因为框架把这套「分层 请求转发」的约定固化成了标准流程。Controller、Service、Mapper 对应到这里就是 Servlet、业务逻辑、DAO。所以这个包无论多简陋只要链路完整它就是理解主流框架的最好教材。看代码时不要在 JSP 里找 Java 逻辑也不要在 DAO 里找 HTML先记住这条单向依赖JSP → Servlet → DAO → MySQL。至于「数据库增删改查」为什么被当成 Web 开发的必修课是因为任何后台管理页面本质都是对一个表的四个操作查列表、新增、编辑、删除。这套模板一旦写顺了后面做用户管理、商品管理、订单管理全都是复制改。四个操作方法对应四条 SQLSELECT 加 WHERE 是查INSERT 是增UPDATE 按主键改DELETE 按主键删整个 javaweb 项目没有比这更骨架的东西了。2.2 源码包里通常会出现的七类文件各归各位这类由老师或博主整理发布的包结构一般比较规整。下面这个清单不是某个包特有的而是这类「javaweb 完整案例 MySQL」源码包的常见共式位置常见内容你会在里面改什么src/entity实体类字段对应数据表加字段时要同步改表和 DAOsrc/daoDAO 接口与实现JDBC 代码都在这查询条件、SQL 语句src/servlet继承 HttpServlet 的控制器URL 路径、参数解析、跳转src/util数据库连接工具类连接参数、连接池web/WEB-INF/web.xml部署描述符欢迎页、Servlet 映射、过滤器web/*.jsp页面文件表格、表单、样式根目录下的 sql 与 jar 包建表脚本和依赖库导入 MySQL、放入 lib注意一点如果你解压后只看到 src 和 web没有 lib 目录也没有 jar 包这种包大概率是「半源码」发布者为了避免 zip 体积过大把依赖去掉了。你需要自己补上 servlet-api 和 MySQL 驱动才能编译。这不是包有问题按第 3 章的步骤补依赖即可别急着骂「跑不起来」。还有一类包是 Maven 工程的形态根目录有 pom.xml没有 lib 目录。这种更省事依赖在 pom.xml 里声明IDEA 打开后会自动下载。判断方法很简单看到 pom.xml 就当 Maven 工程处理看到 lib 目录就当传统 Web 工程处理两条路都能跑到最终结果。2.3 读代码别按文件名排序按「数据流」顺序读最省时间我的习惯是拿到包后不先看 JSP也不先看 Servlet而是按数据流逆向读先读 SQL 脚本搞清楚有几张表、每张表有哪些字段这是理解所有代码的前提。接着读 util 里的数据库连接工具看驱动类、URL、账号密码从哪里读。然后读 DAO因为增删改查的核心语句全在这里你后面要加查询条件、改表字段改的就是它。再读 Servlet看每个 URL 路径调了哪个 DAO 方法、跳转到哪个 JSP。最后才是 JSP看页面如何用 EL 或 JSTL 把 List 渲染出来。这个顺序的好处是你读到 DAO 时已经知道表结构看到select * from user where id ?不会对字段名陌生你读到 JSP 时已经知道 Servlet 塞了哪些请求属性看到${list}能立刻对上来源。很多新手拿到包直接双击 index.jsp 想看到效果这是最常见的误解——JSP 需要经过 Servlet 把数据准备好再渲染直接打开 JSP 只能得到一片空表格。读的过程里顺手判断这个包写得好不好有三个硬指标DAO 里是否用了 PreparedStatement 而不是拼接 SQL数据库连接用完之后是否在 finally 或 try-with-resources 里关闭JSP 里有没有大段% %Java 脚本。前两项不达标的项目运行起来很容易出乱码和连接泄漏第三项不达标的模板改起来会非常痛苦。如果你拿到的包三条全中建议只借鉴结构代码逐段重写反而更节省时间。3. 把项目跑起来IDEA、Tomcat、MySQL 的最小配置顺序3.1 先选版本JDK 8 Tomcat 8.5/9 是最稳组合别一上来追新这类 javaweb 源码包大多是按 JDK 8 的时代写的选环境时别追新。Tomcat 10 之后把包名从javax.servlet改成了jakarta.servlet老代码编译会直接报「程序包 javax.servlet 不存在」。这不是你代码错了是版本断层。常见做法是把环境固定在 JDK 8 配 Tomcat 8.5 或 9这两个版本对老工程兼容性最好课程设计的验收环境一般也是这套。组合兼容性建议JDK 8 Tomcat 8.5老工程直接跑最稳优先选JDK 8 Tomcat 9兼容 javax也能跑可以选JDK 11 Tomcat 9多数能跑编译报错时降回 JDK 8JDK 17 Tomcat 10包名变了老包大概率要改代码MySQL 方面5.7 和 8.0 都能用但驱动和 URL 要匹配。8.0 的驱动类名是com.mysql.cj.jdbc.Driver5.x 是com.mysql.jdbc.Driver混用会报 ClassNotFoundException。如果你没有现成的 MySQL建议装 8.0因为 5.7 官方已经停止维护新装没必要选老的。版本选型这事看着像玄学其实本质是「包的发布时间」和「运行环境」要对上源码包越老环境就越要保守。3.2 IDEA 运行 javaweb 项目配置从导入到部署的四步操作把解压后的目录用 IDEA 打开如果包里有.idea或.iml文件IDEA 会识别成工程如果没有它会当成普通目录打开。这时需要手动补三件事项目 SDK、Web Facet、Artifacts。这一步是「IDEA 运行 javaweb 项目配置」里翻车最多的地方不要跳过。第一步File → Project Structure → Project把 SDK 选成 JDK 8Language Level 选 8。第二步Project Structure → Facets点加号添加 Web把 Web 资源目录指向web或src/main/webapp。第二步如果不做IDEA 不知道这是个 Web 工程后面 Run 按钮都不会出现。第三步Project Structure → Artifacts点加号选 Web Application ExplodedName 保持和你模块名一致。这一步很关键Tomcat 部署时跑的是这个 Artifacts它决定了编译后的 class 和 JSP 打包到哪里。第四步配置 TomcatRun → Edit Configurations点加号选 Tomcat Server → Local在 Deployment 标签页把刚才建好的 Artifacts 加进去Application context 写你的项目名比如/useradmin。这里有个细节context 的值决定访问路径写/就直接用根路径访问写/useradmin就要在 URL 里拼上这串。老包里的 JSP 表单 action 如果写死了/user/list这种绝对路径context 不是/时全部会 404这一点我后面专门讲。3.3 连接 MySQL 数据库建库、驱动、URL 三个必改参数先建库建表把包根目录的 sql 脚本在 Navicat 或命令行里执行。没有现成脚本的话手动执行下面这一段也够跑通增删改查CREATE DATABASE IF NOT EXISTS demo DEFAULT CHARACTER SET utf8mb4; USE demo; CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, email VARCHAR(100) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这条 SQL 建了一个叫user的表id 自增主键name 必填email 可空。字符集特意用了 utf8mb4这样中文数据和 Emoji 都能存老脚本里常见的 utf8 在 MySQL 8 下存 Emoji 会报错建议建库时统一改成 utf8mb4。然后改数据库连接配置。传统非 Maven 包里连接信息一般在src下的db.properties或jdbc.properties内容长这样jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/demo?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8 jdbc.usernameroot jdbc.password123456四个参数里必改的是 URL、用户名、密码。URL 里的demo是库名要和你建的库一致localhost:3306是 MySQL 地址和端口远端数据库要换成 IP。URL 后面三串参数解释一下useSSLfalse是关掉 SSL 警告本地开发没必要开serverTimezoneAsia/Shanghai是解决 MySQL 8 的时区报错不写会提示 CST 无法识别characterEncodingutf8是保证中文读写不乱码。如果你用的是 MySQL 5.x驱动类名要改成com.mysql.jdbc.DriverURL 可以不带时区参数其余不变。3.4 启动与验证从 Tomcat 输出到浏览器 URL 的对应关系点绿色的 Run 启动 Tomcat看到Server startup in xxx ms说明部署成功。浏览器访问http://localhost:8080/useradmin/user/list其中/useradmin是你在 Deployment 里配的 context/user/list是 Servlet 上的 WebServlet 或 web.xml 里的映射路径。如果 404先检查 URL 里 context 有没有拼对再检查 Servlet 映射是否和访问路径一致这两处是最常见的 404 来源。Tomcat 启动后控制台刷报错也别慌看关键字ClassNotFoundException是缺 jar 包CommunicationsException是 MySQL 连不上SQLSyntaxErrorException是表和实体字段对不上。把日志里第一行异常复制出来搜比盯着满屏堆栈猜要快得多。跑通之后浏览器能看到一个用户列表页面能点新增、能编辑、能删除这个包就算真正属于你了。到这一步后面框架层面的东西都建立在「一条请求如何穿过三层」这条主线上。4. 手写最小增删改查DAO、Servlet、JSP 三层代码与参数说明4.1 实体类和 DAOPreparedStatement 是底线不是可选项实体类的写法很固定字段对应表的列。getter/setter 可以用 IDEA 快捷键生成也可以直接用 Lombok但课程设计的老包一般不用 Lombok因为还需要装插件。下面这个是最小形态public class User { private Integer id; private String name; private String email; public Integer getId() { return id; } public void setId(Integer id) { this.id id; } public String getName() { return name; } public void setName(String name) { this.name name; } public String getEmail() { return email; } public void setEmail(String email) { this.email email; } }DAO 层的核心是四个方法查全部、新增、按 id 更新、按 id 删除。先看查询方法注意 SQL 里的参数占位符是?不是拼字符串public ListUser listAll() { String sql select id, name, email from user; ListUser list new ArrayList(); try (Connection conn DbUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { User u new User(); u.setId(rs.getInt(id)); u.setName(rs.getString(name)); u.setEmail(rs.getString(email)); list.add(u); } } catch (SQLException e) { e.printStackTrace(); } return list; }这段代码的两个关键点DbUtil.getConnection()是从工具类拿连接try-with-resources 保证连接、语句、结果集在方法结束时自动关闭不会把数据库连接池占满PreparedStatement预编译 SQL参数用?占位后续用setString填充从根上避免了 SQL 注入。为什么强调这一点如果你拿到的源码包用的是Statement加字符串拼接比如select * from user where name name 这就是典型的反面教材输入一个 or 11能绕过所有验证这种代码交上去容易被批生产环境更不允许。新增和更新的写法同理多一个setString绑定参数public int add(User u) { String sql insert into user(name, email) values(?, ?); try (Connection conn DbUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, u.getName()); ps.setString(2, u.getEmail()); return ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); return 0; } } public int update(User u) { String sql update user set name ?, email ? where id ?; try (Connection conn DbUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, u.getName()); ps.setString(2, u.getEmail()); ps.setInt(3, u.getId()); return ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); return 0; } }删除方法比更新少两个参数delete from user where id ?之后setInt(1, u.getId())。注意executeUpdate返回的是受影响的行数业务上可以用它判断操作是否成功返回 0 说明没删到或没更新到可能是 id 不存在。4.2 Servlet 层用 WebServlet 把请求路由到增删改查四个动作老式项目在 web.xml 里写servlet和servlet-mapping新一点的包直接用 WebServlet 注解少配一段 XML。下面这个列表 Servlet 展示「查所有」的完整流程WebServlet(/user/list) public class UserListServlet extends HttpServlet { private UserDao userDao new UserDao(); Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); ListUser list userDao.listAll(); req.setAttribute(list, list); req.getRequestDispatcher(/list.jsp).forward(req, resp); } }/user/list就是访问路径doGet处理浏览器地址栏直接输入的 GET 请求。流程三步查数据、塞进 request、转发给 JSP。getRequestDispatcher(/list.jsp)的路径是相对于 Web 根目录前面要加斜杠写错会 500。setCharacterEncoding(UTF-8)放在最前面保证后面读到的参数不乱码。新增和编辑走 PostRedirectGet 的简化版表单提交过来在doPost里处理WebServlet(/user/add) public class UserAddServlet extends HttpServlet { private UserDao userDao new UserDao(); Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String name req.getParameter(name); String email req.getParameter(email); User u new User(); u.setName(name); u.setEmail(email); userDao.add(u); resp.sendRedirect(req.getContextPath() /user/list); } }这里有两个细节req.getParameter(name)拿的是表单里input namename的值名字对不上就拿到 nullsendRedirect是重定向地址回到列表页避免用户按 F5 时把同一行数据重复插入。重定向路径里加req.getContextPath()是为了兼容 context 不是根路径的情况老包如果直接写死/user/list重定向后也可能 404。更新和删除的 Servlet 模式完全一致更新 Servlet 在doPost里多接收一个 id 参数删除为了简单直接在doGet里根据?idxx删然后重定向。注意删除用 GET 有个隐患容易被搜索引擎或预加载请求触发。严谨做法是删改用 POST课程设计里用链接加确认弹窗也能接受但你要知道这不适合生产。4.3 JSP 渲染c:forEach 遍历列表form 的 action 对应哪个 Servlet列表页的核心是把 Servlet 塞进来的 List 渲染成表格最方便的手段是 JSTL 标签库% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % table border1 tr thID/thth姓名/thth邮箱/thth操作/th /tr c:forEach items${list} varu tr td${u.id}/td td${u.name}/td td${u.email}/td td a href${pageContext.request.contextPath}/user/edit?id${u.id}编辑/a a href${pageContext.request.contextPath}/user/delete?id${u.id} onclickreturn confirm(确定删除?)删除/a /td /tr /c:forEach /table${list}对应 Servlet 里setAttribute(list, list)的 key${u.id}调用的是 User 类的getId()方法。items${list}就是遍历那个 Listvaru是循环变量名你自己起别和已有的变量冲突。编辑和删除链接里我都拼了${pageContext.request.contextPath}这是 JSP 里拿 context 路径的标准写法。新增和编辑的表单页更简单form 的action指向对应的 Servletmethod 用 postform action${pageContext.request.contextPath}/user/add methodpost 姓名input typetext namename requiredbr 邮箱input typeemail nameemailbr button typesubmit提交/button /form编辑页面就是在 form 里把当前记录的 id、name、email 回显到 input 的 value 里提交时把 id 一起带上action换成/user/update。如果没有 JSTL 的依赖c:forEach不会生效页面会原样把这段标签打出来。解决办法是确认WEB-INF/lib下有jstl.jar和standard.jar或者用 Maven 引入javax.servlet.jsp.jstl依赖。4.4 三个容易忽略的参数编码、context-path、驱动类名第一个是编码。JSP 文件头部的contentType要带charsetUTF-8Servlet 里req.setCharacterEncoding(UTF-8)要在读取参数之前调用数据库 URL 要带characterEncodingutf8这三处缺一处中文就可能乱码。乱码问题是人品筛选器排查顺序永远是「页面 → 请求 → 数据库」一段一段排除。第二个是路径。Java Web 里所有前导带斜杠的路径都分两种站在服务器角度看比如getRequestDispatcher路径相对于 Web 根目录站在浏览器角度看比如 form 的 action 和 a 标签的 href必须带 context-path否则换部署名就 404。最省心的办法是浏览器侧路径全部写成${pageContext.request.contextPath}/user/list这种形式服务器侧路径写死成/list.jsp不拼 context。第三个是驱动类名。MySQL 5.x 和 8.x 的驱动类不一样包里的 DbUtil 写的哪个类名就决定了你必须用哪个版本的 MySQL 驱动 jar。常见做法是把mysql-connector-java8.0.x 的 jar 放进 lib同时 DbUtil 里写com.mysql.cj.jdbc.Driver。如果你用的是 5.x 驱动类名要改回com.mysql.jdbc.DriverURL 里的时区参数也可以删掉。驱动版本和数据库版本不匹配跑起来全是「Unknown database」或「Communications link failure」这类误导性极强的报错。5. javaweb 增删改查最常见翻车现场五个坑与一轮兜底排查5.1 现象Tomcat 一启动就报 ClassNotFoundException / NoClassDefFoundError原因几乎都是 jar 包没有进入最终的部署产物。传统 Web 工程里jar 放在web/WEB-INF/lib下如果工程里没有这个目录或 jar 不在里面编译期 IDEA 可能靠「Add as Library」不报错但 Tomcat 部署时用的是 Artifacts 里的 lib两处不一致就会在运行期现原形。解决Project Structure → Artifacts → 选中你的 exploded 包 → 看 Available Elements把WEB-INF/lib或依赖的库右键 Put into/WEB-INF/lib。然后重新 Run看到 Tomcat 输出里多了一行行 jar 解压日志说明依赖进去了。判断是不是这个问题看异常里丢失的类名如果是com.mysql.cj.jdbc.Driver那就是 MySQL 驱动没打进去如果是javax.servlet.*那反而正常tomcat 自带。5.2 现象连接 MySQL 报 SSL 警告甚至直接 CommunicationsException原因有两层第一层是 MySQL 8.0 默认开启了 caching_sha2_password 认证老驱动不认识会握手失败第二层是 8.0 的 JDBC 驱动默认要求时区信息URL 里没有serverTimezone就报异常。这两个都是「新数据库 老代码」的典型摩擦。解决换 8.x 驱动URL 补上useSSLfalseserverTimezoneAsia/Shanghai。这个组合几乎是通用解。要是还连不上换个思路验证先确认 MySQL 服务真的在跑netstat -ano | findstr 3306看端口再用命令行mysql -uroot -p试能不能登录。很多时候根本不是代码问题是 MySQL 装完没启动或者 root 密码不是配置文件里写的那位。5.3 现象页面显示正常插入数据库的中文变成问号这是乱码问题的经典形态。三种情况按概率排数据库表字符集不是 utf8mb4JDBC URL 没带characterEncodingutf8JSP 和 Servlet 的编码设置不一致。解决三层一起改。表字符集用ALTER TABLE user CONVERT TO CHARACTER SET utf8mb4改完后新写入的数据才正常历史坏数据要手动修JDBC URL 加上characterEncodingutf8同时确认 MySQL 服务端字符集是 utf8mb4SHOW VARIABLES LIKE character_set_server查看JSP 页面第一行统一写charsetUTF-8Servlet 里req.setCharacterEncoding放最前面。这三处是一套缺一个都可能让中文乱码。这算是 javaweb 项目里的血泪经验了大多数乱码不是一处的问题是整条链路的字符集没对齐。5.4 现象列表页能打开但 CSS/JS 加载不出来点击操作 404原因十有八九是路径写死成了/css/style.css这种不带 context-path 的形式。部署在 context/useradmin下时浏览器实际请求的是http://localhost:8080/css/style.css服务器找不到这个资源返回 404。页面排版全乱操作链接点了也白点。解决JSP 里所有静态资源和内页链接统一用${pageContext.request.contextPath}开头。比如link href${pageContext.request.contextPath}/css/style.cssa 标签同理。如果你想偷懒也可以在head里用c:set varctx value${pageContext.request.contextPath}/定义个变量后面写成${ctx}/user/list代码会短不少。这个坑的本质是相对路径和绝对路径的语义混淆和 4.4 节说的是一件事路径问题永远是 Java Web 里最容易翻车的点。5.5 现象IDEA 的 Run 按钮是灰色或者 Tomcat 选项根本不存在原因不是 Tomcat 没装而是 IDEA 没把这个目录当成 Web 工程。只靠 Open 打开一个普通目录IDEA 不会自动生成 Web FacetRun 配置里自然没有 Tomcat Server 选项。解决回到 3.2 的操作顺序先把 Facets 里的 Web 加上把 Web 资源目录指对再到 Artifacts 建一个 exploded 包。只要 Facets 里出现了 Web 字样Run 配置里的 Tomcat Server 选项就会解锁。如果加了还不行检查一下 Project Structure 里 Modules 的 Source 标签页确认src被标成了 Sourcesweb被标成了 Web Resources。很多时候 IDEA 面板只是不刷新关掉 Project Structure 重开就好了。5.6 兜底排查顺序按「部署产物 → 连接 → 路径 → 编码」四段定位如果上面的坑都排完了项目还不跑我一般按这个顺序硬排查第一步部署产物Tomcat 的webapps目录下你的应用文件夹里WEB-INF/lib有没有 jarclasses 里有没有编译后的 class第二步连接先命令行连 MySQL 验证账号密码再写一个只有 DbUtil 的 Test 类调getConnection()能打印出 Connection 对象就说明数据库没问题第三步路径启动后看 Tomcat 本地日志localhost.2024-xx-xx.log里面会有每次请求的完整路径和状态码第四步编码按 5.3 的顺序查字符集。这四段走完还找不到问题把异常第一行完整贴到搜索引擎比在群里问人快。Java Web 的报错大多数有标准答案关键在于你贴的是不是第一行根因而不是中间某条看起来吓人的 Caused by。记住 Caused by 往往才是真正的元凶顺着它往下挖才是正路。6. 给增删改查加两个拿得出手的点分页和连接池再加一道验证课程设计或入职练手的代码能跑只是及格「有数据条数就开始卡」才是真正要解决的问题。两个最值得升级的点分页和连接池。6.1 分页LIMIT 语句加两个参数Control 层多接一个 pageNum分页的核心是两条 SQLselect count(*) from user查总数select id, name, email from user limit ?, ?取当前页数据。第一个?是偏移量(pageNum - 1) * pageSize第二个?是每页条数。封装一个 PageBean字段就五个pageNum、pageSize、total、list、totalPage。Servlet 里从 request 取pageNum没传默认为 1查完把 PageBean 塞进 requestJSP 里渲染页码链接时拼?pageNum1、?pageNum2。这个改造半小时能做但对「数据量大了会不会卡」这个问题的回答比多写两个模块更有说服力。6.2 连接池用 Druid 替换 DbUtil 里的 DriverManager老包用 DriverManager 每次新建物理连接请求一多就报连接超时。替换成 Druid 只需要改 DbUtil 的静态代码块配置写在druid.properties里driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/demo?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8 usernameroot password123456 initialSize5 maxActive20private static DataSource dataSource; static { try { Properties p new Properties(); p.load(DbUtil.class.getClassLoader().getResourceAsStream(druid.properties)); dataSource DruidDataSourceFactory.createDataSource(p); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); }改完之后DAO 层的代码一个字都不用动因为getConnection()的签名没变只是背后从「每次新建」变成了「从池里借」。initialSize5是启动时预建的连接数maxActive20是最大连接数并发不大的内部系统这两个值够用。压测时观察连接池监控如果活跃连接稳定在 10 以内说明配置合理。验证方法很简单连续在 Tomcat 下多刷几次页面然后去 MySQL 执行show processlist能看到多个来自应用服务器的连接被复用而不是频繁新建销毁。分页和连接池做完这个项目就从「能跑的作业」变成了「能抗一点真实流量的基础版后台」。我现在的习惯是拿到任何一套参考源码先跑通再按照这两步改造改造的过程才是真正学会的过程。这一步走完你再回头去看 Spring Boot 里的 PageHelper 和 HikariCP会发现所有东西都是老朋友换了个名字。希望帮到你。本文还有配套的精品资源点击获取