ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java Web学生选课系统:JDK+Tomcat+MySQL环境闭环实战

Java Web学生选课系统:JDK+Tomcat+MySQL环境闭环实战 简介本资源是一套基于Java Web技术栈开发的完整学生选课管理系统实战项目面向Java初学者与Web开发入门学习者聚焦MVC架构实践、前后端交互及数据库集成能力训练。系统采用Java语言编写业务逻辑JSP实现动态页面展示Servlet处理HTTP请求与流程控制MySQL存储学生、课程、选课记录等核心数据并通过JDBC完成持久化操作覆盖用户登录鉴权、课程管理、选课冲突校验、成绩录入与查询等典型教务功能。压缩包共408个文件含43个Java源码、25个JSP页面、50个JS脚本、43个Class编译文件、15个CSS样式表及配套SQL、XML、properties配置文件等结构清晰便于分层理解与调试整体大小为6.59MB。已有6905人学习下载提供可直接部署运行的完整工程含备份文件.bak与典型DAO/Servlet实现类是掌握Java Web基础开发流程与项目组织规范的优质练手案例。1. 这不是“过时技术堆砌”而是一套能跑通、能调试、能交毕设、能改出真实业务逻辑的 Java Web 基础栈闭环你搜“JSP 学生选课系统”十有八九点开的是 GitHub 上 2016 年上传、README 里写着“仅供学习参考”、连 Tomcat 版本都没标清楚的压缩包——解压后web.xml里还配着servlet-classcom.servlet.AddCourseServlet/servlet-class但 src 目录下根本没这个类MySQL 表结构脚本里字段全用VARCHAR(255)硬扛学号、成绩、学期插入2024-2就报错更别说 IDEA 里右键 Run on Server 提示 “No servlet container configured”或者浏览器打开 localhost:8080 直接 404——连 index.jsp 都找不到。这不是你环境没配好是整套资源从设计第一天就没考虑过“在 JDK 17 Tomcat 10.1 MySQL 8.0 下能否真正跑起来”。而这份「JavaJSPServletMySQL 实现 Web 学生选课管理系统」是我去年带三届毕设学生复现、逐行 debug、重写 DAO 层、补全事务控制、适配 MySQL 8.0 认证插件后沉淀下来的可执行最小闭环它包含完整可运行的 7 张表student、course、teacher、selection、grade、department、admin所有 Servlet 均继承HttpServlet并显式覆盖doGet/doPostJSP 页面用 JSTL 替代 scriptlet% %MySQL 连接字符串强制启用useSSLfalseserverTimezoneAsia/Shanghai且每个关键操作如选课冲突检测、成绩录入校验、管理员批量导入都附带单元测试入口和日志埋点。适合正在赶毕设 deadline 的大三学生、需要快速搭建教学演示环境的实训教师以及想亲手摸透“请求怎么从浏览器进 Servlet、数据怎么经 JDBC 落库、页面怎么用 EL 表达式取值”的 Java Web 入门者——它不炫技但每一步都能 debug 到栈帧每一处报错都有明确修复路径。2. 从零启动环境链路必须对齐 JDK/Tomcat/MySQL 三版本坐标这套系统不是“写完就能跑”它的可运行性高度依赖三端版本协同。我见过太多同学卡在第一步IDEA 新建项目选了 “Web Application” 模板却用 JDK 17 编译Tomcat 9 启动时报java.lang.UnsupportedClassVersionError或者 MySQL 8.0 默认启用caching_sha2_password插件而旧版 JDBC 驱动不兼容导致Connection refused却误以为是端口没开。下面这三步缺一不可且顺序不能乱。2.1 JDK 8u291 或 JDK 11避免字节码与容器不兼容的玄学翻车Tomcat 9 及以下版本官方仅支持到 JDK 11Tomcat 10.1 才正式支持 JDK 17 的新特性如 sealed classes。而本系统 Servlet 接口调用方式、web.xml DTD 声明均基于 Servlet 4.0 规范对应 Tomcat 9.0.x强行用 JDK 17 编译会导致javax.servlet包无法解析。实测安全组合是 JDK 8u291长期支持版 Tomcat 9.0.83 MySQL 8.0.33。安装后验证# 终端执行确认输出为 1.8 或 11 java -version # 检查 JAVA_HOME 是否指向正确路径尤其 Windows 用户注意路径含空格时需加引号 echo $JAVA_HOME # Linux/macOS echo %JAVA_HOME% # Windows提示若已装 JDK 17请勿卸载而是通过 IDE 切换 Project SDK。在 IntelliJ IDEA 中File → Project Structure → Project → Project SDK → 选择已安装的 JDK 8 或 11同时 Settings → Build → Compiler → Java Compiler → Target bytecode version 也设为 1.8。2.2 Tomcat 9.0.83手动部署 WAR 包而非依赖 IDE 自动配置IDEA 的 “Run on Server” 功能常因自动添加额外 context path 或忽略 web.xml 中的welcome-file-list导致首页 404。强烈建议采用纯手工部署下载 Tomcat 9.0.83 注意是bin目录下的 zip非源码包解压到无中文、无空格路径如/opt/tomcat或D:\tomcat9将本系统导出的student-selection.war文件非整个 project 文件夹直接复制到$TOMCAT_HOME/webapps/目录下启动 TomcatLinux/macOS 执行$TOMCAT_HOME/bin/startup.shWindows 执行$TOMCAT_HOME/bin/startup.bat观察$TOMCAT_HOME/logs/catalina.out是否出现INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [...] has finished in [...] ms字样确认 WAR 包解压成功。此时访问http://localhost:8080/student-selection/注意末尾斜杠应看到登录页。若提示 404请检查webapps/student-selection/WEB-INF/web.xml是否存在且welcome-file-list中welcome-file值为index.jsp。2.3 MySQL 8.0.33必须显式配置时区与 SSL 关闭MySQL 8.0 默认启用caching_sha2_password认证插件而本系统使用的mysql-connector-java-5.1.47.jar经典驱动不支持该插件。解决方案不是降级 MySQL而是在连接 URL 中强制指定参数# jdbc.properties 文件中配置路径src/main/resources/jdbc.properties jdbc.urljdbc:mysql://localhost:3306/student_db?useUnicodetruecharacterEncodingUTF-8useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.passwordyour_password关键参数说明useSSLfalse关闭 SSL开发环境无需加密开启反而因证书缺失报错serverTimezoneAsia/Shanghai解决The server time zone value XXX is unrecognized错误characterEncodingUTF-8确保中文姓名、课程名不乱码useUnicodetrue配合上一条生效。注意若使用 Navicat 或 DBeaver 连接 MySQL 8.0需在连接属性中将 “Authentication Plugin” 改为mysql_native_password否则客户端登录失败。命令行可执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY your_password; FLUSH PRIVILEGES;3. 代码层拆解Servlet 生命周期与 JDBC 操作的真实映射关系很多教程把 Servlet 写成“接收参数→调用 DAO→跳转页面”的黑匣子却不说清doGet()和doPost()为何要分开处理、request.setAttribute()怎么被 JSP 的${}拿到、DAO 层为什么不能直接new Connection()。本系统将这些隐含逻辑全部暴露出来下面以“学生选课”核心流程为例逐层解析。3.1 Servlet 分发逻辑GET 查列表POST 提交动作绝不混用SelectCourseServlet.java是典型双态 Servlet// src/main/java/com/servlet/SelectCourseServlet.java public class SelectCourseServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // GET 请求展示可选课程列表查询 course 表 ListCourse courseList new CourseDAO().findAllCourses(); request.setAttribute(courseList, courseList); // 数据存入 request 域 request.getRequestDispatcher(/student/select_course.jsp).forward(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // POST 请求处理选课提交插入 selection 表 String studentId request.getParameter(studentId); String courseId request.getParameter(courseId); // 业务校验是否已选是否满员 if (new SelectionDAO().isAlreadySelected(studentId, courseId)) { request.setAttribute(error, 您已选修该课程); request.getRequestDispatcher(/student/select_course.jsp).forward(request, response); return; } if (!new CourseDAO().hasVacancy(courseId)) { request.setAttribute(error, 该课程名额已满); request.getRequestDispatcher(/student/select_course.jsp).forward(request, response); return; } // 执行插入 boolean success new SelectionDAO().selectCourse(studentId, courseId); request.setAttribute(message, success ? 选课成功 : 选课失败请重试); request.getRequestDispatcher(/student/select_result.jsp).forward(request, response); } }关键点说明doGet()只做查询和页面跳转不修改数据库doPost()处理表单提交执行 INSERT/UPDATE这是 RESTful 设计原则也是防止刷新页面重复提交的基石request.setAttribute()设置的属性仅在本次请求生命周期内有效即 forward 到 JSP 时可用JSP 中通过${courseList}直接访问校验逻辑放在 Servlet 层而非前端 JS因为 JS 可被禁用或绕过服务端校验是最后一道防线。3.2 DAO 层事务控制Connection 不是每次 new而是统一管理BaseDAO.java定义了连接获取与释放的模板// src/main/java/com/dao/BaseDAO.java public abstract class BaseDAO { private static final String DB_URL jdbc:mysql://localhost:3306/student_db?useUnicodetruecharacterEncodingUTF-8useSSLfalseserverTimezoneAsia/Shanghai; private static final String DB_USER root; private static final String DB_PASS your_password; protected Connection getConnection() throws SQLException { return DriverManager.getConnection(DB_URL, DB_USER, DB_PASS); } protected void closeResources(Connection conn, PreparedStatement pstmt, ResultSet rs) { if (rs ! null) try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } if (pstmt ! null) try { pstmt.close(); } catch (SQLException e) { e.printStackTrace(); } if (conn ! null) try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }而具体 DAO 如SelectionDAO.java则复用此连接// src/main/java/com/dao/SelectionDAO.java public class SelectionDAO extends BaseDAO { public boolean selectCourse(String studentId, String courseId) { String sql INSERT INTO selection (student_id, course_id, selection_time) VALUES (?, ?, NOW()); try (Connection conn getConnection(); PreparedStatement pstmt conn.prepareStatement(sql)) { pstmt.setString(1, studentId); pstmt.setString(2, courseId); return pstmt.executeUpdate() 0; // executeUpdate 返回影响行数 } catch (SQLException e) { e.printStackTrace(); return false; } } }为什么不用连接池本系统定位教学闭环刻意省略 HikariCP 等第三方池化组件让初学者看清Connection从创建到关闭的完整链路。实际生产环境必须替换为连接池否则高并发下会耗尽数据库连接。3.3 JSP 页面渲染EL 表达式 JSTL 标签替代 scriptletselect_course.jsp中不再出现% while(rs.next()) { %这类易出错的 Java 脚本而是用标准标签% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html headtitle选课页面/title/head body h2可选课程列表/h2 c:if test${not empty error} div stylecolor:red${error}/div /c:if table border1 trth课程编号/thth课程名称/thth学分/thth操作/th/tr c:forEach items${courseList} varcourse tr td${course.courseId}/td td${course.courseName}/td td${course.credits}/td td form actionSelectCourseServlet methodpost input typehidden namestudentId value${sessionScope.student.studentId} input typehidden namecourseId value${course.courseId} button typesubmit选课/button /form /td /tr /c:forEach /table /body /htmlJSTL 使用前提webapp/WEB-INF/lib/下必须有jstl-1.2.jar和standard-1.1.2.jar本系统已内置% taglib prefixc urihttp://java.sun.com/jsp/jstl/core %声明不可少${sessionScope.student.studentId}明确指定作用域避免pageContext与request混淆。4. 避坑指南五个血泪经验总结专治“明明代码一样却跑不通”这套系统最大的价值不是功能多炫而是把新手必踩的坑提前挖出来、标清楚、给解法。以下五条每一条都来自真实 debug 场景不是理论推测。4.1 现象启动 Tomcat 后访问http://localhost:8080/student-selection/报 404但http://localhost:8080/能看到 Tomcat 欢迎页原因WAR 包未正确解压或web.xml中welcome-file-list配置错误。常见于 IDEA 导出 WAR 时勾选了 “include libraries in the WAR” 导致包体过大解压失败或web.xml里welcome-file写成了index.html而实际文件是index.jsp。解决进入$TOMCAT_HOME/webapps/确认存在student-selection/文件夹而非student-selection.war文件残留检查student-selection/WEB-INF/web.xml确认welcome-file-list内容为welcome-file-list welcome-fileindex.jsp/welcome-file /welcome-file-list若仍 404临时将index.jsp复制一份命名为index.html测试是否是 JSP 引擎问题Tomcat 默认支持 JSP此步仅用于排除。4.2 现象登录成功后跳转到student_home.jsp页面显示${student.name}为空白但数据库里姓名字段有值原因Servlet 中request.setAttribute(student, student)的 key 与 JSP 中${student.name}的变量名不匹配或student对象的getName()方法返回 null。解决在 Servlet 的doPost()登录逻辑中打印System.out.println(Login success, student name: student.getName());确认对象属性已加载检查Student.java的 getter 方法是否为public String getName()注意大小写JSP EL 严格匹配驼峰命名JSP 中${student.name}的student必须与setAttribute(student, ...)的第一个参数完全一致区分大小写。4.3 现象MySQL 插入中文时显示???如 “张三” 变成 “??”原因MySQL 服务端、数据库、表、连接 URL 四层字符集未统一为 UTF8MB4。解决登录 MySQL执行SHOW VARIABLES LIKE character_set%;确认character_set_server为utf8mb4创建数据库时指定字符集CREATE DATABASE student_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;修改表字符集ALTER TABLE student CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;连接 URL 中必须含useUnicodetruecharacterEncodingUTF-8注意是UTF-8不是utf8。4.4 现象点击“选课”按钮后页面空白控制台无报错但数据库无新增记录原因表单action属性指向错误的 Servlet URL或web.xml中servlet-mapping的url-pattern与实际请求路径不匹配。解决浏览器按 F12 打开 Network 标签点击选课按钮查看发出的 POST 请求 URL如http://localhost:8080/student-selection/SelectCourseServlet对照web.xml中的配置servlet servlet-nameSelectCourseServlet/servlet-name servlet-classcom.servlet.SelectCourseServlet/servlet-class /servlet servlet-mapping servlet-nameSelectCourseServlet/servlet-name url-pattern/SelectCourseServlet/url-pattern /servlet-mapping确保表单action/SelectCourseServlet相对路径或actionSelectCourseServlet同目录下而非actionselectCourse或绝对路径漏掉上下文名。4.5 现象ClassNotFoundException: com.mysql.jdbc.Driver原因MySQL 8.0 驱动类名已从com.mysql.jdbc.Driver改为com.mysql.cj.jdbc.Driver但代码中仍用旧类名。解决检查src/main/java/com/dao/BaseDAO.java中Class.forName(com.mysql.jdbc.Driver)是否存在删除该行JDBC 4.0 规范要求驱动自动注册无需显式加载确保webapp/WEB-INF/lib/下的 MySQL 驱动 JAR 是mysql-connector-java-8.0.33.jar本系统已提供而非 5.x 版本。5. 进阶验证用 Postman 模拟请求 日志埋点定位真实瓶颈当系统跑通基础功能后下一步不是加新模块而是建立可验证的观测能力。很多同学改完代码不敢提交因为不知道“改对没”只能靠反复点页面猜。我习惯用两个低成本手段构建信心Postman 发送原始 HTTP 请求验证 Servlet 接口契约以及在 DAO 层加System.out.println()输出 SQL 执行耗时。5.1 Postman 验证 Servlet 接口绕过前端直击后端逻辑以“管理员添加课程”为例其 ServletAddCourseServlet接收 POST 请求参数为courseId,courseName,credits。在 Postman 中新建请求MethodPOSTURLhttp://localhost:8080/student-selection/AddCourseServletBody → x-www-form-urlencodedKeyValuecourseIdCS202401courseName数据结构credits3发送后观察响应 Body 是否为{status:success,message:课程添加成功}本系统 JSON 响应由response.getWriter().write(jsonString)输出。若返回 404说明web.xml中AddCourseServlet的 mapping 未生效若返回 500则检查 Tomcat logs 中 stack trace定位是空指针还是 SQL 语法错误。为什么不用浏览器表单浏览器表单提交会触发页面跳转掩盖了服务器真实响应Postman 显示原始响应体且可保存请求模板下次测试只需改参数效率提升 3 倍以上。5.2 DAO 层 SQL 执行耗时埋点一行代码揪出慢查询在BaseDAO.java的closeResources()方法前加入执行时间统计protected void logExecutionTime(long startTime, String sql) { long duration System.currentTimeMillis() - startTime; if (duration 100) { // 超过 100ms 认为慢查询 System.out.println([SLOW QUERY] sql took duration ms); } }然后在CourseDAO.java的findAllCourses()方法中调用public ListCourse findAllCourses() { long startTime System.currentTimeMillis(); String sql SELECT * FROM course ORDER BY course_id; // ... 执行查询 ... logExecutionTime(startTime, sql); return courseList; }启动系统后选课页面加载时控制台会输出类似[SLOW QUERY] SELECT * FROM course ORDER BY course_id took 128ms此时立刻去 MySQL 执行EXPLAIN SELECT * FROM course ORDER BY course_id;发现type为ALL全表扫描说明course_id缺少索引。执行ALTER TABLE course ADD INDEX idx_course_id (course_id);即可优化。5.3 毕设答辩必备三份可展示材料清单作为过来人我深知答辩时老师最常问“你怎么证明这个系统真的能用”。光说“我跑过了”没说服力必须准备三份材料数据库截图Navicat 中student_db库的 7 张表结构含字段类型、主键、外键、以及selection表的几条真实选课记录证明数据写入Tomcat 日志片段catalina.out中包含Deployment of web application archive [...] has finished和SELECT * FROM student WHERE student_id?的日志行证明部署成功且 SQL 执行Postman 测试报告导出 Postman Collection 的 JSON 文件或截图展示AddCourseServlet、SelectCourseServlet、GradeQueryServlet三个关键接口的 200 响应体证明接口契约成立。从那以后我每次交付毕设系统都强制走一遍 Postman 全接口测试 日志耗时分析 数据库快照留存。不是怕出错而是让“能跑”变成“可证”让答辩时的每一句“老师您看这里……”都有据可依。希望帮到你。本文还有配套的精品资源点击获取
返回列表