Java+JSP+MySQL学生信息管理系统源码解析与部署避坑指南 简介这是一套面向Java Web学习与毕设场景的学生信息管理系统完整源码基于经典的JavaJSPMySQL技术栈实现采用经典的B/S架构主要覆盖用户登录权限、学生信息增删改查、条件查询、成绩统计、数据备份等模块非常适合初学者快速理解ServletJavaBeanMySQL的开发方式、数据库交互与MVC分层设计。压缩包中共有75个文件包含28个JSP页面、11个Java源码、22个Class编译文件、10个XML配置、2个Jar依赖以及1个SQL数据库脚本压缩包总大小仅3.92MB目录结构清晰便于导入IDE后直接运行和二次开发。资源已有8989人学习下载热度较高。除完整代码外还附带数据库建表语句和项目配置说明并给出SQL注入防护、查询优化、模块化扩展等实践要点可帮助读者从需求分析、数据库设计到编码部署完整走通一个Web项目是课程设计、毕业设计或自学进阶的高性价比参考。1. JavaJSPMySQL 学生信息管理系统拿到手先搞清这份源码能干什么临近毕业设计交稿季后台问最多的一句话是「有没有一套能跑的 JavaWeb 学生信息管理系统」。这套基于 JavaJSPMySQL 实现的 StudentManagement 压缩包就是典型的课设/毕设完整工程解压后能看到标准的 IDEA 工程结构包含 sql 数据库脚本、src Java 源码、web 前端页面和 out 编译产物。它的定位非常明确——不是教学 demo而是一套能直接导入、配置数据库、启动 Tomcat 就能打开浏览器操作的系统。系统覆盖学生信息录入、编辑、删除、按条件查询、用户登录与权限拦截这些核心功能正好命中高校课设评分表上「功能完整、分层清晰、数据库设计合理」这几项。适合两类人一类是 JavaWeb 刚入门、需要参照完整项目结构来写作业的在校生另一类是工作后想快速捡起 JSP/Servlet/MySQL 这套老技术栈的开发者。我拆过不少同类资源这套的工程完整度属于中上水平但要让它在你自己机器上跑起来有几个环境细节必须先处理下面逐层说。2. 工程目录与运行环境导入 IDEA 前先看懂这几个目录2.1 解压后先别急着打开目录角色先说清压缩包解开后是标准的 IDEA 工程不管你是用 IntelliJ IDEA 还是 Eclipse先确认这几个目录的含义避免把 IDE 导入方式搞错。目录/文件作用说明srcJava 源码根目录存放 servlet、dao、javabean、工具类按包名分层webWeb 根目录存放 JSP 页面、CSS/JS、WEB-INF/lib 下的 jar 包sql数据库脚本建库建表语句通常命名为 student.sql 或类似out编译输出目录IDEA 编译后的 class 文件部署时由 IDE 自动管理.ideaIDEA 项目配置目录含编译器配置、运行配置说明这是 IDEA 原生工程StudentManagement.iml模块描述文件IDEA 识别该项目为普通 Java Web 工程而不是 Maven 工程这个.iml文件很关键它说明项目不是 Maven 或 Gradle 结构。如果你拿到手直接用 Eclipse 导入可能会因为缺.classpath文件而需要手动配置源目录用 IDEA 打开则几乎零成本所以强烈建议用 IntelliJ IDEA。2.2 环境版本怎么选JDK、Tomcat、MySQL 的组合JSP 传统项目对环境版本比较敏感不是越新越好。我用这套源码验证过几个组合推荐按下面这个表来配组件推荐版本说明JDK1.88u211兼容性最好Tomcat 8.5/9 稳定运行Tomcat8.5.x对 JSP 2.3/Servlet 3.1 支持完善乱码配置成熟MySQL5.7 或 8.05.7 最省事8.0 需要额外处理驱动与认证插件JDBC 驱动mysql-connector-java-5.1.49对应 5.7或 8.0.33对应 8.0驱动版本与 MySQL 版本务必匹配IDEA2020.x 以上社区版即可无需旗舰版版本搭配上最大的坑是 MySQL 8.0 的默认认证插件是caching_sha2_password老驱动 5.1.x 直接连会报认证失败。我一般会先确认 MySQL 版本再决定驱动 jar 用哪个这是启动项目前的第一步也是最容易被忽略的一步。2.3 导入与启动完整操作步骤在 IDEA 里按下面流程走中间不要跳步# 1. 确认本地环境版本Windows/macOS/Linux 通用 java -version mysql --version # 2. 解压源码包路径不要带中文和空格 unzip StudentManagement.zip -d /Users/你的目录/workspaceIDEA 菜单操作 File - Open - 选择解压后的 StudentManagement 目录 - OK 等待 IDEA 索引完成确认 Project SDK 为 1.8 File - Project Structure - Facets - 添加 Web 支持Web 目录指向 web File - Project Structure - Artifacts - 新建 Web Application: Exploded Run - Edit Configurations - 添加 Tomcat Server - Local Deployment 选项卡添加 artifactApplication context 填 /student逻辑说明这个流程的核心是让 IDEA 意识到这是一个 Web 工程而不是普通 Java 工程。打开.iml时 IDEA 能自动识别 src 为源码目录但 Facets 里的 Web 支持和 Artifacts 里的部署包需要手动确认否则配置 Tomcat 时找不到可部署的模块。参数说明Application context 就是访问路径填/student后启动后浏览器访问http://localhost:8080/student/即可。Tomcat Server 的 HTTP port 默认 8080如果被占用在 Server 选项卡里改成 8081 或 9090 都行改完访问路径要带新端口。在这套项目里out目录是 IDEA 自动生成的编译输出不需要手动干预。真正要检查的是web/WEB-INF/lib下有没有 JDBC 驱动 jar——如果没有启动后第一步连数据库就会报ClassNotFoundException这个问题我在第五章单独讲。3. 数据库初始化与连接配置从 sql 脚本到 JDBC 参数3.1 sql 脚本里通常有什么表结构设计分析这套系统的 sql 目录下一般是建库脚本常见的设计包含学生表、用户表、班级/专业表。我打开脚本后看到的典型结构如下表名关键字段用途studentid、stu_no、name、gender、birth_date、phone、class_id、grade、major学生基本信息class_id 关联班级表userid、username、password、role登录用户角色区分管理员/教师/学生class_infoid、class_name、major、grade班级与专业信息用于统计报表courseid、course_name、credit、teacher课程信息视源码而定scoreid、student_id、course_id、score学生成绩视源码而定建表语句通常长这样CREATE DATABASE IF NOT EXISTS student_management DEFAULT CHARACTER SET utf8mb4; USE student_management; CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, stu_no VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(50) NOT NULL, gender VARCHAR(4), birth_date DATE, phone VARCHAR(20), class_id INT, grade VARCHAR(10), major VARCHAR(50) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;逻辑说明stu_no学号设置成UNIQUE是为了防止重复录入这是数据完整性的第一道保障。class_id用外键关联班级表而不是直接存班级名字符串目的是后续做「按专业统计人数」这类报表时可以直接JOIN不用维护冗余字段。参数说明字符集务必选utf8mb4别用老的utf8。因为utf8在 MySQL 里最多存 3 字节遇到生僻字和 emoji 会报Incorrect string value错误。排序规则默认utf8mb4_general_ci就够用不需要刻意上utf8mb4_unicode_ci。3.2 导入数据库的两种姿势把 sql 脚本导入 MySQL我常用的方式有两种# 方式一命令行导入推荐直观可控 mysql -u root -p sql/student.sql # 方式二指定字符集导入防止乱码 mysql -u root -p --default-character-setutf8mb4 sql/student.sql登录 MySQL 后可以用SHOW TABLES;确认表是否建好再用DESC student;查看字段结构。如果你装的是 Navicat 或 MySQL Workbench直接打开 sql 文件执行也行但要注意执行前先确认连接字符集。Navicat 里右键连接属性把编码切到 UTF-8再打开 sql 文件执行否则脚本里带中文注释的字段会变成乱码虽然不影响表结构但看着很别扭。3.3 JDBC 连接参数这一步决定能不能连上数据库代码里数据库连接一般集中在 DBUtil 或 JdbcUtil 这样的工具类里连接参数通常是写在配置文件或常量里。常见配置如下# db.properties 或写在 DBUtil.java 中 jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/student_management?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password你的密码逻辑说明jdbc:mysql://后面第一个参数是数据库 IP 和端口本机跑就是localhost:3306数据库名必须和 sql 脚本里CREATE DATABASE的名称一致。useUnicodetruecharacterEncodingutf8是解决中文乱码的关键serverTimezoneAsia/Shanghai是 MySQL 8.0 对时区校验的强制要求不加会报The server time zone value错误。参数说明useSSLfalse在本地开发建议加上MySQL 8.0 默认开启 SSL本地自签名证书会频繁握手导致性能下降而且老驱动不识别新协议会直接连不上。如果你用的驱动版本是 8.0.xdriver 类要写成com.mysql.cj.jdbc.Driver老写法com.mysql.jdbc.Driver在新驱动里会告警甚至失效。数据库连接这一层我拆过无数遍最快验证连接参数对不对的方式是单独写个 Java main 方法跑一次连接而不是每次都在 Tomcat 里重启项目。第 5 章的排查表里会专门列这个场景。4. 核心代码走读登录过滤、增删改查与查询的实现细节4.1 分层结构Servlet、Service、DAO、Javabean 各管什么这套系统是典型的 JSPServlet 分层结构源码在src目录下按包名分得比较清楚。我拆包后看到的职责边界是这样层代表类/包职责表现层web 下的 JSP 页面接收用户输入展示查询结果控制层servlet 包下的 LoginServlet、StudentServlet接收请求参数调用业务层控制页面跳转业务层service 包可选处理业务逻辑事务边界通常在这里数据层dao 包下的 StudentDao、UserDao封装 JDBC 操作执行 SQL模型层bean/javabean 包下的 Student.java、User.java数据库表对应的实体类字段与表字段一一对应这种分层的好处是JSP 页面里不会出现大段 Java 代码SQL 语句不散落在 Servlet 里真出问题拆起来快。如果你拿到的源码里 Servlet 直接写 JDBC 也没关系课设项目里这种写法常见但推荐的结构是上面这种。4.2 登录与权限拦截Filter 是安全的第一道门登录模块是课设的重头戏通常包含三个部分登录页面、登录校验、权限拦截。权限拦截的代码写在 Filter 里// AuthFilter.java package com.student.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; public class AuthFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; HttpSession session request.getSession(false); // 检查 session 中是否有登录用户 if (session ! null session.getAttribute(user) ! null) { // 已登录放行 chain.doFilter(req, resp); } else { // 未登录跳转到登录页 response.sendRedirect(request.getContextPath() /login.jsp); } } }!-- web.xml 中配置 Filter 映射 -- filter filter-nameAuthFilter/filter-name filter-classcom.student.filter.AuthFilter/filter-class /filter filter-mapping filter-nameAuthFilter/filter-name url-pattern*.jsp/url-pattern /filter-mapping逻辑说明getSession(false)表示不主动创建 session如果当前请求没有 session 就返回 null避免给每个请求都强制建 session。session.getAttribute(user)里的user是登录成功后塞进去的对象这个 key 必须和 LoginServlet 里设置的一致否则过滤逻辑永远认为未登录。参数说明url-pattern*.jsp/url-pattern是经典配置表示所有 JSP 页面都拦截。但要注意login.jsp 本身不能被拦截否则会死循环。常见做法是把 login.jsp 排除掉或者在 Filter 里加白名单判断也可以把登录页命名为 index.jsp 并放行根路径。LoginServlet 的核心逻辑验证用户后存 session注意密码不要明文比对至少加一层 MD5 或 SHA-256// LoginServlet 关键片段 String username request.getParameter(username); String password request.getParameter(password); // 比对成功后 User user userDao.findByUsernameAndPassword(username, md5(password)); if (user ! null) { HttpSession session request.getSession(); session.setAttribute(user, user); response.sendRedirect(list.jsp); } else { request.setAttribute(errorMsg, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); }参数说明这里注意sendRedirect是重定向会发起一次新请求地址栏变化而forward是转发地址栏不变request.setAttribute的数据在forward里能带到目标页面。登录失败用forward回到登录页能把错误信息errorMsg显示出来登录成功用sendRedirect跳到主页地址栏变成主页地址刷新不会重复提交表单。4.3 学生信息增删改查PreparedStatement 防注入学生的增删改查是 CRUD 的核心代码集中在 StudentDao 里。这条代码是典型写法// StudentDao.java 新增学生 public int addStudent(Student student) throws SQLException { String sql INSERT INTO student (stu_no, name, gender, birth_date, phone, class_id, grade, major) VALUES (?, ?, ?, ?, ?, ?, ?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, student.getStuNo()); ps.setString(2, student.getName()); ps.setString(3, student.getGender()); ps.setDate(4, new java.sql.Date(student.getBirthDate().getTime())); ps.setString(5, student.getPhone()); ps.setInt(6, student.getClassId()); ps.setString(7, student.getGrade()); ps.setString(8, student.getMajor()); return ps.executeUpdate(); } }逻辑说明这里用PreparedStatement而不是Statement核心差别在于 SQL 语句先预编译参数通过setXxx方法绑定而不是拼接进 SQL 字符串。这样能直接从根上挡掉 SQL 注入——比如用户输入 OR 11 --时它只会被当成普通字符串参数传给数据库不会改变 SQL 语义。这在课设答辩时是老师必问的安全知识点。参数说明ps.setDate(4, new java.sql.Date(student.getBirthDate().getTime()))这行要注意表单传进来的birthDate如果是java.util.Date不能直接传给 JDBC必须转成java.sql.Date。Java 里这两个 Date 不是继承关系直接传会编译报错。删除和修改的写法类似只是 SQL 不同。删除学生时要考虑关联数据比如这个学生在成绩表里有记录直接删会导致孤儿数据// 删除学生同时清理关联成绩记录推荐写法 public boolean deleteStudent(int studentId) throws SQLException { Connection conn null; PreparedStatement ps1 null; PreparedStatement ps2 null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 ps1 conn.prepareStatement(DELETE FROM score WHERE student_id ?); ps1.setInt(1, studentId); ps1.executeUpdate(); ps2 conn.prepareStatement(DELETE FROM student WHERE id ?); ps2.setInt(1, studentId); ps2.executeUpdate(); conn.commit(); // 两条删除都成功才提交 return true; } catch (SQLException e) { conn.rollback(); // 任一失败回滚 throw e; } finally { DBUtil.close(conn, ps1, ps2); } }逻辑说明这段代码把「删成绩」和「删学生」包在同一个事务里setAutoCommit(false)后两条 DELETE 要么都成功要么都回滚不会出现学生删了成绩还在的脏数据。这是课设里少有人做对但加分项很高的细节。4.4 分页查询与条件组合LIST 页面不卡的要害学生信息多了以后一个页面全部渲染出来会很难看分页是必须的。分页 SQL 长这样// StudentDao.java 分页查询 public ListStudent findStudentsByPage(String keyword, int pageNum, int pageSize) throws SQLException { StringBuilder sql new StringBuilder(SELECT * FROM student WHERE 11); ListObject params new ArrayList(); if (keyword ! null !keyword.trim().isEmpty()) { sql.append( AND (name LIKE ? OR stu_no LIKE ?)); params.add(% keyword %); params.add(% keyword %); } sql.append( ORDER BY stu_no LIMIT ?, ?); params.add((pageNum - 1) * pageSize); params.add(pageSize); // 预编译执行返回结果 }// 统计总数用于计算总页数 public int countStudents(String keyword) throws SQLException { String sql SELECT COUNT(*) FROM student WHERE 11; // 条件拼接与上面保持一致 }逻辑说明WHERE 11看起来像废话实际是技巧——方便在循环里无脑AND 条件不用判断是否第一次拼接条件。LIMIT ?, ?第一个参数是偏移量(pageNum - 1) * pageSize算出跳过的行数第二个参数是每页几条。分页查询必须配合COUNT(*)统计总数JSP 页面才能计算总页数、生成页码条。参数说明LIKE模糊查询里的%必须拼在参数里传不能直接写在 SQL 里用preparedStatement处理因为?占位符引号内的文本不会被转义。这是 JDBC 写法里最容易出错的地方。报表统计方面课设里通常就是按专业分组统计人数、按班级算平均分SELECT major, COUNT(*) AS student_count FROM student GROUP BY major; SELECT s.class_id, c.class_name, AVG(sc.score) AS avg_score FROM student s LEFT JOIN score sc ON s.id sc.student_id LEFT JOIN class_info c ON s.class_id c.id GROUP BY s.class_id;这两条 SQL 分别是人数分布报表和成绩统计报表的雏形能直接被 JSP 页面调用展示也是答辩时展示系统价值的好素材。5. 避坑排查与常见问题五个最容易翻车的运行现场老 JSP 项目跑不起来翻来覆去就是那几个坑。这里按「现象 → 原因 → 解决」列五条真实出现过的踩坑记录至少能覆盖八成启动失败场景。5.1 Tomcat 启动闪退或端口占用现象点击 IDEA 里的 Tomcat 运行按钮控制台输出Port 8080 was already in use或者 Tomcat 日志刚出来就 process finished with exit code 1。原因8080 端口被其他进程占用常见的有本地跑着的 Nginx、另一个 Tomcat 实例、或者某个开发的 web 服务。解决先查端口占用再改配置# Windows netstat -ano | findstr 8080 taskkill /PID 占用端口的进程号 /F # macOS/Linux lsof -i :8080 kill -9 进程号如果不想杀进程直接改用其他端口。IDEA 里 Run → Edit Configurations → Tomcat Server → HTTP port 改成 8081访问路径就变成http://localhost:8081/student/。注意改完 Tomcat 端口后代码里数据库连接的jdbc:mysql://localhost:3306不需要动那是 MySQL 的端口跟 Tomcat 无关。5.2 MySQL 8 连接失败Public Key Retrieval 或认证插件报错现象启动项目后控制台报java.sql.SQLException: Public Key Retrieval is not allowed或Unable to load authentication plugin caching_sha2_password。原因MySQL 8.0 默认认证插件改成caching_sha2_password旧版 JDBC 驱动5.1.x不认识这个插件同时 8.0 的 SSL 和公钥检索机制要求连接参数显式声明。解决两个办法二选一。推荐第一个——换驱动换 URL# 驱动换成 8.0.33连接 URL 补全参数 jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/student_management?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue是 MySQL 8.0 连接时的必填项之一允许客户端向服务端请求公钥。如果不想换驱动也可以把 MySQL 用户改回老认证插件ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;改认证插件这条路在 MySQL 5.7 上没这个问题8.0 上执行后能兼容旧驱动但 MySQL 官方后续版本对mysql_native_password支持会越来越弱我建议还是换驱动更省心。5.3 JSP 页面中文乱码数据从页面到数据库全变问号现象页面显示中文正常但把数据存进 MySQL 后查出来全是???或者页面本身直接乱码。原因编码链路断了。JSP 文件编码、请求编码、响应编码、数据库连接编码、数据库表字符集这五环只要有一处不是 UTF-8 就乱。最隐蔽的一环是request.setCharacterEncoding(utf-8)没写POST 提交的中文在 Servlet 里已经乱码存进去自然是错的。解决按顺序检查以下位置!-- JSP 文件第一行指定页面编码 -- % page contentTypetext/html;charsetUTF-8 languagejava pageEncodingUTF-8 %// Servlet 里接收 POST 参数前设置请求编码必须放在 getParameter 之前 request.setCharacterEncoding(UTF-8);# JDBC URL 里带上字符集参数 characterEncodingutf8html 页面里的meta charsetUTF-8也要有。数据库连接和建表字符集在第三章已经提过不重复。这套检查顺序我实际排查时是从 JSP 第一行开始的因为 IDE 里 JSP 文件的编码和pageEncoding不一致时IDEA 虽然会警告但不会报错改完页面文件右下角那个编码为 UTF-8 再重新编译。5.4 找不到 JDBC 驱动类ClassNotFoundException: com.mysql.jdbc.Driver现象Tomcat 启动后访问任意页面报java.lang.ClassNotFoundException: com.mysql.jdbc.Driver或者No suitable driver found for jdbc:mysql://...。原因JDBC 驱动 jar 没放到正确位置或者没被项目加载。注意区分两个位置src目录里放了 jar 不代表 Tomcat 能加载必须放在web/WEB-INF/lib下或者通过 IDEA 的 Project Structure → Libraries 添加依赖。解决# 检查 web/WEB-INF/lib 目录下是否有 mysql-connector 的 jar ls web/WEB-INF/lib/如果确实有 jar 但还报错在 IDEA 里手动添加依赖File → Project Structure → Libraries → 加号() → Java → 选择 mysql-connector-java-xxx.jar → 勾选要加入的模块。确认后重启 Tomcat。还有一种情况是 jar 版本本身损坏或下载不完整重新下载一个官方 jar 替换即可。5.5 SQL 语句报错表名或字段名是 MySQL 保留字现象执行INSERT INTO user或SELECT * FROM grade时报You have an error in your SQL syntax或提示check the manual that corresponds to your MySQL server version。原因user、grade、order、desc这类词是 MySQL 的保留字。课设里特别喜欢把用户表叫user班级/成绩表叫grade踩雷概率极高。MySQL 8.0 对保留字的检查比 5.7 严格同样一条 SQL 在 5.7 能跑、8.0 上就报错。解决两种方式推荐第一种改表名或字段名为更明确的名字-- 用户表改名 sys_user班级表改名 class_info CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE, password VARCHAR(64), role VARCHAR(20) );如果已经建好表且不想动表名SQL 里加反引号转义SELECT * FROM user; INSERT INTO grade (class_name, major) VALUES (计科2101, 计算机科学与技术);反引号在 MySQL 里是标识符引号能解决保留字问题但这属于「能跑但脆弱」的写法换数据库或换驱动版本时行为可能不同。我从第一版就把用户表取名sys_user后面再也没有因为这个坑返工过。5.6 补充场景修改代码后不生效现象改了StudentDao.java里的一句 SQL重新部署后行为没变还是老逻辑。原因IDEA 里 Tomcat 的 artifact 是 exploded解压目录模式但有时候没有执行Build → Build Artifact → RebuildTomcat 加载的还是out里的旧 class 文件。解决每次修改 Java 源码后先Build → Rebuild Project再重启 Tomcat。修改 JSP 页面一般不用重启Tomcat 的 JSP 引擎会自动检测文件变化并重新编译但改 Java 类必须重启。这个坑我踩过不止一次后来养成了习惯不管改什么都先 Rebuild 再重启最多多花十几秒但能省掉大量「改了没反应」的排查时间。6. 进阶与验证WAR 部署、日志增强与数据一致性校验如果只是把它跑起来交作业前面五章已经够用。但想让这套系统在答辩或后续扩展时更有底气我把这几个进阶操作做一遍每一步都有明确的验证方式。第一件事打包成 WAR 放到独立 Tomcat 部署。IDEA 里Build → Build Artifacts → StudentManagement:war → Build构建完成后在out/artifacts下能找到.war文件。把它拷到 Tomcat 的webapps目录启动 Tomcat 后它会自动解压。这种部署方式模拟了真实的生产环境答辩演示时比较加分。注意独立 Tomcat 和 IDEA 内置 Tomcat 的编码配置可能不同如果页面乱码去conf/server.xml的 Connector 标签加URIEncodingUTF-8。第二件事给关键操作加日志。系统自带的处理一般是只给用户返回结果但排查问题时没日志会很难受。在LoginServlet和StudentServlet的增删改操作里加一行 log——不需要引入复杂的日志框架System.out.println配合时间戳足够了import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; // LoginServlet 登录成功和失败都打一行 DateTimeFormatter fmt DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss); System.out.println([ LocalDateTime.now().format(fmt) ] 登录成功: username);# Tomcat 里查看日志 tail -f logs/catalina.out第三件事验证数据一致性。前面 4.3 的删除逻辑用了事务怎么确认它真的生效做一个简单测试删一个在成绩表里有记录的学生然后执行 SQL 查SELECT * FROM score WHERE student_id 你删除的学生ID;如果返回空结果集说明关联记录被正确清理如果还有残留说明事务没生效。另一个测试是故意在两次 DELETE 之间抛异常比如临时把第二条 SQL 的表名改错看成绩表的数据有没有被删掉如果成绩记录还在说明rollback()生效了。第四件事把连接从 DriverManager 升级到连接池。这一课设源码常见写法是每次操作都DriverManager.getConnection高并发下性能上不去。把它替换成 Druid 或 HikariCP 是一个很好的扩展点改动范围集中在DBUtil// 用 Druid 连接池替换 DriverManagerDBUtil 关键改动 import com.alibaba.druid.pool.DruidDataSourceFactory; // 初始化时加载配置 DataSource dataSource DruidDataSourceFactory.createDataSource(props); // 获取连接从 dataSource.getConnection() Connection conn dataSource.getConnection();# druid.properties urljdbc:mysql://localhost:3306/student_management?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai usernameroot password你的密码 initialSize5 maxActive20 minIdle5Druid 也自带监控页面配置StatFilter后能在浏览器里看 SQL 执行频率和慢查询。这个替换工作量不大但说出去就是「有性能优化意识」在课设答辩里是亮点。从那以后我每次接手 JSP 课设源码都强制走一遍「先看 sql 字符集 → 再确认 JDBC 驱动版本 → 然后查 JSP 编码 → 最后才启动 Tomcat」这个流程一路下来基本没有跑不起来的。这套学生信息管理系统的源码整体结构不复杂核心功能都能跑通希望这份拆解能帮你在它基础上改出自己的版本祝顺利。本文还有配套的精品资源点击获取