新闻详情

新闻详情

首页 / 资讯中心 / 详情

Java学生选课系统从源码到跑通:事务、并发与Tomcat避坑指南

发布时间:2026/10/1 13:41:23来源:尧图网络
Java学生选课系统从源码到跑通:事务、并发与Tomcat避坑指南
简介这套Java Swing结合MySQL实现的学生选课系统项目源码适合Java初学者、课程设计或毕业设计参考覆盖登录认证、课程管理、选课退课、密码修改等典型功能模块。压缩包共168个文件包含23个Java源文件与63个编译后的class文件、24个Swing界面Form设计文件、41张界面预览PNG图片、数据库SQL脚本及db备份文件另有doc/docx说明文档和操作演示avi视频整体大小约58.87MB。资源完整度高从环境配置、数据库导入到功能演示均有配套材料可帮助读者快速跑通项目并理解Swing桌面应用与MySQL交互的开发流程。该资源已有861人学习下载适合需要借鉴选课系统业务逻辑、界面布局或数据库设计的人群。1. 从「Java毕业设计之学生选课系统」到能跑的第一行代码先看项目结构再动手拿到「Java毕业设计之实现的学生选课系统项目源码.zip」这类压缩包第一反应应该是把它跑起来而不是先去看哪段代码写得漂亮。能跑通才有资格谈改代码、加功能、应付答辩。学生选课系统是Java课设里出现频率最高的题材之一因为它覆盖了登录鉴权、增删改查、多表关联、事务控制这些Java后台开发的基础面也是面试里“你说说你做过什么”最常被追问的项目。这篇笔记不打算复述教科书上那张ER图而是从解压到出数据的完整路径讲清怎么让这套源码真正属于你。2. 拆开学生选课系统源码包先看结构判断技术栈比猜启动方式靠谱2.1 拿到 zip 不乱解压先用命令确认架构学员把压缩包发过来最常见的第一句是“老师我这个怎么启动不了”。我一般先不回话让他把解压后的根目录结构截图发过来。因为「Java毕业设计」这个标签太宽泛了同为选课系统可能是JSP Servlet Tomcat的老古董也可能是Spring Boot Vue的前后端分离项目启动方式完全不同。解压动作虽然简单但也值得用命令行做方便后面清理路径问题。在Windows下打开PowerShell或CMD进入压缩包所在目录后执行unzip 学生选课系统.zip -d 选课系统 cd 选课系统 dir /b-d参数指定解压目标目录避免默认把所有文件散在当前目录里。解压后第一件事不是打开IDE而是看根目录里有没有pom.xml、build.gradle、WEB-INF、web.xml、src/main/java这些标志性文件。pom.xml存在说明是Maven工程大概率是Spring Boot或SSM看到WEB-INF和.jsp文件则是传统Java Web项目得靠Tomcat才能跑。说实话这里有个实战里的血泪经验不要一上来就用IDEA直接开整个文件夹。IDEA的自动索引容易把老项目的.classpath、.project文件识别错导致左侧目录结构一团乱。我的习惯是先用dir /b或find . -maxdepth 2 -type d在命令行里瞄一眼结构心里有数后再用IDEA以Maven或普通项目方式导入。这个过程花不了两分钟但能省掉后面“包路径不对”“web.xml没加载”的玄学问题。2.2 两种主流技术栈的启动参数对照Spring Boot 和 JSP Servlet判断完技术栈启动方式就分两岔路了。我把近三年收到的学生选课系统源码做过统计大约七成是Spring Boot MyBatis Vue或Thymeleaf三成还在用JSP Servlet JDBC。两种的启动命令和配置位置差异很大分开说。如果是Spring Boot项目先改数据库连接。配置文件在src/main/resources/application.yml或application.properties核心是这几行spring.datasource.urljdbc:mysql://localhost:3306/course_selection?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai spring.datasource.usernameroot spring.datasource.password123456 spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver server.port8080改完账号密码后启动方式有两种直接用IDEA跑*Application.java里的main方法或者用Maven命令mvn spring-boot:run启动日志里看到Tomcat started on port(s): 8080就说明成了。浏览器访问http://localhost:8080能跳登录页就继续往下做。如果是JSP Servlet项目没有内嵌Tomcat需要把项目打成war包丢进Tomcat的webapps目录。我不建议用IDEA的Artifact打包这地方太容易出错。直接用Maven插件更稳mvn clean package -DskipTests把生成的target/xxx.war复制到Tomcat的webapps目录然后到bin目录执行启动脚本。Windows下是这样cd C:\apache-tomcat-9.0.xx\bin catalina.bat run注意这里的命名空间问题Tomcat 10以上把javax.servlet改成了jakarta.servlet如果你的源码是2019年前后的老代码用Tomcat 9而不是Tomcat 10。这件事在第4章展开讲这里先记住结论。2.3 数据库初始化不是导入就完事字符集、时区、初始化数据源码包基本都会带一个.sql脚本但很多小白直接双击导入然后登录时报“Unknown column”或中文乱码就以为是代码坏了。实际上问题出在数据库初始化这一步没做干净。我的标准操作是先建库再指定字符集导入而不是让脚本自己决定mysql -uroot -p -e CREATE DATABASE course_selection DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; mysql -uroot -p course_selection db_course_selection.sql第一条命令强制把库的字符集设为utf8mb4第二条把SQL脚本导入到这个库里。字符集用utf8mb4而不是utf8是因为课程名里如果出现生僻字或Emojiutf8在MySQL 5.7以下会存不进去。导入成功不代表数据没问题还要顺手查三条关键数据管理员账号、测试学生账号、初始课程数。很多源码包把默认账号写在SQL脚本里但没人告诉你密码是MD5还是明文。这时候用这条命令看SELECT user_name, user_password FROM sys_user;看到的结果如果是32位十六进制字符串说明是MD5加密码登录时框架会自动加密比对如果直接是123456注意登录时可能要做加盐处理。这一步的意义在于别等到登录页报“用户名或密码错误”才发现自己连默认账号都不知道是什么。3. 把「选课」做成不翻车的事务核心表设计与选课接口实现3.1 三张表搞定学生选课系统索引、约束、计数器的设计很多课程设计的代码能跑但表结构经不起问。答辩老师最喜欢问的一句话是“你这两张表之间是什么关系外键为什么不建”你答不上来前面的辛苦就白费了。学生选课系统的核心表结构其实很标准三张表就能讲清楚全部业务。第一张是学生表第二张是课程表第三张是选课记录表。选课记录表是典型的多对多关联表一个学生可以选多门课一门课可以被多个学生选。建表SQL如下CREATE TABLE t_student ( student_id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL UNIQUE COMMENT 学号, student_name VARCHAR(32) NOT NULL, password VARCHAR(64) NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_course ( course_id INT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(64) NOT NULL, teacher_name VARCHAR(32) NOT NULL, class_time VARCHAR(32) NOT NULL COMMENT 如周一第3-4节, max_student INT NOT NULL DEFAULT 30 COMMENT 人数上限, selected_count INT NOT NULL DEFAULT 0 COMMENT 已选人数, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_class_time (class_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_selection ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_student_course (student_id, course_id), KEY idx_course_id (course_id), CONSTRAINT fk_selection_student FOREIGN KEY (student_id) REFERENCES t_student (student_id), CONSTRAINT fk_selection_course FOREIGN KEY (course_id) REFERENCES t_course (course_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有两个细节必须说透。第一t_selection表上加UNIQUE KEY uk_student_course (student_id, course_id)这一行是防守重复选课的底线。就算代码里漏了判断数据库层面也不会允许同一个学生插入两条相同课程的选课记录。第二t_course表里的selected_count是一个冗余计数器正常情况下它应该等于t_selection里该课程的行数但为什么还要单独存因为查人数上限时你不可能每次都用COUNT(*)扫全表这是一个典型的空间换时间设计。听懂这个点答辩时的“数据库设计你怎么考虑的”基本就过关了。3.2 选课接口逻辑事务、重复校验、时间冲突校验、人数上限有了表结构选课的业务逻辑就是面试里最常挂起的“那你讲讲数据一致性是怎么保证的”。很多课设代码喜欢在Controller里写一大坨if再丢一个INSERT进去这个做法能跑但漏洞百出。正确姿势是放到Service层用事务包起来。下面这段是我改课设时给的标准示范Service public class CourseService { Resource private CourseMapper courseMapper; Resource private SelectionMapper selectionMapper; Transactional(rollbackFor Exception.class) public boolean chooseCourse(Integer studentId, Integer courseId) { // 1. 课程必须存在 Course course courseMapper.selectById(courseId); if (course null) { throw new IllegalStateException(课程不存在请刷新列表); } // 2. 重复选课校验数据库唯一索引兜底 Long count selectionMapper.countByStudentAndCourse(studentId, courseId); if (count ! null count 0) { throw new IllegalStateException(你已经选过这门课别重复报名); } // 3. 人数上限校验已选人数 1 后仍不能超过上限 if (course.getSelectedCount() course.getMaxStudent()) { throw new IllegalStateException(手慢了课程名额已满); } // 4. 时间冲突校验遍历我已有的课表逐门对比上课时间 ListCourse myCourses selectionMapper.selectCoursesByStudentId(studentId); for (Course c : myCourses) { if (course.getClassTime().equals(c.getClassTime())) { throw new IllegalStateException(时间冲突 c.getCourseName() 也在 course.getClassTime() 上课); } } // 5. 正式选课插入选课记录同时课程已选人数加 1 selectionMapper.insert(studentId, courseId); courseMapper.increaseSelectedCount(courseId); return true; } }这段代码有四个关键点值得拆开讲。第一Transactional(rollbackFor Exception.class)写在Service方法上rollbackFor指定为Exception.class而不是默认的RuntimeException因为这里抛的是IllegalStateException属于运行时异常但显式声明是在告诉队友方法里任何一步失败前面插入的记录和更新的人数都要整体回滚。第二第3步的校验结束后真正执行更新的是这行courseMapper.increaseSelectedCount(courseId)对应SQL是UPDATE t_course SET selected_count selected_count 1 WHERE course_id #{courseId}这在单用户操作时没问题但并发场景下会超卖。第4步的时间冲突校验逻辑上要用equals比较字符串如果class_time为空或格式不统一这里会直接抛NullPointerException所以建表时给它加了NOT NULL约束。第5步的两条SQL必须放在同一个事务里否则就会出现“选课记录插成功了但人数没加”的数据不一致。3.3 为什么拒绝把业务塞进 Controller分层与答辩时的加分点学生选课系统的源码包如果质量不高最典型的表现是Controller里写完一切验证用户、查课程、查重叠、插入、更新全在HttpServletRequest里处理。这种代码跑起来没问题但答辩时老师只要问一句“你的业务层在哪里”场面就会很尴尬。标准的三层结构是Controller只做参数接收和结果返回Service做业务规则校验和事务控制Mapper只做SQL交互。选课接口被调用时数据流向是前端 → Controller → Service → Mapper → 数据库返回结果按原路返回。每层职责单一出了问题能顺着日志定位到具体层。这个分层在答辩时的直接好处有两个。第一老师问“你的选课人数上限是怎么防止超出的”你可以直接说“在Service层校验人数并用数据库唯一索引兜底”而不是答“我在Controller里写了if”。第二老师问“如果上课时间冲突怎么办”你可以带他看第4步的时间冲突遍历并解释为什么需要用equals而不是比较字符串。这两问两答基本能把分数从“及格”拉到“良好”。4. 常见问题与避坑本地跑通学生选课系统最常踩的五个坑4.1 Tomcat 10 启动后报 NoClassDefFoundError包名命名空间变了现象老一代JSP Servlet项目在Tomcat 10 / Tomcat 11里一启动页面报错控制台提示类似java.lang.NoClassDefFoundError: javax/servlet/ServletException或者ClassNotFoundException: javax.servlet.*。原因Tomcat 9及更早版本里Servlet API的包名是javax.servlet而从Tomcat 10开始官方把包名迁移到了jakarta.servlet。源码包里的旧代码还在import javax.servlet.http.*容器里根本找不到这个类。解决把部署的Tomcat换成9.0.x版本这是最省事的路线。如果坚持用Tomcat 10就必须把源码里所有javax.servlet手动改成jakarta.servlet。但老项目里涉及的地方不只是Java代码JSP页面里的指令也可能写死旧包名改起来容易漏。我处理这类项目时直接换Tomcat 9并给机器装了多个Tomcat版本以便切换。4.2 MySQL 连接报时区错误或乱码JDBC URL 缺参数现象Spring Boot项目启动时报The server time zone value Öйú±ê׼ʱ¼ä is unrecognized or represents more than one time zone或者页面上课程名显示成???一串问号。原因这条报错的中文提示实际是“中国标准时间”被错误编码后的乱码MySQL 8.0以上对JDBC连接增加了时区校验URL里没写serverTimezone就会炸。中文乱码则是URL里没写characterEncoding或者数据库本身建库时没指定utf8mb4。解决JDBC URL统一按下面这段配置写齐四个参数缺一不可jdbc:mysql://localhost:3306/course_selection?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseuseUnicodetrue和characterEncodingutf8解决中文编码serverTimezoneAsia/Shanghai解决时区useSSLfalse是MySQL 8对连接协议带来的额外要求。改完配置重启再在SQL客户端里执行SHOW VARIABLES LIKE character_set_database;确认库字符集是utf8mb4。4.3 端口被占用解决 8080 被占的三种方式现象点击启动后控制台报Web server failed to start. Port 8080 was already in use.Spring Boot直接退出。原因本机其他Java进程或服务占用了8080端口。Maven、Jenkins、微信开发者工具等都可能占这个口问题不在代码在系统环境。解决先找出占用进程然后按情况处理。Windows下执行netstat -ano | findstr :8080 taskkill /PID 4xxxx /F这里findstr输出的最后一列是进程IDtaskkill强制结束它。如果是自己启动过另一个Tomcat实例把它正常停掉即可。如果不想动系统里的程序更省事的是改Spring Boot的端口在application.properties里加一行server.port8081。再顺手检查一下你的后端CORS配置里是否写死允许来源http://localhost:8080改端口后前端页面可能因为跨域被拦。4.4 选课人数超卖并发选课导致 selected_count 超过上限现象用两个浏览器窗口同时登录两个学生账号掐着点抢同一门只余1个名额的课程两个人都提示“选课成功”。去数据库一查selected_count变成2比max_student还大。原因第3章的代码里先查selected_count再更新它查询和更新不是原子操作。两个请求同时读到同一个旧值各自加1后写回后写的覆盖先写的最终结果是2。这是典型的并发丢更新和淘宝秒杀超卖是同一个模型。解决最稳妥的办法是把“检查人数、更新人数”合并成一条带条件的SQL让数据库来保证原子性。改造后的Mapper方法是int decreaseStockOrIncreaseCount(Param(courseId) Integer courseId, Param(maxStudent) Integer maxStudent);对应SQLUPDATE t_course SET selected_count selected_count 1 WHERE course_id #{courseId} AND selected_count #{maxStudent}这条SQL执行后返回影响行数如果返回0说明选课人数已经到达上限代码里直接抛出“名额已满”异常。因为UPDATE在InnoDB下对同一行记录加锁第2个并发请求会等待第1个请求提交后再执行天然避免了超卖。4.5 数据库导入 SQL 报语法错误MySQL 版本与字符集不一致现象执行源码包里的.sql脚本时报类似You have an error in your SQL syntax或Unknown collation: utf8mb4_0900_ai_ci。原因后一种报错最常见。utf8mb4_0900_ai_ci是MySQL 8.0的默认排序规则而你本地装的是MySQL 5.7根本认不出这个排序规则。前一种则是脚本里用了USING BTREE、窗口函数等高版本特性低版本不支持。解决按本地MySQL版本做对应修改。用记事本打开SQL脚本把utf8mb4_0900_ai_ci全部替换成utf8mb4_general_ci再全局搜索ENGINEInnoDB ROW_FORMATDYNAMIC这类高版本语法直接删掉ROW_FORMAT部分即可。替换完保存再重新导入。这地方没什么技术含量但确实能卡住一大批刚装好数据库的新手。5. 给毕业设计加一点「耐答辩」的小改动乐观锁选课与执行计划自查5.1 改造选课核心代码加上版本号字段第4章给出的条件更新SQL已经能防守超卖但答辩时如果老师追问“你的并发控制怎么做的”只答条件更新还不够出彩。更规范的做法是给t_course表加一个version字段用乐观锁来做并发控制。改造后的Mapper接口方法增加版本号条件Update(UPDATE t_course SET selected_count selected_count 1, version version 1 WHERE course_id #{courseId} AND version #{version}) int updateCountWithVersion(Param(courseId) Integer courseId, Param(version) Integer version);Service层把原来的increaseSelectedCount换成带版本号的更新Course course courseMapper.selectById(courseId); if (course.getVersion() 0) { int rows courseMapper.updateCountWithVersion(courseId, course.getVersion()); if (rows 0) { throw new IllegalStateException(课程信息已变化请重试); } }这个写法的好处是每次更新只修改自己读到的那个版本号如果别的事务先改了行数据当前事务的更新影响行数就是0立刻抛异常让用户重试。这套逻辑听起来很高级实际代码不到五行而且不依赖数据库的锁机制性能比行锁更好。5.2 用自测清单验证改造效果不看「跑通了」就收工改完并发控制还要验证它真的生效了别看到BUILD SUCCESS就觉得完事。我给自己定的自测清单有三项第一单机开两个浏览器一个Edge一个Chrome用两个学生账号同时选同一门只剩1个名额的课程看最终是否只有一个成功第二看数据库selected_count是否始终等于t_selection表里该课程的记录数这两者对不上说明事务或更新逻辑有问题第三在选课接口里故意抛一个异常比如传一个不存在的courseId查t_selection里是否没有残留半条数据。三关都过了才敢说这个选课系统是真的能交付的。这三项自测里第二项最容易被忽略。很多项目“能跑”但数据表里全是脏数据比如选课记录删了selected_count却还在那儿挂着。这背后的原因多半是删除接口没走事务或者删除时根本没更新计数器。你把这些细节补上在演示时顺手翻开数据库表给老师看说服力比PPT上的架构图强得多。这趟调下来我的感受是毕业设计不是越炫越好而是每一行代码都能说出“为什么这么写”。把版本号加进选课接口虽然只是十几行改动却逼着你去查了事务隔离级别、乐观锁原理、JDBC批量更新这些Java基础问题面试被追问时也站得住。做完最后一轮自测记得把application.properties里测试用的端口和数据库账号改回常规配置这是我吃过亏才养成的收尾习惯。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Windows C语言网络编程:winsock2.h包含顺序与链接避坑指南 2026/10/1 16:00:24

Windows C语言网络编程:winsock2.h包含顺序与链接避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
目标平台与技术栈:C语言和Lua在游戏引擎中的协同落地 2026/10/1 16:00:17

目标平台与技术栈:C语言和Lua在游戏引擎中的协同落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
应用 I 触摸屏静电浪涌防护方案 2026/10/1 16:00:17

应用 I 触摸屏静电浪涌防护方案

一、应用背景触摸屏(又称触控屏或、触控面板)是一种可接收触头输入信号的感应式液晶显示装置。用户通过手指与屏幕直接触碰,就可与计算机等设备进行交互。触摸屏被广泛运用在各个行业,其中较为常见的就是手机屏幕。在使用过程中&a…

阅读更多 →
2026年五大经销商订货商城定制推荐:渠道流程与集成解析 2026/10/1 16:00:17

2026年五大经销商订货商城定制推荐:渠道流程与集成解析

2026年五大经销商订货商城定制推荐:渠道流程与集成解析摘要订货商城做定制,最怕的不是改不动,而是改完之后没人维护。企业的渠道政策本来就在变:今年新增一层代理,明年调整返利口径,后年又要接一套新的财务…

阅读更多 →
《HelloGitHub》第 20 期精选:23 个跨语言入门级开源项目实战解读 2026/10/1 16:00:17

《HelloGitHub》第 20 期精选:23 个跨语言入门级开源项目实战解读

技术博客文档知识库 【免费下载链接】HelloGitHub :octocat: 分享 GitHub 上有趣、入门级的开源项目。Share interesting, entry-level open source projects on GitHub. 项目地址: https://gitcode.com/GitHub_Trending/he/HelloGitHub 点击查看 免费下载 兴趣是最…

阅读更多 →
Ansys Workbench螺栓仿真:三条路线、预紧力与非线性收敛 2026/10/1 16:00:17

Ansys Workbench螺栓仿真:三条路线、预紧力与非线性收敛

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉