尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Java Web选课系统课设工程:Servlet事务并发与权限设计实战
简介这是一份面向计算机专业毕业设计或课程设计的Java自助选课系统论文资料包针对高校传统选课方式耗时长、信息不透明、易出错等痛点完整给出了基于模型视图控制器模式、Java语言、MySQL数据库及浏览器/服务器架构的设计方案。压缩包仅含1个docx文档大小7.03MB文档正文覆盖绪论、关键技术简介、系统分析、系统设计、系统实现等核心章节并具体描述了用户管理、课程管理、课程推荐、选课管理、班级管理、教师信息管理及公告管理等功能模块的数据库设计与实现细节。读者可从中获得包括系统总体架构、概念结构与逻辑结构设计、IDEA开发环境下前后端交互方法以及功能测试结论在内的完整素材其中还包含各功能模块的源码实现思路适合正在开展同类课题研究或需要撰写毕业设计论文的学生参考借鉴。目前已有35人学习对理解自助选课系统的完整开发流程和提升论文规范性都有不错的参考价值。1. 自助选课系统一套能直接答辩的 Java Web 完整工程一到选课季教务公告栏贴满手写加选名单、学生守着浏览器刷到系统转圈这是不少高校每学期都要上演一次的固定节目。这套基于 Java 的自助选课系统解决的就是这个场景学生在线选课、退课、查成绩教师维护课程信息与录入成绩教务统一管理开课和最终选课名单论文和源码成套交付属于典型的“设计与实现”型课设工程。它适合两类人一类是 Java Web 课设或毕设还缺一个完整闭环的学生另一类是准备校招、想用 Servlet/JSP 把请求链路补扎实的在读同学。整个工程技术栈收敛没有花哨框架每个请求都走完 Servlet → Service → DAO → MySQL 全链路答辩被问到“底层发生了什么”也不会心虚。2. 技术栈与模块拆解为什么这套 Java 方案适合课设复现2.1 选型逻辑Servlet JSP MySQL 比 Spring 全家桶更可控这份源码用的是 Java Web 最经典的组合Servlet 做控制层、JSP 做视图层、MySQL 存数据中间夹一层 Service 和 DAO。拿到手的第一感觉是“朴素”没有 Spring Boot 那种一键启动的省心但恰恰是这份朴素最适合课设场景。原因很简单课设答辩老师最爱问的三件事请求怎么进来的、数据怎么存进去的、事务边界在哪里。Spring Boot 把这些都包装成了注解和自动配置学生答不上来底层而 Servlet/JSP 项目里web.xml里写着每一个 Servlet 映射Connection是手动DriverManager.getConnection()拿的事务是手动setAutoCommit(false)切的每一环都摆在明面上。对想补基础的人来说这种“笨”反而是最好的教材。对比维度Servlet JSP 课设工程Spring Boot 工程请求入口web.xml 显式映射肉眼可见DispatcherServlet 自动分发注解驱动数据库操作JDBC 手写 SQL连接自己管MyBatis/JPA 自动映射事务控制Connection 手动 commit/rollbackTransactional 声明式答辩容错度答不出底层会露怯被问自动配置原理容易翻车上手成本环境配好即可跑需要理解 starter 和自动装配如果你是准备面试我也建议先把 Servlet 这条链路吃透。很多 Java 面试题里考的 Session 机制、Filter 拦截、JDBC 批处理在这个项目里都是真实存在的代码不是在八股文里背来的概念。2.2 模块划分与角色权限学生、教师、教务三端的边界这套系统按角色拆成三个端用户表里用一个role字段区分身份而不是给每个角色建一张用户表。这个设计很值得学习课设级系统里学生和教师的字段差异其实只有工号/学号拆三张表反而让登录逻辑重复三份。角色role 值核心权限涉及模块学生0浏览课程、选课、退课、查成绩选课中心、我的课表教师1维护课程信息、录入成绩课程管理、成绩管理教务管理员2审核开课、查看选课名单、统计报表开课审核、选课统计权限控制是靠一个登录过滤器实现的所有请求先经过 Filter从 Session 里拿当前登录用户拿不到就重定向到登录页拿到了再比对role和请求路径的前缀比如/student/*只允许 role0 访问。这里有个细节值得注意JSP 页面本身也要做权限判断否则学生直接访问/admin/manage.jsp的 URL 就能绕过菜单看到管理界面。源码里是在页面顶部用c:if标签判断 Session 里的角色再渲染内容这种“后端接口拦截 前端按钮隐藏”的双层校验是课设答辩里能加分的点。模块之间通过 Service 层解耦比如学生选课调用的是SelectionService.selectCourse()教师管课程调用的是CourseService.saveOrUpdate()两者各用各的 DAO不交叉访问对方的表。这样拆的好处是后续加功能时不需要动老代码比如第 6 章要加的乐观锁字段只改SelectionService和course表其他模块完全不受影响。3. 核心功能落地建表 SQL、选课事务与退课边界3.1 数据库设计四张表加一个视图覆盖课设闭环整个系统只需要四张核心表用户表、课程表、选课记录表、公告表。选课记录表是核心中的核心它同时关联用户和课程是典型的中间表结构。下面这份 SQL 是源码里建表语句的简化版去掉了字符集和注释保留关键约束。-- 用户表学生、教师、管理员统一存储用 role 区分 CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(30) NOT NULL UNIQUE COMMENT 登录名, password VARCHAR(64) NOT NULL COMMENT 存 MD5 或加盐哈希, real_name VARCHAR(30) NOT NULL COMMENT 真实姓名, role TINYINT NOT NULL DEFAULT 0 COMMENT 0学生 1教师 2管理员, created_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 课程表balance 是剩余名额每次选课要扣减 CREATE TABLE course ( id INT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(50) NOT NULL, teacher_id INT NOT NULL COMMENT 任课教师关联 sys_user.id, credit DECIMAL(3,1) DEFAULT 2.0 COMMENT 学分, capacity INT NOT NULL COMMENT 课程总容量, balance INT NOT NULL COMMENT 剩余名额选课成功则减一, status TINYINT DEFAULT 1 COMMENT 1可选 0已关闭, FOREIGN KEY (teacher_id) REFERENCES sys_user(id) ); -- 选课记录表联合唯一索引保证同一学生不能重复选同一门课 CREATE TABLE course_selection ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, selected_time DATETIME DEFAULT CURRENT_TIMESTAMP, score DECIMAL(5,2) DEFAULT NULL COMMENT 成绩教师录入, UNIQUE KEY uk_student_course (student_id, course_id), FOREIGN KEY (student_id) REFERENCES sys_user(id), FOREIGN KEY (course_id) REFERENCES course(id) ); -- 公告表教务发布选课通知 CREATE TABLE notice ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, content TEXT, publish_time DATETIME DEFAULT CURRENT_TIMESTAMP );两个设计点要特意说一下。第一sys_user表用role字段区分身份而不是拆三张表因为学生和教师共用的字段占 90%拆表只会让登录逻辑重复三份。第二course_selection表上的联合唯一索引uk_student_course是在数据库层面挡住重复选课这比只在 Java 代码里if判断可靠得多——就算两个请求同时打到接口上数据库唯一索引也能让第二个插入直接报错这是第一道防线。balance字段是后面所有并发问题的焦点。如果你在源码里看到 SELECT 余量再 UPDATE 的写法那只是初版真正稳定版会改成条件更新这一点在第 4 章会详细展开。3.2 选课事务名额扣减与冲突检测的编码写法选课的核心逻辑是三步检查是否已选过、检查余量是否充足、扣减余量并插入记录。这三个操作必须包在同一个事务里否则会出现“记录插入成功但余量没扣”的脏数据。源码里SelectionService的选课方法核心骨架是这样的public boolean selectCourse(int studentId, int courseId) { Connection conn null; PreparedStatement ps null; ResultSet rs null; try { conn DBUtil.getConnection(); // 关闭自动提交手动控制事务边界 conn.setAutoCommit(false); // 第一步判断是否已经选过利用联合唯一索引兜底 String checkSql SELECT COUNT(*) FROM course_selection WHERE student_id? AND course_id?; ps conn.prepareStatement(checkSql); ps.setInt(1, studentId); ps.setInt(2, courseId); rs ps.executeQuery(); rs.next(); if (rs.getInt(1) 0) { conn.rollback(); return false; // 已选过直接回滚 } // 第二步条件更新余量balance 0 保证不超选 String deductSql UPDATE course SET balance balance - 1 WHERE id ? AND balance 0 AND status 1; ps conn.prepareStatement(deductSql); ps.setInt(1, courseId); int rows ps.executeUpdate(); if (rows 0) { conn.rollback(); return false; // 余量不足或课程已关闭 } // 第三步插入选课记录 String insertSql INSERT INTO course_selection (student_id, course_id) VALUES (?, ?); ps conn.prepareStatement(insertSql); ps.setInt(1, studentId); ps.setInt(2, courseId); ps.executeUpdate(); conn.commit(); // 三步全部成功才提交 return true; } catch (Exception e) { try { if (conn ! null) conn.rollback(); } catch (SQLException ex) {} e.printStackTrace(); return false; } finally { DBUtil.close(rs, ps, conn); } }关键在第二步那条UPDATE语句SET balance balance - 1 WHERE id ? AND balance 0。这是个原子操作数据库在更新时会锁住这一行余量检查从“先 SELECT 再 UPDATE”的两步变成了“UPDATE 自带条件”的一步从根上掐掉了并发超选的可能。rows 0表示影响行数为零也就是余量已经被别的请求扣光了这时直接回滚不让后面的插入执行。两个参数值得留意status 1条件用来防止向已关闭的课程选课先查重复再插记录的顺序不能反过来——如果先插记录再查重联合唯一索引的报错会让事务回滚得更粗暴。这种写法虽然多一次查询但代码逻辑更清晰答辩时能讲明白“数据库约束兜底 业务代码前置判断”的双保险思路。3.3 退课与补选事务边界怎么切退课是选课的逆操作但比选课多一个容易忽略的点补回名额的时机。源码里的退课逻辑是先删除选课记录再给课程余量加一同样在事务里执行。如果先加名额再删记录万一删除失败会出现“名额空了但记录还在”的幽灵选课。public boolean dropCourse(int studentId, int courseId) { Connection conn null; PreparedStatement ps null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 删除选课记录连带删除成绩所以退课后成绩自动清空 String deleteSql DELETE FROM course_selection WHERE student_id? AND course_id?; ps conn.prepareStatement(deleteSql); ps.setInt(1, studentId); ps.setInt(2, courseId); int rows ps.executeUpdate(); if (rows 0) { conn.rollback(); return false; // 没选过这门课幂等返回 } // 回补名额 String restoreSql UPDATE course SET balance balance 1 WHERE id ?; ps conn.prepareStatement(restoreSql); ps.setInt(1, courseId); ps.executeUpdate(); conn.commit(); return true; } catch (Exception e) { try { if (conn ! null) conn.rollback(); } catch (SQLException ex) {} e.printStackTrace(); return false; } finally { DBUtil.close(null, ps, conn); } }退课的“幂等”是个常被忽略的细节。前端如果按钮被双击后端会收到两个删除请求第二个请求删除的rows 0走回滚分支返回 false不会把余量加两次。这种边界在真实项目里非常重要但很多课设源码根本没处理——你可以在答辩时主动提这一点属于加分项。事务边界还有一个设计取舍整个方法用同一个Connection并且手动setAutoCommit(false)而不是每步操作单独开一个连接。如果每个 DAO 方法各拿各的连接三个步骤就变成了三个独立事务中间任何一步失败都无法整体回滚。源码里这类方法都写在 Service 层而不是 DAO 层因为只有 Service 层能控制连接的生命周期。这也是面试里常问的“事务应该放在哪一层”的答案。4. 避坑记录从乱码到并发超选的五个现场4.1 现象JSP 页面中文全部变成问号选课列表页打开课程名“计算机网络”显示成“???? 网络”。原因Tomcat 8.5 默认请求和响应的编码不是 UTF-8JSP 页面顶部声明的pageEncoding和数据库连接串里的characterEncoding三者不一致有一处断链就乱码。解决全链路统一 UTF-8。数据库连接串加?useUnicodetruecharacterEncodingUTF-8JSP 顶部写% page languagejava contentTypetext/html; charsetUTF-8 pageEncodingUTF-8%然后在web.xml里加一个编码过滤器强制所有请求使用 UTF-8。加上之后从浏览器到 Servlet 到数据库一路 UTF-8 到底问题消失。4.2 现象启动 Tomcat 后访问页面报 ClassNotFoundException: com.mysql.jdbc.Driver原因很蠢但常见mysql-connector-java-xxx.jar只被加到了项目的 Build Path 里没有复制到WEB-INF/lib目录下。Eclipse 和 IDEA 对这种情况的表现还不一样IDEA 会直接编译通过但运行时找不到驱动类。解决把 MySQL 驱动 jar 拷进src/main/webapp/WEB-INF/lib/或项目对应的web/WEB-INF/lib/下右键 Add as Library然后重启 Tomcat。检查方式很直白展开WEB-INF/lib目录能看到驱动 jar 才算数。4.3 现象五十个学生同时选同一门课余量从 20 直接变成负数这是并发超选问题也是课设答辩最容易翻车的点。原因在前面提过初版代码是“先 SELECT balance判断大于 0再 UPDATE balance”两个请求同时读到余量 1都判断可以选最后都执行 UPDATE余量就变成了 -1。解决把查余量再更新的两步合并成一条带条件的 UPDATE 语句UPDATE course SET balance balance - 1 WHERE id ? AND balance 0靠数据库行锁保证同一时刻只有一个请求能改成功。改完之后余量永远不会低于零失败的一方会收到rows 0。这个改法是这份源码里最值得抄走的一段。4.4 现象改了 Java 代码重启 Tomcat 后页面还是旧功能改完代码重新部署结果浏览器里跑的还是上一个版本。原因IDEA 里默认的部署方式是 exploded但 Tomcat 工作目录和你的项目输出目录不一致或者 Tomcat 实例没有真正 clean旧的 class 文件还在缓存里。解决IDEA 里双击 Tomcat 配置Deployment 页签选择 exploded 部署然后在 Server 页签勾选 “On frame deactivation: Update resources”接着手动删掉 Tomcat 的 work 目录再重启。这一套做完改代码后的热更新才真正生效。这属于部署层面的玄学问题遇到一次之后就老实了。4.5 现象删除一门课程时 SQL 报错提示外键约束失败教务要在开课前删掉一门误建的课程DELETE FROM course WHERE id13直接抛外键异常。原因course_selection表里有学生选了这门课外键约束不允许删除被引用的父表记录。解决先删选课记录再删课程包在事务里执行。DELETE FROM course_selection WHERE course_id13; DELETE FROM course WHERE id13;。如果要做通用处理可以在 Java 代码里写成事务方法先查选课记录数量再决定删除顺序。5. 把源码跑起来环境版本搭配、部署顺序与验证清单5.1 版本搭配JDK 1.8 Tomcat 8.5 MySQL 5.7 最稳课设级别的 Java Web 工程版本搭配直接决定你今天是写代码还是配环境。这套源码按 JDK 1.8 写的不建议升级到 JDK 17 或 21原因很现实Tomcat 8.5 对高版本 JDK 的兼容性需要额外调参数而课设代码基本用不到新特性折腾半天不如老老实实装 1.8。组件推荐版本说明JDK1.8即 Java 8与 Servlet/JSP 代码完全兼容IDE 提示最少Tomcat8.5.x支持 Servlet 3.1匹配大多数课设代码MySQL5.7比 8.0 少踩密码认证插件的坑IDEEclipse 或 IDEA都可以IDEA 需配置 Tomcat 集成MySQL 8.0 不是不能用但它默认的caching_sha2_password认证方式和老版驱动不兼容如果源码里用的驱动版本较旧连接时会报认证失败。如果你只有 8.0 环境在连接串里加allowPublicKeyRetrievaltrueuseSSLfalse能救回来但省事起见5.7 是这套工程的最优解。5.2 部署顺序从导入工程到第一次启动的完整命令拿到源码包之后别急着打开 IDE先按顺序把环境铺好。第一步建数据库源码包里通常会附一个init.sql或db.sql里面是建库建表语句和几条初始数据在命令行执行mysql -u root -p init.sql注意先看一眼init.sql里有没有CREATE DATABASE语句没有的话要手动建库mysql -u root -p -e CREATE DATABASE course_select DEFAULT CHARACTER SET utf8;第二步是改数据库连接配置。源码里的DBUtil.java或db.properties文件写着连接串、用户名、密码把密码改成你自己的。这里最容易踩坑的是连接串里的serverTimezone参数MySQL 5.7 不加这个参数会报时区错误标准写法是String url jdbc:mysql://localhost:3306/course_select?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/Shanghai;第三步是部署到 Tomcat。用 IDEA 的话打开工程后配置 TomcatRun/Debug Configurations → Tomcat Server → LocalDeployment 页签点加号选 Artifact选择 war exploded 形式。然后点启动控制台出现Server startup in xxx ms就算成功。5.3 验证清单三个角色各走一遍主流程环境起来不代表系统能用建议按下面这张清单把三个角色都走一遍每项验证通过再进下一步。角色测试操作预期结果学生选用一门有余量的课选课成功个人课表出现该课程课程余量减一学生重复选同一门课提示不可重复选课余量不变学生退掉已选课程退课成功课程余量回补一教师新增一门课程课程列表出现新课程默认状态下学生可见可选教师录入和修改成绩学生端可查看到成绩修改后实时生效教务关闭一门课程学生端该课程状态变为不可选教务查看选课统计各课程已选人数与选课记录表一致走完这轮验证基本可以确认系统没有功能性问题。如果某一项不通过对照第 4 章的避坑记录排查绝大多数问题都出在编码、驱动和事务边界这三个地方。6. 进阶技巧给余额字段加版本号彻底治掉并发超选前面第 4 章讲的“条件 UPDATE”能挡住超选但还不够优雅。条件更新靠的是balance 0这个条件它只能保证余额不为负却没法区分“两人同时选课谁先谁后”。如果你希望选课记录带上先后顺序或者后续要做抢课排队更通用的做法是加一个版本号字段这就是乐观锁。给course表加一列version INT DEFAULT 0每次更新余额时同时把版本号加一并在 WHERE 里带上旧版本号UPDATE course SET balance balance - 1, version version 1 WHERE id ? AND balance 0 AND version ?Java 代码里先查出当前版本号再带上这个版本号执行更新。如果影响行数是零说明版本号已经被别人改过这次选课就得重试或者直接提示“课程已被抢完”。和条件更新相比版本号把“是否可以更新”的判断从业务值余额变成了独立字段将来加任何需要并发控制的业务字段都能复用同一种思路。另一个值得做的增强是批量导入课程。教务每学期要录入上百门课手写 INSERT 会疯掉。源码里如果只支持单条新增你可以加一个批量导入方法用 PreparedStatement 的批处理模式一次性提交五十条课程数据String sql INSERT INTO course (course_name, teacher_id, credit, capacity, balance, status) VALUES (?, ?, ?, ?, ?, 1); PreparedStatement ps conn.prepareStatement(sql); for (Course c : courseList) { ps.setString(1, c.getCourseName()); ps.setInt(2, c.getTeacherId()); ps.setBigDecimal(3, c.getCredit()); ps.setInt(4, c.getCapacity()); ps.setInt(5, c.getCapacity()); // 初始余额等于容量 ps.addBatch(); } ps.executeBatch();这样做的额外收益是只用一次网络往返比循环执行单条 INSERT 快一个量级。如果数据量大还可以每五十条executeBatch()一次配合手动事务控制中途出错能整批回滚。这套工程我前后拆过两遍第一遍照着源码跑通第二遍才真正看懂余额字段和事务边界的配合。从那以后我写每一个带名额扣减的功能都会先问自己一句这个字段在并发下会不会被两个请求同时改问完这一句能省下后面一整周的返工。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

SQL COUNT函数全解析:从NULL陷阱到慢查询性能优化

SQL COUNT函数全解析:从NULL陷阱到慢查询性能优化

在日常开发和数据库运维里,COUNT 可能是我们写过最多次的 SQL 函数之一,但越是常见的函数,越容易在细节上翻车。不管是统计报表少算了几行,还是大表 COUNT 慢到把应用拖垮,根子往往不是 SQL 写错,而是对这个…

📅 2026/10/8 20:14:47
如何区分著、编著、编、主编?一文讲透出版署名规则

如何区分著、编著、编、主编?一文讲透出版署名规则

1. 这四个词,到底在争什么先说个我常碰见的场景。有朋友拿着自己新出版的书跑来问我:“我这书封面写着‘某某著’,同事的书写的是‘某某主编’,是不是我这本书更厉害?”还有人评职称前抱着一摞书来咨询:“我…

📅 2026/10/8 20:14:47
OpenClaw报错AttributeError: ‘Claw‘ object has no attribute ‘calibrate‘的排查与修复

OpenClaw报错AttributeError: ‘Claw‘ object has no attribute ‘calibrate‘的排查与修复

执行OpenClaw测试脚本时,如果你也碰到这条AttributeError: Claw object has no attribute calibrate,先别急着怀疑人生。这几乎是所有从零上手OpenClaw做自动化的人都会撞上的第一面墙——报错信息本身很直白,就是告诉你Claw这个对象上没有ca…

📅 2026/10/8 20:14:47
MORE NEWS

更多资讯

📰

Hugging Face与魔搭:开源大模型落地的双操作系统

1. 项目概述:为什么今天必须搞懂开源大模型生态的“双核驱动”如果你最近三个月翻过技术社区、刷过AI资讯、甚至只是在招聘网站上扫过几眼算法岗JD,大概率已经撞见过这两个名字:Hugging Face和魔搭(ModelScope)。它们不…

📰

决策模型新选择:NeoHorse-Jev-4B本地部署实战

1. Jev 走红背后的真相:数据系统真正缺的不是大模型,而是"会做决定的小模型"先抛一个我在社区群里看到很多人讨论过的现象:大家提到 Jev,第一反应是"又一个推理很强的大模型",但真正动手用过的朋友…

📰

从RAG到Agent:联网搜索与工具调用式搜索的工程实践

1. 从搜索框到 Agent 的演进逻辑 1.1 为什么传统搜索框模式走到了瓶颈 做过 Chatbot 的人都有一个共同体会:用户问“今天有什么值得关注的科技新闻”,如果机器人只能从训练数据里翻答案,那它给出的内容大概率是几个月前的旧闻。这就是纯生成…

📰

多模态情感分析大作业实战:从Jupyter到可复现模型全流程

简介:本资源为基于Jupyter与Python实现的多模态情感分析模型完整项目包,面向计算机、人工智能、自动化等专业的学生与教师,可用于期末课程设计、课程大作业或毕业设计,也适合希望入门多模态学习的开发者参考。压缩包共约2000个文件…

📰

基于机器学习的Web日志异常检测工具:配置、实战与避坑指南

简介:这是一套面向安全运维与日志分析学习者的命令行Web日志审计工具,基于Python实现,将日志统计、终端可视化与机器学习恶意请求识别整合在一起,适合具备Python基础、希望上手日志审计与异常检测实战的开发者。资源包共63个文件&…

📰

LangGraph.js+Next.js构建可解释AI求职智能体

1. 这不是又一个“AI简历生成器”,而是一套能真正下地干活的智能体工作流最近帮三位应届生朋友优化求职流程,发现一个扎心事实:他们花8小时调格式、改措辞、投50份简历,结果打开邮箱——已读不回率92%。不是能力不行,是…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬