
简介一套面向Java Web初学者的JSP火车查询系统毕业设计项目采用B/S模式以Java、JSP与MySQL为核心技术栈适用于计算机专业学生进行课程设计、毕业设计或Java Web入门练习。资源包含完整项目源代码、数据库脚本及部署配置管理员账号为admin密码admin数据库配置文件位于DBO.java开发环境为Tomcat7.0、MyEclipse8.5和MySQL5.0导入后即可运行调试。压缩包内共141个文件涵盖41个JSP页面、10个Java源码、10个class文件以及SQL数据库脚本。JSP文件负责页面展示与请求交互Java及class文件处理业务逻辑和数据库访问另有GIF图片等页面素材与部分配置文件整体仅707KB结构轻量、目录清晰便于按模块阅读和二次开发。目前已有322人学习下载对于希望理解JSP页面与Java后台的协作流程、JDBC数据库连接方式以及基础增删改查实现的读者是一份可运行的完整参考。1. JSP火车查询系统是给谁用的课设、毕设和快速交付的真实场景如果你手上压着一个 Java Web 课程的结课作业或者毕业设计选题里写着“基于 JSP 的火车票查询系统”恭喜你你选到了一个经典得不能再经典的题目。它不新但它把 Java 后端开发里最核心的几块东西——Servlet 生命周期、JSP 页面渲染、JDBC 数据库操作、HTTP 请求响应——全串在了一条完整的链路上。做完它你不只是交了一个作业你是真的把 Java Web 的骨架摸了一遍。这个项目最适合两类人。第一类是正在学 Java Web 的学生需要在一个两周到四周的周期里拿出一个能演示、能答辩、代码能讲清楚的项目第二类是想快速回顾 Java 后端基础、准备面试或者巩固 Servlet/JSP 知识的初级工程师。它能帮你解决的问题很简单怎么用 JSP 做页面、怎么用 Java 写业务逻辑、怎么用 MySQL 存数据以及这三者之间是怎么通过 Tomcat 这个容器协作起来的。这套项目不讲高深的微服务不搞复杂的消息队列它就是干干净净的一门 Java Web 基本功。接下来我会把这个项目的完整落地路径拆给你从技术选型的理由、环境搭建的版本搭配到数据库设计、核心查询代码再到最常见的翻车现场和解决手段。你可以直接照着敲也可以把它当成一份“从标题到答辩”的完整路线图。2. 技术选型与环境搭建Java JSP MySQL 的版本搭配和最小可行环境2.1 为什么这个项目选 JSP Servlet MySQL 而不是 Spring Boot你可能会有疑问现在出去找工作Java 后端几乎都是 Spring Boot 的天下为什么还要用一个 JSP Servlet 的项目做火车查询系统答案有两层。第一层是教学与课设的现实需求。很多学校的 Java Web 课程大纲还停留在 Servlet JSP 这个阶段教材和实验指导书也是按这个栈写的。用 Spring Boot 交作业老师不一定接受因为课程考核点就是“你能不能理解 Servlet 的生命周期、能不能自己写 JSP 页面、能不能手动配 web.xml”。Spring Boot 把这些东西全封装掉了你反而没法回答答辩时“请讲一下请求从浏览器到数据库的完整过程”这个问题。第二层是理解成本。JSP Servlet MySQL 这个组合每个环节都是显式的浏览器发请求到 TomcatTomcat 根据 web.xml 的映射找到对应的 ServletServlet 里写业务逻辑、调用 JDBC 访问 MySQL拿到结果后通过 request 域传给 JSPJSP 在服务端渲染成 HTML 返回给浏览器。每一步都能在代码里看到对应的文件。而 Spring Boot 里一个 RequestMapping 注解就把路由、参数绑定、返回渲染全接管了新手很难把“底层发生了什么”和“我写的这几行注解”对应起来。所以我的结论很直接如果目标是用最短时间通过课程考核、把手动开发 Java Web 的链路走通JSP Servlet MySQL 是正确选择如果目标是找一份 Spring Boot 相关工作那也应该先做完这个项目再迁移因为你在 JSP 项目里理解的请求生命周期、数据库连接管理、参数传递在 Spring Boot 里依然成立只是换了一种更自动化的写法。2.2 JDK / Tomcat / MySQL 版本搭配与安装注意点JSP 项目的环境搭建比 Spring Boot 要敏感得多核心原因在于 Tomcat 版本和 JDK 版本的兼容矩阵。我见过太多人在这上面浪费一整天代码照着教程抄的但 Tomcat 版本不一样启动直接报错还以为是代码写错了。先给出一套我验证过最稳的组合适合 JSP 火车查询系统这类传统 Servlet 项目组件推荐版本说明JDKJDK 8 或 JDK 11JDK 8 兼容性最好JDK 11 也行但别用 JDK 17 跑老项目某些反射和类加载行为有差异TomcatTomcat 9.0.x这是 javax.servlet 命名空间的最后主力版本兼容绝大多数 JSP 教程MySQLMySQL 5.7 或 MySQL 8.05.7 更省心8.0 需要多配时区和驱动参数JDBC 驱动mysql-connector-java 5.1.49配 5.7/ 8.0.x配 8.0驱动版本必须和 MySQL 大版本匹配混用会报奇怪的连接错误这里有一个最重要的坑必须先说不要用 Tomcat 10 或 Tomcat 11 跑这个项目。原因很简单Tomcat 10 开始把 Java EE 的 javax.servlet 包迁移到了 jakarta.servlet而你找到的绝大多数 JSP 教程、课设代码、教材里的 import javax.servlet.http.HttpServlet 在 Tomcat 10 上会直接编译失败。如果你想少掉一半头发就老老实实用 Tomcat 9。安装顺序建议是先 JDK再 MySQL最后 Tomcat。JDK 装完后在命令行执行 java -version 确认输出里有 Java(TM) SE Runtime Environment 而不是 JREMySQL 装完以后用 mysql -u root -p 验证能登录Tomcat 解压后进入 bin 目录执行 startup.batWindows或 startup.shLinux/macOS然后浏览器访问 http://localhost:8080 能看到 Tomcat 默认首页。这三个验证都通过环境才算就绪千万别急着写代码。3. 把查询系统跑起来数据库设计、JDBC 连接与车次查询的核心代码3.1 数据库设计车次表与余票表的最小表结构火车查询系统的核心业务是查车次、查余票。数据库设计不需要很复杂但要有起码的范式意识。我一般习惯建两张表一张存车次的基础信息一张存余票的实时数量。分开存的好处是余票每天、每小时都可能变而车次的始发站、终点站、发车时间、到达时间是相对固定的拆开以后查询时只关联需要的字段不会把一张表搞得很宽。CREATE DATABASE IF NOT EXISTS train_db DEFAULT CHARACTER SET utf8mb4; USE train_db; CREATE TABLE train_info ( id INT PRIMARY KEY AUTO_INCREMENT, train_no VARCHAR(20) NOT NULL COMMENT 车次号如 G101, from_station VARCHAR(50) NOT NULL COMMENT 始发站, to_station VARCHAR(50) NOT NULL COMMENT 终点站, depart_time VARCHAR(10) NOT NULL COMMENT 发车时间格式 HH:MM, arrive_time VARCHAR(10) NOT NULL COMMENT 到达时间格式 HH:MM, seat_type VARCHAR(20) DEFAULT 二等座 COMMENT 座位类型, price DECIMAL(10, 2) NOT NULL COMMENT 票价 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE ticket_stock ( id INT PRIMARY KEY AUTO_INCREMENT, train_id INT NOT NULL, stock INT NOT NULL DEFAULT 0 COMMENT 余票数量, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_train FOREIGN KEY (train_id) REFERENCES train_info(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这段建表 SQL 有几个地方是刻意为之的。train_no、from_station、to_station 都用了 VARCHAR 而不是 CHAR因为车站名称长度不固定CHAR 会造成存储浪费depart_time 和 arrive_time 用 VARCHAR(10) 而不是 TIME 类型是因为查询时用户输入“上午”“下午”这类条件时VARCHAR 配合 LIKE 做模糊查询更灵活两张表都显式指定了 ENGINEInnoDB因为 InnoDB 支持外键约束MyISAM 不支持而 ticket_stock 里的 fk_train 外键需要这个能力。注意 MySQL 8.0 默认就是 InnoDB但 5.7 不写也行写上是给阅卷老师看的“我有这个意识”。3.2 JDBC 连接工具类连接、查询和资源释放的标准写法JSP 项目里访问数据库最朴素也最常见的做法是写一个 DBUtil 工具类负责加载驱动、获取连接、关闭连接。很多新手会把这个逻辑直接塞进 JSP 页面里页面里到处是 Class.forName 和 DriverManager.getConnection这种写法的代码没法维护而且每个页面重复连接数据库性能和整洁度都很差。package com.train.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/train_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/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) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { if (rs ! null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt ! null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn ! null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }这段代码里有两个细节值得注意。一是 Class.forName(com.mysql.cj.jdbc.Driver)这是 MySQL 8.0 的驱动类名如果连的是 MySQL 5.7 且用的是老驱动类名应该是 com.mysql.jdbc.Driver。把类名写错会直接报 ClassNotFoundException这是 JSP 项目里最高频的启动报错之一。二是 URL 里的 characterEncodingutf8 和 serverTimezoneAsia/Shanghai前者保证读写中文不乱码后者解决 MySQL 8.0 默认时区和本地时区不一致导致连接报错的问题。这些参数不是可选项是必须项。3.3 查询主流程Servlet 接收参数、调用查询、JSP 渲染结果火车查询系统最核心的功能就是“查车次”。完整的查询流程分成四步用户在 JSP 页面输入出发地和目的地表单提交到 ServletServlet 从 request 里取出参数调用 DAO 层的方法查询数据库查询结果封装成 List 放进 request 域请求转发到结果页面JSP 用 JSTL 或者脚本片段把数据渲染成表格。我建议你按这个分层来写不要图省事把 SQL 直接写在 Servlet 里。先看 Servlet 的代码它负责接收请求和调度package com.train.servlet; import java.io.IOException; import java.util.List; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.train.dao.TrainDao; import com.train.entity.Train; WebServlet(/search) public class SearchServlet extends HttpServlet { private TrainDao trainDao new TrainDao(); Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String fromStation req.getParameter(fromStation); String toStation req.getParameter(toStation); ListTrain trainList trainDao.findByStations(fromStation, toStation); req.setAttribute(trainList, trainList); req.getRequestDispatcher(/result.jsp).forward(req, resp); } Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }这里用 WebServlet(/search) 注解代替了 web.xml 配置Tomcat 9 支持这个写法代码更简洁。doGet 里先执行 setCharacterEncoding这一行很多人会漏漏掉的后果是页面提交的中文参数在 Servlet 里变成乱码。把表单请求统一由 doGet 处理是这个小项目里比较省事的做法因为查询操作本身是幂等的用 GET 也没有安全性问题。参数从 request.getParameter 取出后传给 DAO返回的 List 放进 request 域再转发到 JSP。整个过程不直接写 HTMLServlet 只做控制这是 MVC 的基本思想。DAO 层负责和数据库打交道package com.train.dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.List; import com.train.entity.Train; import com.train.util.DBUtil; public class TrainDao { public ListTrain findByStations(String fromStation, String toStation) { ListTrain list new ArrayList(); String sql SELECT t.id, t.train_no, t.from_station, t.to_station, t.depart_time, t.arrive_time, t.seat_type, t.price, s.stock FROM train_info t LEFT JOIN ticket_stock s ON t.id s.train_id WHERE t.from_station LIKE ? AND t.to_station LIKE ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, % fromStation %); ps.setString(2, % toStation %); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Train train new Train(); train.setId(rs.getInt(id)); train.setTrainNo(rs.getString(train_no)); train.setFromStation(rs.getString(from_station)); train.setToStation(rs.getString(to_station)); train.setDepartTime(rs.getString(depart_time)); train.setArriveTime(rs.getString(arrive_time)); train.setSeatType(rs.getString(seat_type)); train.setPrice(rs.getDouble(price)); train.setStock(rs.getInt(stock)); list.add(train); } } } catch (Exception e) { e.printStackTrace(); } return list; } }这段 DAO 代码里最关键的是用了 PreparedStatement而不是 Statement。PreparedStatement 有两个优势一是参数用 ? 占位符再用 setString 绑定彻底避免拼接 SQL 带来的注入风险二是同样的 SQL 结构下PreparedStatement 可以被数据库预编译重复执行时性能更好。SQL 里的 LIKE ? 配合 % fromStation % 实现的是包含式模糊匹配用户输入“北京”能查到“北京南”“北京西”这更符合真实查询习惯。LEFT JOIN 保证即使某车次还没有余票记录也能在结果里显示车次信息stock 为 0 而已。最后看 JSP 页面用 JSTL 标签渲染结果列表% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html head title车次查询结果/title style table { border-collapse: collapse; width: 100%; } th, td { border: 1px solid #ccc; padding: 8px; text-align: center; } /style /head body h2查询结果/h2 table tr th车次/thth出发站/thth到达站/th th发车时间/thth到达时间/thth座位类型/th th票价/thth余票/th /tr c:forEach items${trainList} vartrain tr td${train.trainNo}/td td${train.fromStation}/td td${train.toStation}/td td${train.departTime}/td td${train.arriveTime}/td td${train.seatType}/td td${train.price}/td td${train.stock}/td /tr /c:forEach /table /body /htmlJSP 里用 ${train.trainNo} 这种 EL 表达式访问 Train 对象的属性底层是通过 getter 方法完成的所以 Train 实体类里必须有对应的 getTrainNo 方法。taglib 指令引入了 JSTL 核心标签库c:forEach 负责循环遍历。用 JSTL 而不是 % % 脚本片段的好处是页面干净、不混 Java 代码这也是答辩时老师比较看重的“视图层不应该出现业务逻辑”的体现。如果项目没有引入 JSTL 依赖常见做法是在 pom.xml 里加 jstl 和 standard 两个依赖或者直接把 JAR 包放到 WEB-INF/lib 目录下。3.4 分页与模糊查询让查询结果在页面上好读、好用车次数据一多查询结果可能就是几十上百行一次性渲染到页面上既慢又难读。分页是这类查询系统迟早要面对的需求。我一般会给查询功能加上最简单的页码参数Servlet 接收 page 和 pageSize 两个参数默认 page1、pageSize10DAO 层用 LIMIT ? OFFSET ? 做分页查询同时再查一次总数用于计算总页数。SELECT t.id, t.train_no, t.from_station, t.to_station, t.depart_time, t.arrive_time, t.seat_type, t.price, s.stock FROM train_info t LEFT JOIN ticket_stock s ON t.id s.train_id WHERE t.from_station LIKE ? AND t.to_station LIKE ? LIMIT ? OFFSET ?;对应的 Java 代码里最后一页可能出现数据不足的情况但这不影响查询正确性。分页参数不能直接拼接进 SQL继续用 PreparedStatement 的 setInt 绑定 pageSize 和 offsetoffset 的计算公式是 (page - 1) * pageSize。页面上放“上一页”“下一页”两个链接携带同样的查询条件和 page 参数。这一块代码不复杂但能让你的项目在功能完整性上明显拉开和普通课设的差距答辩时也更好讲。模糊查询则要考虑一个细节用户输入的空格。常见做法是在 Servlet 里先 trim() 再传给 DAO不然用户手滑多打了个空格LIKE 匹配就会失败。另一个细节是城市名匹配的粒度如果用户输入“北京南”而你只存了“北京”精确匹配当然查不到但 LIKE %北京南% 也查不到因为你的数据里没有“北京南”这个子串。所以真实项目里更合理的方案是同时支持站名包含和拼音首字母匹配但课设层面做一层包含式模糊查询已经够了。4. 避坑JSP 火车查询系统最常见的 4 个翻车现场4.1 现象Tomcat 10 上部署后旧教程代码直接编译报错这是整个项目里出现频率最高的问题没有之一。你把网上找的代码或者教材代码导入项目启动 Tomcat 后访问页面控制台报错信息里有大量 cannot find symbol指向 javax.servlet.http.HttpServlet但你的 JDK 里明明有 servlet-api。原因很简单Tomcat 10 开始把 Java EE 迁移到了 Jakarta EE包名从 javax.* 改成了 jakarta.*。而你代码里写的是 import javax.servlet.http.HttpServletTomcat 10 的类库里只有 jakarta.servlet自然找不到。报错和代码本身没关系是容器版本和代码所用的 API 命名空间不匹配。解决方法是把 Tomcat 卸掉换成 9.0.x代码一行都不用改。如果你非要用 Tomcat 10就得把所有 import javax.servlet 改成 import jakarta.servlet但 JSTL 依赖也要换对应的 Jakarta 版本牵扯面比较大课设完全没必要折腾。我的建议是环境搭建阶段直接锁定 Tomcat 9从根上避开这个坑。4.2 现象启动后访问页面Tomcat 报 ClassNotFoundException: com.mysql.jdbc.DriverJDBC 代码里 Class.forName 写的类名找不到大多数情况下不是类名拼错而是驱动 JAR 包没有放进正确的位置。原因分两种。一是 JAR 包放错目录常见做法是把 mysql-connector-java 的 JAR 放在项目的 lib 目录下但普通 Java 项目里 lib 目录不会自动被 Tomcat 加载必须放在 WEB-INF/lib 下才行。二是驱动版本和 MySQL 版本不匹配比如 MySQL 8.0 配了 5.1.x 的驱动驱动类名是 com.mysql.jdbc.Driver而 MySQL 8.0 的驱动类名是 com.mysql.cj.jdbc.Driver写错了就是找不到。解决方法是先确认 MySQL 版本8.0 用 mysql-connector-java 8.0.x 并写 com.mysql.cj.jdbc.Driver5.7 用 5.1.49 并写 com.mysql.jdbc.Driver。然后把 JAR 文件复制到项目的 WEB-INF/lib 目录下重新发布项目、重启 Tomcat。判断 JAR 是否被正确加载可以在 Tomcat 启动日志里搜驱动关键字或者用压缩工具打开 JAR 确认里面有 Driver.class 文件。4.3 现象查询结果里的中文全部变成问号或乱码页面表单里输入“北京”查询结果页显示“”或者“æµè¯”这类乱码。这个问题看起来是显示问题实际上是编码在四个环节之间没有统一。原因是 JSP 页面编码、Servlet 请求编码、数据库连接编码、数据库表编码必须全部一致任何一个环节掉链子中文就出问题。最常见的缺失是JSP 页面设置了 UTF-8但 Servlet 里没写 req.setCharacterEncoding(UTF-8)导致 Tomcat 按默认的 ISO-8859-1 解析请求参数或者 JDBC 连接串里没加 characterEncodingutf8。解决方法是四层统一JSP 页面第一行写 % page contentTypetext/html;charsetUTF-8 %Servlet 里 doGet 开头执行 req.setCharacterEncoding(UTF-8)JDBC URL 里带 useUnicodetruecharacterEncodingutf8建库建表时用 DEFAULT CHARACTER SET utf8mb4。注意 utf8mb4 和 utf8 的区别utf8mb4 是 MySQL 8.0 推荐的全字符集能存 emojiutf8 是 MySQL 5.7 时代的默认遇到特殊字符可能报错。查乱码问题时先看浏览器开发者工具里响应头 Content-Type 的 charset再看数据库里直接 SQL 查询的结果是否正常就能定位是渲染层还是存储层的问题。4.4 现象连续刷新页面一段时间后页面卡死日志报连接超时项目在本地跑前几分钟正常多刷新几次后页面转圈过一会儿报 Communications link failure 或 Connection refused。这个问题在课设阶段不常出现但一旦出现就很吓人很多人以为是 MySQL 崩了。原因是数据库连接没有被及时释放。JSP 页面或者 Servlet 里每查询一次就 new 一个 Connection用完不 closeMySQL 默认的 max_connections 是 151连接数耗尽后新的请求就拿不到连接了。另一种情况是代码里用了连接池但连接池的 maxActive 配置太小并发请求一多就排队。解决方法是严格遵循“获取连接的代码块和关闭连接的代码块成对出现”的规范用 try-with-resources 语法如前面 DAO 代码所示保证连接自动关闭。如果是生产环境或者演示环境更稳的办法是引入连接池比如 commons-dbcp2 或 HikariCP让连接池管理连接的创建和回收但课设阶段先保证不用连接池也能稳定跑半小时以上连接释放检查一遍再演示不然答辩现场卡死在投影仪上非常狼狈。5. 让查询系统从“能跑”到“能演示”验证方法、答辩准备和一个小技巧5.1 功能验证清单照着走一遍就知道项目能不能交付项目写完后我建议你按下面这套顺序完整走一遍而不是直接拿给老师看第一步环境验证。启动 Tomcat 和 MySQL浏览器访问 http://localhost:8080/ 确认 Tomcat 页面正常命令行执行 mysql -u root -p -e show databases; 确认数据库连接正常。第二步数据验证。往 train_info 和 ticket_stock 里插入至少 5 条测试数据覆盖不同出发站和到达站组合。第三步功能验证。在页面上查“北京到上海”确认结果列表出现查一个不存在的站名组合确认页面显示空列表而不是报错。第四步异常验证。把 MySQL 服务停掉再执行查询确认报错信息在页面上可读——这一步能看出你的项目有没有做基本的异常处理。第五步重启验证。关闭 Tomcat 和 MySQL再按顺序启动重新执行查询确认没有“重启后数据丢了”之类的问题。如果这五步全部通过你的项目就能进入答辩节奏了。5.2 一个值得花半小时做的小改进用连接池替换裸 JDBC课设阶段用 DBUtil 裸连数据库没问题但如果你想在答辩时多展示一点“工程意识”把裸 JDBC 换成连接池是一个投入产出比极高的改进。HikariCP 是目前 Java 生态里性能最好的连接池集成到 JSP 项目里只需要加两个 JAR 包、改一个工具类。package com.train.util; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; public class DBUtil { private static final HikariDataSource dataSource; static { HikariConfig config new HikariConfig(); config.setJdbcUrl(jdbc:mysql://localhost:3306/train_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai); config.setUsername(root); config.setPassword(123456); config.setMaximumPoolSize(10); config.setMinimumIdle(2); config.setConnectionTimeout(30000); dataSource new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }改动量很小但效果和原理都能讲清楚连接池在应用启动时预创建少量连接请求时从池里取用完归还而不是销毁省去了每次 getConnection 都经历 TCP 握手和 MySQL 认证的开销。答辩时老师问到“你项目的性能瓶颈在哪”你就可以接着这个话头讲连接池的作用和参数含义。MaximumPoolSize 别调太大课设场景 10 就够太大反而浪费 MySQL 的连接配额。底层还是同一个 Connection 接口你 DAO 层代码不需要改动JSP 调 Service 的链路完全不变。最后说一个我自己的习惯每次改完代码重启 Tomcat 之前把 catalina.out 或 localhost 日志清空这样启动后一旦报错第一眼看到的就是这一次改动的结果不会被历史堆栈干扰。排查乱码问题时多用浏览器无痕窗口避免缓存干扰判断。这套方法陪着我从第一个 JSP 课程设计一路走到现在希望帮到你。本文还有配套的精品资源点击获取