JSP+Servlet+MySQL学生管理系统手写实战指南
简介这是一套基于JSPServletJDBCMySQL技术栈开发的完整学生管理系统适用于高校Java Web课程设计、期末实训及毕业设计参考帮助初学者掌握MVC分层架构、数据库连接池配置、CRUD操作与前后端交互全流程。资源包共341个文件包含84个Java源码如StudentDaoImpl、TeacherDaoImpl等DAO实现类、84个编译后class文件、36个JSP页面负责展示层、61个JS脚本含表单验证与AJAX交互、28个CSS样式文件以及SQL建表语句、Jar依赖库和少量图片资源整体压缩包仅6.99MB轻量易部署。已有3323人学习下载作者已实测可稳定运行配套博文详述环境配置、功能模块划分与关键代码逻辑。读者可直接导入Eclipse/IDEA运行获取完整项目结构、规范化的JavaBean封装、Servlet请求分发机制、JDBC事务处理示例及常见异常排错提示。1. 这不是“老古董”用 JSPServletJDBCMySQL 搭建学生管理系统为什么今天还值得从头手写一遍很多人看到“JSPServletJDBCMySQL”第一反应是这不就是教科书里的经典三层架构 demo 吗早该被 Spring Boot、Vue 前后端分离取代了。但真实情况是某高校信息学院连续三年在《Web 应用开发实训》课中仍强制要求学生完全手写一个无框架依赖的学生管理系统某省职业院校技能大赛 Web 开发模块明确限定技术栈为“JSP/Servlet/JDBC/MySQL”禁用任何 ORM 或 MVC 框架更关键的是——在中小型企业内部培训系统、教务辅助工具、实验室资产登记等轻量级管理场景中一个结构清晰、无外部依赖、可单机部署的 JSPServlet 系统反而比动辄要配 Nginx、Redis、Maven 多模块的现代方案更易交付、更易维护、故障面更小。这个.zip包背后不是怀旧而是一套被反复验证过的、面向教学与轻量落地的最小可行 Web 架构范式。它不追求高并发但要求逻辑闭环不强调炫酷 UI但必须字段完整、事务可靠、SQL 可见、错误可追。本文就带你从零开始把student_management_system.zip解压后的骨架真正跑通、调稳、改活——不是照着 PPT 抄代码而是理解每一行request.getParameter()为什么写在这里每一条PreparedStatement的?为何不能换成字符串拼接以及 MySQL 的autocommitfalse在增删改时到底锁住了什么。你不需要 Spring 的自动装配但必须亲手写Class.forName(com.mysql.cj.jdbc.Driver)你不用 React 的虚拟 DOM但得搞懂out.write()和response.sendRedirect()的本质区别你不会配置application.yml但得会手动在web.xml里注册 Servlet 映射路径。这不是倒退而是把 Web 开发的“地基钢筋”一根根摸清楚。如果你正面临课程设计答辩、实习项目交付或想给简历补上一个“能讲清请求生命周期”的实战项目这篇笔记就是为你写的。2. 从解压到启动本地环境搭建与最小可运行流程2.1 环境版本对齐别让 JDK 17 和 Tomcat 10 杀死你的第一个 request很多同学解压 ZIP 后双击index.jsp浏览器显示空白页或者启动 Tomcat 报java.lang.ClassNotFoundException: javax.servlet.ServletException——这不是代码错了是环境没对齐。JSPServlet 是 Java EE现 Jakarta EE规范其 API 包名在 Tomcat 9 和 Tomcat 10 有根本性变化Tomcat 9 及以下使用javax.servlet.*Tomcat 10升级为jakarta.servlet.*而绝大多数教学用.zip包包括本项目基于 Tomcat 8/9 编写其web.xml、import语句、甚至pom.xml如果含 Maven都写的是javax.*。强行用 Tomcat 10 运行必然类找不到。提示本项目实测兼容性最佳组合为JDK 8u202 Tomcat 8.5.94 MySQL 5.7.42若你已装 JDK 17请单独下载 Tomcat 8.5.x非 10.x并确保JAVA_HOME指向 JDK 8。Tomcat 8.5 是最后一个支持javax.servlet且仍获安全更新的稳定分支教学与轻量部署足够可靠。验证方式解压后进入student_management_system/WEB-INF/lib/检查是否存在servlet-api.jar或javax.servlet-api-*.jar。若存在且包名含javax.servlet则必须配 Tomcat 8/9。2.2 目录结构还原WEB-INF是生命线别把它拖进webapps根目录解压 ZIP 后常见错误操作是直接把整个文件夹拖进tomcat/webapps/结果访问http://localhost:8080/student_management_system/报 404。原因在于标准 Java Web 应用必须遵循 Servlet 规范定义的目录结构核心是WEB-INF文件夹的位置和内容。正确做法是将 ZIP 解压得到的文件夹假设名为student_management_system整体复制到tomcat/webapps/下确保该文件夹内直接包含WEB-INF子目录而非嵌套在另一层文件夹里WEB-INF内必须有web.xml部署描述符和classes/编译后的.class文件、lib/JDBC 驱动等 jar所有 JSP 页面如index.jsp,add.jsp必须放在WEB-INF同级目录下即webapps/student_management_system/下不可放入WEB-INF内JSP 不可被直接 URL 访问但可被 forward/include。典型错误结构❌tomcat/webapps/student_management_system/ └── student_management_system/ ← 多了一层 ├── WEB-INF/ └── index.jsp正确结构✅tomcat/webapps/student_management_system/ ├── WEB-INF/ │ ├── web.xml │ ├── classes/ │ │ └── com/example/servlet/AddStudentServlet.class │ └── lib/ │ └── mysql-connector-java-5.1.49.jar ├── index.jsp ├── list.jsp └── add.jsp若你发现WEB-INF被藏在子文件夹里请手动剪切出来放到与 JSP 同级位置。2.3 JDBC 驱动加载mysql-connector-java-5.1.49.jar必须进WEB-INF/lib项目 ZIP 中通常自带mysql-connector-java-*.jar常见为 5.1.x 版本这是连接 MySQL 的桥梁。它必须放在WEB-INF/lib/下而非WEB-INF/classes/或 Tomcat 全局lib/目录。原因Servlet 容器按“Web 应用类加载器”隔离各应用的依赖WEB-INF/lib是其默认扫描路径。若缺失该 jar启动时无报错但首次执行数据库操作如点击“查询所有学生”时控制台会抛出java.lang.ClassNotFoundException: com.mysql.jdbc.Driver或新版驱动java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver解决步骤确认WEB-INF/lib/下存在mysql-connector-java-5.1.49.jar推荐此版本兼容性最好若使用 MySQL 8.0需升级驱动至mysql-connector-java-8.0.28.jar并修改DBUtil.java中的驱动类名为com.mysql.cj.jdbc.Driver重启 TomcatJAR 包变更必须重启容器才生效热部署不识别新加入的lib。参数说明mysql-connector-java-5.1.49.jar支持 MySQL 5.5–5.7URL 格式为jdbc:mysql://localhost:3306/dbname?useUnicodetruecharacterEncodingUTF-88.0 驱动 URL 需加serverTimezoneGMT%2B8参数否则可能报时区错误。3. 数据库初始化建库、建表、插测试数据三步闭环3.1 创建数据库与用户用CREATE DATABASE而非图形化工具点点点项目 ZIP 中常附带db.sql或init.sql但新手常犯的错误是用 Navicat 或 MySQL Workbench 打开 SQL 文件全选执行——结果报错Unknown database student_db。这是因为 SQL 文件里第一条通常是USE student_db;但数据库还没创建。正确顺序必须是登录 MySQL命令行或工具手动创建数据库CREATE DATABASE student_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;创建专用用户非 root并授权CREATE USER student_userlocalhost IDENTIFIED BY Stu123456; GRANT ALL PRIVILEGES ON student_db.* TO student_userlocalhost; FLUSH PRIVILEGES;为什么不用 root教学系统虽在本地但养成最小权限习惯至关重要。student_user只能操作student_db且密码含大小写字母数字符号符合基础安全实践。3.2 执行建表 SQL注意AUTO_INCREMENT和VARCHAR长度陷阱典型student表结构如下摘自常见db.sqlCREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20) NOT NULL, gender VARCHAR(10), age INT, class_name VARCHAR(30), phone VARCHAR(15), email VARCHAR(50) );这里有两个易踩坑点VARCHAR(20)对中文姓名太窄UTF8MB4 下一个汉字占 4 字节VARCHAR(20)最多存 5 个汉字20÷4。建议改为VARCHAR(50)覆盖“欧阳修远”等长名phone VARCHAR(15)合理但若未来要校验手机号格式应在 Servlet 层做正则^1[3-9]\\d{9}$绝不在数据库设 CHECK 约束MySQL 5.7 不支持8.0 支持但教学环境常禁用。执行建表后务必用DESCRIBE student;验证字段类型、是否允许 NULL、主键是否生效。3.3 插入初始测试数据用INSERT ... VALUES而非INSERT ... SELECT为快速验证功能插入 3–5 条测试数据INSERT INTO student (name, gender, age, class_name, phone, email) VALUES (张三, 男, 20, 计算机2101, 13800138000, zhangsanexample.com), (李四, 女, 19, 软件工程2102, 13900139000, lisiexample.com), (王五, 男, 21, 网络工程2103, 13700137000, wangwuexample.com);注意不要用INSERT INTO student SELECT ... FROM other_table因为other_table不存在。教学项目必须保证db.sql是自包含的、可独立执行的脚本。执行后用SELECT * FROM student;确认数据已入库且id自增正常首条id1。4. Servlet 请求链路打通从index.jsp到ListStudentServlet的完整生命周期4.1web.xml中的 Servlet 映射url-pattern决定你能访问哪个地址web.xml是整个 Web 应用的“总开关”。打开WEB-INF/web.xml找到类似以下配置servlet servlet-nameListStudentServlet/servlet-name servlet-classcom.example.servlet.ListStudentServlet/servlet-class /servlet servlet-mapping servlet-nameListStudentServlet/servlet-name url-pattern/list/url-pattern /servlet-mapping这意味着当浏览器访问http://localhost:8080/student_management_system/list时Tomcat 会将请求交给com.example.servlet.ListStudentServlet类处理。关键点servlet-name是内部标识符servlet和servlet-mapping中必须严格一致url-pattern/list/url-pattern是对外暴露的路径不包含项目名student_management_system是 Context Path由 Tomcat 自动添加若你想通过index.jsp的按钮跳转到列表页HTML 中应写a hreflist查看所有学生/a !-- 正确相对路径 -- !-- 而非 -- a href/student_management_system/list查看所有学生/a !-- 错误硬编码 Context Path --4.2ListStudentServlet的核心逻辑doGet中的三板斧打开src/com/example/servlet/ListStudentServlet.java或WEB-INF/classes/com/example/servlet/ListStudentServlet.class反编译其doGet方法典型结构为protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 调用 DAO 获取学生列表 ListStudent students StudentDAO.findAll(); // 2. 将数据存入 request 域 request.setAttribute(students, students); // 3. 转发forward到 list.jsp request.getRequestDispatcher(list.jsp).forward(request, response); }这三步缺一不可Step 1StudentDAO.findAll()内部通过DBUtil.getConnection()获取连接执行SELECT * FROM student用ResultSet遍历并封装为Student对象列表Step 2request.setAttribute()将数据绑定到本次请求作用域list.jsp中才能用${students}EL 表达式取到Step 3forward是服务器端跳转URL 不变仍是/list且request域数据可传递若用sendRedirect(list.jsp)则 URL 变为/list.jsp且request域丢失重定向是两次 HTTP 请求。参数说明request.setAttribute(students, students)中students是 keylist.jsp中c:forEach items${students} vars的items值必须与此 key 一致。4.3list.jsp的 EL 与 JSTL 渲染为什么${students}不显示list.jsp中常见代码% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % ... c:forEach items${students} vars tr td${s.id}/td td${s.name}/td td${s.gender}/td /tr /c:forEach若页面空白或报错According to TLD or attribute directive in tag file, attribute items does not accept any expressions原因有二缺少 JSTL jarWEB-INF/lib/下必须有jstl-1.2.jar和standard-1.1.2.jarJSP 2.0 推荐用jstl-1.2.jar单文件TLD URI 过时Tomcat 8 已弃用http://java.sun.com/jsp/jstl/core应改为http://java.sun.com/jsp/jstl/core旧或更推荐http://xmlns.jcp.org/jsp/jstl/core新。检查web.xml是否声明了web-app_3_0.xsd若是则用新 URI。修正后重启 Tomcat访问/list即可看到表格。5. 避坑指南5 个血泪经验换来的高频翻车点与解法5.1 现象点击“添加学生”提交后页面空白控制台无报错原因AddStudentServlet的doPost方法中request.setCharacterEncoding(UTF-8)调用位置错误。它必须在读取任何请求参数getParameter()之前调用否则中文参数乱码后续name null导致空指针但未捕获异常Servlet 静默失败。解决在doPost开头立即加request.setCharacterEncoding(UTF-8); // 必须第一行 String name request.getParameter(name); // ...后续逻辑5.2 现象删除学生后刷新列表被删学生又出现原因删除操作未开启事务或DELETE语句后未conn.commit()。JDBC 默认autocommittrue但某些DBUtil实现为提升性能设为false却忘记在delete()方法末尾conn.commit()。解决检查StudentDAO.delete(int id)方法在ps.executeUpdate()后添加if (!conn.getAutoCommit()) { conn.commit(); // 确保删除生效 }5.3 现象login.jsp登录成功后跳转到index.jsp但 URL 显示http://.../index.jsp且再次刷新变成 404原因登录 Servlet 使用了response.sendRedirect(index.jsp)这是客户端重定向暴露了 JSP 路径。而index.jsp本就不该被直接访问应通过 Servlet 控制入口。解决改为request.getRequestDispatcher(index.jsp).forward(request, response)或更佳实践——登录成功后sendRedirect(list)由ListStudentServlet统一处理首页逻辑。5.4 现象MySQL 中插入中文显示为???但数据库、表、字段均为utf8mb4原因JDBC URL 缺少字符集参数或参数值错误。仅characterEncodingUTF-8不够必须显式指定useUnicodetrue。解决修正DBUtil.getConnection()中的 URLString url jdbc:mysql://localhost:3306/student_db?useUnicodetruecharacterEncodingUTF-8serverTimezoneGMT%2B8;5.5 现象修改学生信息时UPDATE语句执行成功但数据库未更新原因UPDATE的WHERE条件用的是前端传来的id但request.getParameter(id)返回的是字符串而数据库id是INT。若未做Integer.parseInt()PreparedStatement会把字符串id当作VARCHAR绑定导致WHERE id 1无法匹配INT类型的id1MySQL 会隐式转换但某些严格模式下失败。解决在UpdateStudentServlet中务必解析int id Integer.parseInt(request.getParameter(id)); // 关键 ps.setInt(7, id); // 第7个 ? 是 WHERE id ?6. 进阶技巧让这个“老系统”具备生产级健壮性的 3 个关键改造6.1 用Filter统一处理字符编码告别每个 Servlet 里写setCharacterEncoding重复代码是维护噩梦。与其在每个 Servlet 的doGet/doPost开头写request.setCharacterEncoding(UTF-8)不如用Filter一劳永逸。新建src/com/example/filter/EncodingFilter.javapublic class EncodingFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req (HttpServletRequest) request; HttpServletResponse resp (HttpServletResponse) response; req.setCharacterEncoding(UTF-8); resp.setCharacterEncoding(UTF-8); chain.doFilter(req, resp); } }并在web.xml中注册放在所有servlet-mapping之前filter filter-nameEncodingFilter/filter-name filter-classcom.example.filter.EncodingFilter/filter-class /filter filter-mapping filter-nameEncodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping效果所有请求/list,/add,/delete在到达 Servlet 前编码已统一设置。这是教科书级的横切关注点分离也是你向面试官证明“我懂设计模式”的最简实例。6.2 用try-with-resources重构 DAO彻底消灭Connection泄露原始StudentDAO常见写法Connection conn null; PreparedStatement ps null; try { conn DBUtil.getConnection(); ps conn.prepareStatement(sql); // ... } finally { if (ps ! null) ps.close(); if (conn ! null) conn.close(); // 但若 ps.close() 抛异常conn.close() 就不执行 }风险极高ps.close()异常 →conn.close()跳过 → 连接泄露 → Tomcat 连接池耗尽。改造为try-with-resourcesJDK 7public static int delete(int id) throws SQLException { String sql DELETE FROM student WHERE id ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, id); return ps.executeUpdate(); } // 自动关闭 conn 和 ps无论是否异常 }参数说明try (Resource res ...)中的资源必须实现AutoCloseable接口Connection、PreparedStatement、ResultSet均满足。这是 Java 世界里最优雅的资源管理语法没有之一。6.3 用EL函数替代 Java 代码块让 JSP 真正“只负责展示”list.jsp中常见反模式% String gender s.getGender(); if (male.equals(gender)) out.print(男); else if (female.equals(gender)) out.print(女); else out.print(未知); %Java 代码块混在 JSP 中难以测试、无法复用、违反 MVC 分离原则。正确做法自定义 EL 函数创建src/com/example/tag/GenderConverter.javapublic class GenderConverter { public static String toChinese(String gender) { if (male.equalsIgnoreCase(gender)) return 男; if (female.equalsIgnoreCase(gender)) return 女; return 未知; } }编写WEB-INF/gender.tldTag Library Descriptor?xml version1.0 encodingUTF-8? taglib xmlnshttp://java.sun.com/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd version2.1 tlib-version1.0/tlib-version short-namegender/short-name urihttp://example.com/gender/uri function nametoChinese/name function-classcom.example.tag.GenderConverter/function-class function-signaturejava.lang.String toChinese(java.lang.String)/function-signature /function /tagliblist.jsp中使用% taglib prefixg urihttp://example.com/gender % ... td${g:toChinese(s.gender)}/td价值从此 JSP 里只有 HTML 和 EL业务逻辑全部下沉到 Java 类。这是迈向现代化 Web 开发的第一步——视图层纯净化。我带过的 A 同学靠这一招在课程答辩中被导师当场追问“如何扩展支持英文性别”他脱口而出“改toChinese方法为toDisplay加locale参数”顺利拿下满分。希望帮到你。本文还有配套的精品资源点击获取