news 2026/10/1 1:54:51

JavaWeb选课系统实训全流程:从SQL初始化到Tomcat部署避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaWeb选课系统实训全流程:从SQL初始化到Tomcat部署避坑指南

简介:这份JavaWeb实训资料包面向计算机相关专业学生与Java初学者,围绕学生选课系统这一典型Web项目,提供从需求分析到代码实现的完整实训方案。系统按角色划分功能:学生可注册登录、浏览课程、选课退选并查询已选结果;教师可发布与删除课程、查看选课情况,涵盖注册登录、学生选课、选课结果浏览、教师浏览选课结果、教师添加课程五大模块,适合课程设计、期末大作业或面试项目练习。压缩包共111个文件,约20.84MB,包含java源码、jsp页面、class编译文件、css与scss样式、sql建库脚本,以及实训报告docx和答辩pptx,另附jar依赖与前端字体资源,结构完整便于直接运行与二次开发。目前已有4575人学习下载,可帮助读者快速理解Servlet、DAO分层与数据库交互流程,并借助报告与PPT完成答辩准备。

1. 从一份能跑通的 JavaWeb 选课系统说起

很多同学做 JavaWeb 实训,卡住的地方往往不是 Servlet 本身,而是「代码能编译、数据库连不上、页面跳转 404」这三件事同时发生。这份学生选课系统资源包,包含实训报告、答辩 PPT、完整源代码和 SQL 文件,正好覆盖了从建库到部署的全流程。它解决的核心问题是:让你在有限时间内跑通一个具备注册登录、学生选课、退选、教师发布课程、查看选课结果五个模块的完整 Web 应用。适合正在做课程设计、期末大作业或需要一份可参考的 JavaWeb 项目完整案例 MySQL 实现的同学。源码里能看到DBUtil.class、LoginServlet.class、CourseDao.class这些编译产物,说明项目已经过至少一轮构建,不是半成品。

2. 环境搭建与数据库初始化:把 SQL 文件变成可连的库

2.1 为什么先处理数据库而不是先开 IDE

拿到资源包后,最常见的翻车顺序是:先导入项目到 IDEA,再找数据库配置,最后发现 SQL 文件里的库名和代码里的 JDBC URL 对不上。我一般会反过来,先把 SQL 文件在 MySQL 里执行完,确认表结构和初始数据都在,再去改代码里的连接参数。这份资源的 SQL 文件里通常包含建库、建表、插入测试课程和测试账号的语句。执行前先看一眼文件头部有没有CREATE DATABASE,如果没有,需要手动建库再USE。

-- 先确认 SQL 文件里的库名,假设是 course_selection CREATE DATABASE IF NOT EXISTS course_selection DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; USE course_selection; -- 执行资源包里的建表语句后,检查三张核心表 SHOW TABLES; -- 预期看到:user、course、selected_course(表名以实际 SQL 为准) -- 确认测试账号存在 SELECT id, username, role FROM user LIMIT 5; -- role 字段通常用 0/1 或 student/teacher 区分身份

执行完 SQL 后,重点核对user表的role字段类型和取值。如果代码里用Integer接收,而 SQL 里存的是varchar的"student",登录后跳转就会出错。这一步花两分钟,能省掉后面半小时的排查。

2.2 IDEA 导入与 Tomcat 配置的关键参数

把项目导入 IDEA 后,先检查Project Structure里的Modules是否识别到Webfacet。如果没有,手动添加Web并指向web目录(或WebContent,取决于项目结构)。接着配置 Tomcat:Run→Edit Configurations→+→Tomcat Server→Local。在Deployment标签页添加Artifact,选择war exploded模式,这样改 JSP 或 HTML 不用重启。

# 如果项目用 Maven,先确认 pom.xml 里的 servlet-api 版本 # 常见坑:scope 写成 compile 会导致 Tomcat 启动时类冲突 # 正确写法 <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency>

provided的含义是:编译时用这个 jar,打包时不放进去,因为 Tomcat 自己带了。如果写成默认的compile,部署后大概率报ClassCastException或NoSuchMethodError。另外检查DBUtil里的 JDBC URL,常见格式是jdbc:mysql://localhost:3306/course_selection?useSSL=false&serverTimezone=Asia/Shanghai。MySQL 8 以上必须加serverTimezone,否则连接直接抛异常。

2.3 编译产物与源码的对应关系

资源正文里列出的TeacherSkimAll.class、StudnetSkimServlet1.class(注意原文拼写是 Studnet,不是 Student)、AddCourse.class、SkimSelectedCourse.class、CourseDao.class、TeacherSkimServlet.class、LoginServlet.class、UpdatePassword.class、Register.class,这些是编译后的字节码。如果你拿到的是源码包,应该能找到同名的.java文件。重点看LoginServlet和Register这两个类,它们决定了身份选择逻辑。Register里通常有一个role参数,从下拉框取值后存入数据库。LoginServlet根据role字段决定跳转到学生端还是教师端。

提示:如果源码里StudnetSkimServlet1拼写有误,不要急着改类名,先确认web.xml或注解里的映射路径是否一致。改类名不改映射,等于白改。

3. 五个功能模块的代码走读与参数传递

3.1 注册登录:身份字段如何影响后续跳转

注册模块的核心是Register类,它接收表单的username、password、role三个参数。role一般用request.getParameter("role")获取,值可能是"student"或"teacher"。存入数据库前,建议做一次非空校验和长度校验,避免空字符串进库。登录模块的LoginServlet先根据用户名和密码查user表,查到后取出role,再用response.sendRedirect()跳转。

// LoginServlet 核心逻辑示意 String username = request.getParameter("username"); String password = request.getParameter("password"); User user = userDao.findByUsernameAndPassword(username, password); if (user == null) { request.setAttribute("msg", "用户名或密码错误"); request.getRequestDispatcher("login.jsp").forward(request, response); } else if ("teacher".equals(user.getRole())) { response.sendRedirect("teacherIndex.jsp"); } else { response.sendRedirect("studentIndex.jsp"); }

这里的关键参数是role的取值必须和注册时写入的一致。如果注册时存的是"1",登录时判断"teacher",就会永远跳到学生端。排查方法:登录失败或跳转错误时,先在数据库里SELECT role FROM user WHERE username='你的测试账号',确认实际存储值。

3.2 学生选课与退选:课程容量与重复选课校验

学生端的核心 Servlet 是StudnetSkimServlet1和SkimSelectedCourse。前者负责展示所有课程列表,后者负责处理选课和退选请求。选课逻辑里必须有两个校验:课程是否已满、学生是否已选过该课。常见做法是在CourseDao里写一个getSelectedCount(courseId)方法,和course.capacity比较。

// 选课前的双重校验 int selectedCount = courseDao.getSelectedCount(courseId); Course course = courseDao.findById(courseId); if (selectedCount >= course.getCapacity()) { request.setAttribute("msg", "该课程已满员"); request.getRequestDispatcher("courseList.jsp").forward(request, response); return; } if (selectedCourseDao.exists(studentId, courseId)) { request.setAttribute("msg", "你已选过这门课"); request.getRequestDispatcher("courseList.jsp").forward(request, response); return; } selectedCourseDao.insert(studentId, courseId); response.sendRedirect("skimSelectedCourse");

capacity字段在course表里通常是int类型,默认值可以设为 30 或 50。selected_course表里要有student_id和course_id的联合唯一索引,否则并发选课时可能插入重复记录。退选就是DELETE FROM selected_course WHERE student_id=? AND course_id=?,执行后重定向回已选列表。

3.3 教师端:发布课程与查看选课结果

教师端的AddCourse负责插入新课程,TeacherSkimAll和TeacherSkimServlet负责展示所有课程的选课情况。发布课程时,表单通常包含courseName、capacity、teacherId等字段。teacherId从 session 里取,不要从表单隐藏域取,否则学生可以伪造。

// AddCourse 插入课程 HttpSession session = request.getSession(); User teacher = (User) session.getAttribute("user"); if (teacher == null || !"teacher".equals(teacher.getRole())) { response.sendRedirect("login.jsp"); return; } String courseName = request.getParameter("courseName"); int capacity = Integer.parseInt(request.getParameter("capacity")); courseDao.insert(new Course(courseName, capacity, teacher.getId())); response.sendRedirect("teacherSkimAll");

查看选课结果时,TeacherSkimAll通常会联表查询course、selected_course、user,统计每门课的已选人数。如果页面显示人数为 0 但数据库里有记录,检查联表条件是否写成了INNER JOIN而漏掉了没有选课记录的课程。改用LEFT JOIN可以保留所有课程。

3.4 修改密码与 Session 管理

UpdatePassword类处理密码修改,通常需要旧密码、新密码、确认新密码三个字段。校验旧密码时,从 session 里取当前用户,再查库比对。修改成功后建议让 session 失效并跳转登录页,强制重新登录。

// UpdatePassword 核心步骤 User user = (User) session.getAttribute("user"); String oldPwd = request.getParameter("oldPassword"); String newPwd = request.getParameter("newPassword"); if (!user.getPassword().equals(oldPwd)) { request.setAttribute("msg", "旧密码错误"); request.getRequestDispatcher("updatePassword.jsp").forward(request, response); return; } userDao.updatePassword(user.getId(), newPwd); session.invalidate(); response.sendRedirect("login.jsp");

Session 超时时间可以在web.xml里配置,默认 30 分钟。如果学生选课中途 session 过期,提交选课请求时会跳回登录页,已填数据丢失。常见做法是在选课页面加一个定时刷新或提示。

4. 避坑与常见问题排查

4.1 数据库连接报错:Access denied 与 Unknown database

现象:Tomcat 启动后访问登录页,提交表单后报java.sql.SQLException: Access denied for user 'root'@'localhost'。原因:DBUtil里的用户名或密码和 MySQL 实际不一致,或者 MySQL 8 的caching_sha2_password插件导致旧驱动无法认证。解决:先确认DBUtil里的url、username、password三个参数;如果 MySQL 是 8.0 以上,把驱动换成mysql-connector-java-8.0.xx,并在 URL 后加allowPublicKeyRetrieval=true。

现象:报Unknown database 'course_selection'。原因:SQL 文件里的库名和代码里写的不一致,或者根本没执行建库语句。解决:SHOW DATABASES;确认库是否存在,不存在就手动CREATE DATABASE。

4.2 404 与 500:路径映射和空指针

现象:访问http://localhost:8080/项目名/login报 404。原因:web.xml里的url-pattern和表单action不匹配,或者注解@WebServlet("/login")没生效。解决:检查web.xml的servlet-mapping,如果用了注解,确认web.xml的metadata-complete不是true。

现象:登录后报 500,日志里是NullPointerException。原因:LoginServlet里从 session 取用户时 session 为空,或者DBUtil.getConnection()返回 null。解决:在DBUtil里加日志打印连接状态,在 Servlet 里对 session 和查询结果做非空判断。

4.3 中文乱码:从表单到数据库的三层编码

现象:注册时输入中文用户名,数据库里显示???。原因:JSP 页面、Servlet 请求、数据库连接三层编码不一致。解决:JSP 页面头部加<%@ page contentType="text/html;charset=UTF-8" %>;Servlet 里在doPost第一行加request.setCharacterEncoding("UTF-8");JDBC URL 加useUnicode=true&characterEncoding=utf8;数据库和表用utf8mb4。

4.4 选课人数不更新:事务与缓存

现象:学生选课后,教师端查看已选人数还是旧值。原因:选课插入和人数统计不在同一个事务里,或者页面有缓存。解决:在CourseDao的统计方法里直接SELECT COUNT(*),不要用内存变量;如果用了连接池,确认每次查询用的是新连接。浏览器端可以加response.setHeader("Cache-Control", "no-cache")。

4.5 编译产物与源码不一致

现象:改了.java文件,重启 Tomcat 后行为没变。原因:IDEA 没有重新编译,或者out目录里的.class是旧的。解决:Build→Rebuild Project,然后检查out/artifacts下的war exploded是否更新。如果资源包里只有.class没有.java,那只能反编译或找原作者要源码。

5. 从能跑到好用:三个进阶技巧

5.1 用过滤器统一处理编码和登录校验

每个 Servlet 里都写request.setCharacterEncoding("UTF-8")很啰嗦,而且容易漏。常见做法是加一个CharacterEncodingFilter,在web.xml里配置成拦截所有请求。登录校验也可以做成LoginFilter,拦截学生端和教师端路径,检查 session 里有没有用户。

@WebFilter("/*") public class CharacterEncodingFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding("UTF-8"); resp.setContentType("text/html;charset=UTF-8"); chain.doFilter(req, resp); } }

@WebFilter("/*")表示拦截所有请求。如果只想拦截特定路径,改成/student/*或/teacher/*。注意过滤器的执行顺序,编码过滤器要放在登录校验过滤器之前。

5.2 用连接池替代每次 DriverManager

DBUtil里如果每次DriverManager.getConnection(),并发一高就崩。常见做法是引入 Druid 或 HikariCP。以 Druid 为例,加依赖后在DBUtil里初始化DruidDataSource,设置initialSize、maxActive、maxWait三个参数。

参数建议值说明
initialSize5启动时创建的连接数
maxActive20最大连接数,按并发量调整
maxWait3000获取连接的超时时间,单位毫秒
validationQuerySELECT 1连接有效性检测语句

改完后,DBUtil.getConnection()从dataSource.getConnection()取,close()改成connection.close()归还连接。这一步能让选课高峰期的响应稳定很多。

5.3 答辩 PPT 与实训报告的对齐技巧

资源包里的实训报告和答辩 PPT 通常需要对应。我一般会先把代码里的五个模块画成一张功能结构图,然后报告里每个模块写「功能描述 + 核心类 + 关键 SQL」,PPT 里每个模块放一张截图加三行说明。截图要包含浏览器地址栏和页面关键区域,证明是真实运行的结果。如果老师问「选课并发怎么处理」,就答联合唯一索引加连接池,不要扯分布式锁。

注意:答辩时如果被问到StudnetSkimServlet1的拼写,直接承认是笔误,说明功能不受影响,比强行解释更稳妥。

从那以后我每次拿到这类实训资源,都强制先跑 SQL 再开 IDE,确认数据库通了再动代码。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 1:54:25

如何免费把PC游戏串到电视和手机上?Sunshine游戏串流完整指南

如何免费把PC游戏串到电视和手机上&#xff1f;Sunshine游戏串流完整指南 【免费下载链接】Sunshine Self-hosted game stream host for Moonlight. 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine Sunshine 是一个开源、自托管的游戏串流服务器&#xff0…

作者头像 李华
网站建设 2026/10/1 1:54:06

NSQ 开源贡献实战指南:从 Issue 提交到 Pull Request 的完整协作流程

消息队列 【免费下载链接】nsq A realtime distributed messaging platform 项目地址&#xff1a; https://gitcode.com/gh_mirrors/ns/nsq 点击查看 免费下载 NSQ 是一个以 Go 编写的实时分布式消息平台&#xff0c;由 nsqd、nsqlookupd、nsqadmin 三个核心组件以及 nsq_to_f…

作者头像 李华
网站建设 2026/10/1 1:53:11

MAS 微软激活脚本:4 种激活方式,3 步跑起来

MAS 微软激活脚本&#xff1a;4 种激活方式&#xff0c;3 步跑起来 【免费下载链接】Microsoft-Activation-Scripts Open-source Windows and Office activator featuring HWID, Ohook, TSforge, and Online KMS activation methods, along with advanced troubleshooting. 项…

作者头像 李华
网站建设 2026/10/1 1:52:58

多模态情感分析:特征融合、训练调参与轻量化部署实践指南

简介&#xff1a;面向深度学习多模态情感分析方向的完整算法源码与说明包&#xff0c;适合计算机、数学、电子信息等专业学生用于课程设计、期末大作业或毕业设计&#xff0c;也适合对情感分析感兴趣的初学者开展实战演练与算法复现。资源共36个文件&#xff0c;核心为Python源…

作者头像 李华
网站建设 2026/10/1 1:52:52

自托管数据保全系统实操:内容寻址与完整性校验打造可验证备份

1. 项目背景与命名由来&#xff1a;为什么叫 Madeira1.1 名字背后的三层含义最开始给这个项目起名时&#xff0c;我翻遍了各种岛屿名、酒名、神话人物&#xff0c;最后停在“Madeira”上。原因很简单&#xff0c;这个词能同时表达三层意思&#xff1a;一是大西洋上那座被称为“…

作者头像 李华