简介:这份JavaWeb实训资料包面向高校计算机相关专业学生与JavaWeb初学者,围绕学生选课系统这一典型课程设计场景,提供从需求分析到代码落地的完整参考。系统按角色划分功能:学生可注册登录、浏览课程、选课退课并查询已选结果;教师可发布与删除课程、查看选课情况,涵盖注册登录、学生选课、选课结果浏览、教师浏览结果、教师添加课程五大模块。包内共111个文件,约20.84MB,包含java源码、jsp页面、class编译文件、css与scss样式、sql建库脚本,以及实训报告docx与答辩pptx,另有jar依赖和字体资源,结构完整便于对照复现。目前已有4575人学习下载,适合需要完成课程实训、撰写报告并准备答辩的读者,可据此快速理解Servlet与DAO分层写法、数据库表设计及前后端交互流程,也可作为二次开发与排错参考。
1. 从零交付一套 JavaWeb 学生选课系统:实训报告、答辩 PPT、源代码与 SQL 文件怎么一次做齐
很多同学做 JavaWeb 实训,卡住的地方从来不是“不会写 Servlet”,而是四件事凑不齐:源代码能跑、SQL 文件能导入、实训报告有内容、答辩 PPT 讲得清。标题里这套「学生选课系统(实训报告+答辩ppt+源代码+sql文件)」本质是一个完整交付包,核心是选课业务,外壳是课程设计验收。它适合正在做 JavaWeb 课程设计、毕业设计开题、或者想拿一个完整案例练手 MySQL 增删改查和 JSP/Servlet 的人。我见过太多人代码写了一半,报告没素材,PPT 只能贴截图,最后答辩被问“你的选课冲突怎么判的”直接卡壳。这篇就按真实交付顺序,把技术选型、库表设计、核心代码、报告与 PPT 的写法、以及最容易翻车的地方一次讲透,让你照着能复现一套能过验收的系统。
2. 技术选型与工程骨架:为什么这套选课系统用 JSP+Servlet+MySQL 最稳
2.1 选型理由:别一上来就 SpringBoot
标题写的是 JavaWeb 实训,不是企业级微服务。很多热搜词里出现「javaweb项目完整案例mysql」「idea运行javaweb项目配置」,说明大家真正需要的是一个能在 IDEA 里直接跑起来、依赖少、结构清晰的案例。这个场景下,JSP+Servlet+JDBC+MySQL 是最稳的组合:Tomcat 一挂就能跑,不需要 Maven 拉一堆依赖,答辩时老师问“请求怎么走的”你能从浏览器一路讲到 DAO。
如果你非要用 SpringBoot,也不是不行,但实训报告里要解释的东西会翻倍,而且很多学校机房环境老,JDK 版本低,SpringBoot 3.x 直接跑不起来。常见做法是:主体用 Servlet,把 JDBC 工具类抽出来,事务手动控制。这样既体现分层思想,又不至于被框架黑匣子挡住排查路径。
工程目录我一般这样组织,IDE 用 IDEA,构建用普通 Web 项目(非 Maven 也行,Maven 更规范):
StudentCourseSys/ ├── src/ │ ├── com/scs/entity/ # 实体类:Student、Course、Teacher、SC │ ├── com/scs/dao/ # 数据访问:StudentDao、CourseDao、SCDao │ ├── com/scs/service/ # 业务:CourseService、SCService │ ├── com/scs/servlet/ # 控制:LoginServlet、CourseServlet │ └── com/scs/util/ # DBUtil、PageUtil ├── web/ │ ├── WEB-INF/web.xml │ ├── css/ js/ │ └── *.jsp # login.jsp、course_list.jsp └── sql/scs.sql # 建库建表+初始数据2.2 数据库设计:四张表撑起整个选课逻辑
选课系统的核心是「学生」和「课程」的多对多关系,中间必须有一张选课记录表。热搜里「如何用sql文件」「javaweb项目完整案例mysql」指向的就是这块。下面是我常用的建表脚本,直接存成scs.sql,用 Navicat 或命令行source导入即可。
-- 创建数据库,字符集用 utf8mb4,避免中文乱码 CREATE DATABASE IF NOT EXISTS scs DEFAULT CHARSET utf8mb4; USE scs; -- 学生表 CREATE TABLE student ( sno VARCHAR(20) PRIMARY KEY, -- 学号 sname VARCHAR(50) NOT NULL, -- 姓名 password VARCHAR(50) NOT NULL, -- 密码(实训可明文,生产必须加密) major VARCHAR(50) -- 专业 ); -- 课程表,capacity 是容量,用于判断是否选满 CREATE TABLE course ( cno VARCHAR(20) PRIMARY KEY, -- 课程号 cname VARCHAR(50) NOT NULL, -- 课程名 credit DECIMAL(3,1), -- 学分 teacher VARCHAR(50), -- 任课教师 capacity INT DEFAULT 50, -- 容量上限 selected INT DEFAULT 0 -- 已选人数 ); -- 选课记录表,联合主键防止重复选课 CREATE TABLE sc ( sno VARCHAR(20), cno VARCHAR(20), score DECIMAL(5,1) DEFAULT NULL, -- 成绩,选课时为空 PRIMARY KEY (sno, cno), FOREIGN KEY (sno) REFERENCES student(sno), FOREIGN KEY (cno) REFERENCES course(cno) ); -- 初始数据 INSERT INTO student VALUES ('2021001','张三','123456','计算机'); INSERT INTO course VALUES ('C001','数据结构',4.0,'李老师',50,0); INSERT INTO course VALUES ('C002','操作系统',3.5,'王老师',40,0);参数说明:capacity和selected两个字段是选课冲突判断的关键,每次选课成功要selected+1,退课要-1,这两个操作必须和插入sc记录在同一个事务里,否则会出现“人数加了但记录没插进去”的脏数据。sc表用(sno,cno)联合主键,从数据库层面挡住重复选课,比在 Java 里查一遍再插更可靠。
2.3 IDEA 里跑通第一个页面:环境配置清单
热搜「idea运行javaweb项目配置」是高频卡点。按这个顺序配,基本不会翻车:
- File → Project Structure → Modules,确认 Web 模块的
web.xml路径指向web/WEB-INF/web.xml。 - Artifacts 里添加 Web Application Exploded,输出目录默认即可。
- Run → Edit Configurations,新增 Tomcat Local,Deployment 里加上刚才的 Artifact,Application context 设成
/scs。 - 把 MySQL 驱动
mysql-connector-java-8.x.jar丢进web/WEB-INF/lib,并在 Project Structure → Libraries 里引入。
配好后访问http://localhost:8080/scs/login.jsp能看到页面,说明骨架通了。这一步不通,后面全是白搭,所以先跑通再写业务。
3. 核心业务代码:登录、选课、退课与冲突判断怎么写
3.1 数据库工具类与连接管理
所有 DAO 都依赖一个可靠的连接工具。实训项目不建议上连接池(Druid 也行,但多一个依赖),手写一个DBUtil足够,重点是记得关资源。
public class DBUtil { private static final String URL = "jdbc:mysql://localhost:3306/scs?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"; private static final String USER = "root"; private static final String PWD = "你的密码"; static { try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { throw new RuntimeException("MySQL 驱动未加载", e); } } public static Connection getConn() throws SQLException { return DriverManager.getConnection(URL, USER, PWD); } // 统一关闭,顺序:ResultSet -> Statement -> Connection public static void close(Connection c, Statement s, ResultSet r) { try { if (r != null) r.close(); } catch (SQLException ignored) {} try { if (s != null) s.close(); } catch (SQLException ignored) {} try { if (c != null) c.close(); } catch (SQLException ignored) {} } }逻辑说明:serverTimezone不写会在 MySQL 8 上报时区错误,这是热搜「sql访问许可验证文件出错」之外另一个高频连接坑。characterEncoding=utf8保证中文课程名不乱码。关闭顺序不能反,先关结果集再关连接,否则可能抛异常。
3.2 选课事务:容量判断与重复选课一起解决
选课是整个系统最容易出并发问题的地方。两个学生同时选最后一门课,如果先查再插,可能都查到“没满”,结果超选。正确做法是把判断和更新放进一个事务,并用行锁。
public boolean selectCourse(String sno, String cno) { Connection conn = null; PreparedStatement ps1 = null, ps2 = null, ps3 = null; ResultSet rs = null; try { conn = DBUtil.getConn(); conn.setAutoCommit(false); // 开启事务 // 1. 锁定该课程行,查容量 ps1 = conn.prepareStatement( "SELECT capacity, selected FROM course WHERE cno=? FOR UPDATE"); ps1.setString(1, cno); rs = ps1.executeQuery(); if (!rs.next()) { conn.rollback(); return false; } int capacity = rs.getInt("capacity"); int selected = rs.getInt("selected"); if (selected >= capacity) { conn.rollback(); return false; } // 已满 // 2. 插入选课记录,联合主键会挡住重复选课 ps2 = conn.prepareStatement("INSERT INTO sc(sno,cno) VALUES(?,?)"); ps2.setString(1, sno); ps2.setString(2, cno); ps2.executeUpdate(); // 3. 已选人数 +1 ps3 = conn.prepareStatement( "UPDATE course SET selected=selected+1 WHERE cno=?"); ps3.setString(1, cno); ps3.executeUpdate(); conn.commit(); return true; } catch (SQLIntegrityConstraintViolationException dup) { // 联合主键冲突 = 重复选课 try { if (conn != null) conn.rollback(); } catch (SQLException ignored) {} return false; } catch (SQLException e) { try { if (conn != null) conn.rollback(); } catch (SQLException ignored) {} e.printStackTrace(); return false; } finally { DBUtil.close(conn, ps1, rs); try { if (ps2 != null) ps2.close(); } catch (SQLException ignored) {} try { if (ps3 != null) ps3.close(); } catch (SQLException ignored) {} } }参数与逻辑说明:FOR UPDATE是关键,它给课程行加排他锁,第二个并发请求会等第一个提交后再读,避免超选。SQLIntegrityConstraintViolationException专门捕获联合主键冲突,用来识别重复选课,比先SELECT再判断更省一次查询。退课逻辑对称:删sc记录、selected-1,同样放事务里。注意setAutoCommit(false)后任何提前return都必须先rollback,否则连接归还时事务还挂着,这是新手最常见的翻车点。
3.3 分页查询课程列表
课程列表要分页,否则数据一多页面卡。用LIMIT实现,前端传pageNo。
public List<Course> listByPage(int pageNo, int pageSize) { List<Course> list = new ArrayList<>(); String sql = "SELECT * FROM course ORDER BY cno LIMIT ?, ?"; try (Connection conn = DBUtil.getConn(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setInt(1, (pageNo - 1) * pageSize); // 起始偏移 ps.setInt(2, pageSize); // 每页条数 try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { Course c = new Course(); c.setCno(rs.getString("cno")); c.setCname(rs.getString("cname")); c.setCredit(rs.getDouble("credit")); c.setTeacher(rs.getString("teacher")); c.setCapacity(rs.getInt("capacity")); c.setSelected(rs.getInt("selected")); list.add(c); } } } catch (SQLException e) { e.printStackTrace(); } return list; }参数说明:pageNo从 1 开始,偏移量是(pageNo-1)*pageSize,这是分页公式,写错就会出现第一页重复或漏数据。pageSize一般设 10,前端 JSP 用<c:forEach>渲染,页码用总记录数除以pageSize向上取整算。这里用了 try-with-resources,比手动 close 更安全,但注意它只关Connection和Statement,ResultSet也一起关了,没问题。
4. 实训报告与答辩 PPT:怎么把代码变成能交差的文档
4.1 实训报告的结构与素材来源
热搜里「javaweb实训」「实训报告」说明文档和代码一样重要。报告不要写成代码注释的堆砌,按这个骨架写,每部分都有素材来源:
| 报告章节 | 写什么 | 素材从哪来 |
|---|---|---|
| 需求分析 | 学生选课、退课、查成绩三类用例 | 画用例图,列功能点 |
| 系统设计 | 分层架构、E-R 图、表结构 | 第 2 章的建表脚本和目录结构 |
| 详细实现 | 关键代码+流程图 | 第 3 章的事务、分页代码 |
| 测试 | 测试用例表、截图 | 实际跑一遍,截图登录/选课/满员提示 |
| 总结 | 遇到的问题与解决 | 第 5 章的踩坑记录 |
E-R 图用 Visio 或 draw.io 画,三个实体(学生、课程、选课记录)加两条关系线即可。测试章节最有说服力的是“选满后提示”和“重复选课提示”两张截图,答辩老师一看就知道你真跑过。
4.2 答辩 PPT 的 8 页模板
PPT 别超过 10 页,讲 8 分钟刚好。我常用的结构:
- 封面:题目、姓名、学号、指导老师。
- 选题背景:为什么做选课系统,一句话说清痛点。
- 技术栈:JSP+Servlet+MySQL,配一张架构图。
- 数据库设计:E-R 图 + 四张表说明。
- 核心功能演示:登录、选课、退课三张截图。
- 技术难点:事务与并发选课,讲
FOR UPDATE。 - 测试结果:正常流程 + 异常流程截图。
- 总结与不足:比如“未做成绩录入,后续可扩展”。
第 6 页是加分项,大部分同学 PPT 只贴界面,你能讲出“并发下怎么保证不超选”,老师印象直接拉满。演示时提前把 Tomcat 跑起来,别现场启动,机房环境说不准。
5. 避坑与排查:这套选课系统最容易翻车的 5 个地方
5.1 中文乱码:现象是课程名显示问号
现象:页面显示“?????”,数据库里其实是好的。原因:JSP 页面没声明编码,或者请求没设字符集。解决:JSP 顶部加<%@ page contentType="text/html;charset=UTF-8" %>,Servlet 里request.setCharacterEncoding("UTF-8")必须在第一次getParameter之前调用,晚一行都无效。数据库连接串带上characterEncoding=utf8,三处齐了才彻底不乱。
5.2 选课人数对不上:现象是 selected 和 sc 表记录数不一致
现象:课程显示已选 5 人,但sc表只有 4 条记录。原因:插入记录和更新人数没放同一事务,中间抛异常只回滚了一半。解决:按 3.2 的写法,两步操作包在setAutoCommit(false)和commit之间,任何异常都rollback。已经脏了的数据,用一条 SQL 校准:UPDATE course c SET selected=(SELECT COUNT(*) FROM sc WHERE cno=c.cno);。
5.3 驱动报错:现象是 ClassNotFound 或时区异常
现象:启动报com.mysql.cj.jdbc.Driver找不到,或The server time zone value错误。原因:驱动 jar 没放进WEB-INF/lib,或连接串缺serverTimezone。解决:确认 jar 在WEB-INF/lib且被 Artifact 打包;连接串补上serverTimezone=Asia/Shanghai。热搜里「sql访问许可验证文件出错」多半也是连接串参数或权限问题,检查用户名密码和useSSL=false。
5.4 重复选课没拦住:现象是同一学生同一课程出现两条记录
现象:连点两次选课按钮,sc表出现重复。原因:表没设联合主键,或前端没防重复提交。解决:sc表必须PRIMARY KEY(sno,cno),Java 层捕获主键冲突异常返回“已选过”;前端按钮点击后disabled,双保险。
5.5 部署后 404:现象是本地能跑,换机器就找不到页面
现象:访问报 404,日志无异常。原因:Artifact 没更新,或 context path 写错。解决:重新 Build Artifact,确认访问路径是http://ip:8080/应用名/页面,应用名和 Deployment 里的 Application context 一致。换机器时把sql文件重新导入,别指望数据库跟着走。
6. 让这套系统在答辩里更抗问:两个进阶技巧
第一个技巧是给选课加“时间冲突”判断,这是老师最爱追问的点。现在只判了容量和重复,如果两门课上课时间相同,理论上不该同时选。做法是在course表加course_time字段(如“周一1-2节”),选课时查该生已选课程的course_time,有交集就拒绝。SQL 用SELECT COUNT(*) FROM sc s JOIN course c ON s.cno=c.cno WHERE s.sno=? AND c.course_time=?,命中就返回冲突。这个扩展不用改架构,加一个字段和一段判断,报告里能多写一节“冲突检测”,PPT 多一页,性价比极高。
第二个技巧是准备一份“答辩问答预案”,把可能被问的问题和答案写成一页纸背下来。高频问题就那几个:为什么用 Servlet 不用框架(答:实训要求理解底层请求流程)、事务怎么保证一致性(答:FOR UPDATE加手动提交回滚)、数据量大了怎么办(答:加索引、上连接池、分页)。我当年答辩被问“如果两个人同时选最后一门课会怎样”,因为提前想过,直接画了锁的时序图,老师点头就过了。血泪经验是:别背代码,背“为什么这么写”。
最后说个习惯,我做完任何课程设计都会把sql文件、源码、报告、PPT 放一个文件夹,命名带日期,再导出一份 PDF 版报告。因为答辩现场 U 盘不认、Office 版本不兼容这种事太常见,PDF 是后悔药。这套学生选课系统本身不难,难的是交付完整、讲得清楚、经得起追问。把事务和冲突这两块吃透,你就不只是交了个作业,而是真懂了 JavaWeb 的请求-业务-数据这条链路。希望帮到你。
本文还有配套的精品资源,点击获取