Java+小程序+MySQL学生选课系统源码实战:环境搭建与选课逻辑拆解 简介这是一套面向高校计算机相关专业学生的微信小程序毕业设计/课程设计完整源码主题为学生选课系统采用Java后端与微信小程序前端结合适合正在准备毕业设计、需要参考完整项目结构或进行二次开发的学习者。资源包共1245个文件约24.79MB涵盖104个java后端源码、127个vue管理端页面、166个js脚本、76个wxss与74个wxml小程序页面以及2个sql数据库文件另含png、svg等图片素材与json配置前后端与数据库文件齐全。系统区分学生、教师、管理员三类角色覆盖首页、个人中心、学生管理、教师管理、课程类型与课程信息管理、选课与取消选课管理、管理员管理及系统管理等功能模块可帮助学生理解MVC架构、Servlet请求处理与前后端交互流程。目前已有63人学习下载适合作为Java Web与小程序开发的实践素材也可为同类课程设计提供开发思路与目录结构参考。1. 学生选课系统源码拆解一套 Java 小程序 MySQL 的毕设能跑多远每年毕业季前两个月总有人来问我同一件事手里拿到一个「学生选课系统源码」的压缩包Java 后端、微信小程序前端、MySQL 数据库还带一份论文文档到底能不能直接拿来交差。我的回答通常是——能跑起来但能不能过答辩取决于你有没有真正拆开它。这套技术栈之所以成为毕业设计里的高频组合是因为它把「后端业务逻辑 移动端交互 数据持久化」三件事压缩进了一个体量可控的项目里Java 负责选课规则、名额扣减、冲突检测小程序负责学生端的课程浏览与提交MySQL 负责课程表、选课记录、用户信息的存储。它适合两类人一类是时间紧、需要快速搭出可演示系统的同学另一类是想借这个壳子练手、把 Java 和 MySQL 真正用一遍的初学者。但源码不是后悔药拿到手只是起点真正决定成败的是你能不能把环境跑通、把选课逻辑讲清楚、把数据一致性这个坑填上。2. 环境搭建从 MySQL 建库到小程序开发者工具跑通首页2.1 先确认这套源码的目录结构与依赖边界拿到压缩包后别急着双击运行先解压看目录。常见的结构是三个平级文件夹一个放 Java 后端工程可能是 Maven 或普通 Web 项目一个放小程序前端代码一个放数据库脚本和论文文档。后端工程里通常有src源码目录、pom.xml或lib依赖目录、application.properties或db.properties配置文件。小程序目录里会有app.json、pages页面文件夹、utils工具函数。数据库脚本一般是一个.sql文件里面包含建库、建表、插入初始数据的语句。先做一件事用文本编辑器打开配置文件找到数据库连接信息。你会看到类似jdbc:mysql://localhost:3306/xxx的地址、用户名和密码。把这几项记下来后面建库和改配置都要用。同时确认后端用的是哪个 JDK 版本常见的是 JDK 8 或 JDK 11版本不对会在编译阶段直接报错。小程序端要确认它用的是原生小程序语法还是 uni-app 之类的框架这决定了你后面用哪个工具打开。提示如果压缩包里没有pom.xml而是散落的jar包说明这是一个传统 Web 工程需要手动把 jar 包加入构建路径别当成 Maven 项目处理。2.2 MySQL 建库与导入脚本的完整命令数据库是这套系统的地基地基没打好后面全是玄学问题。先确认本机装了 MySQL版本建议 5.7 或 8.0两者在语法上差异不大但 8.0 默认的认证插件可能导致老驱动连接失败。安装完成后用命令行或 Navicat 这类工具连上数据库。第一步创建数据库并指定字符集。很多选课系统源码里的中文课程名乱码根源就是建库时没指定utf8mb4。-- 创建数据库字符集用 utf8mb4 以支持中文和特殊符号 CREATE DATABASE student_course DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; -- 切换到该数据库 USE student_course;第二步导入源码里的.sql脚本。假设脚本文件叫course.sql放在当前目录下。# 在命令行中导入-u 后跟用户名-p 表示需要输入密码 mysql -u root -p student_course course.sql导入完成后用SHOW TABLES;检查表是否齐全。一个典型的学生选课系统至少包含这几张表学生表student、教师表teacher、课程表course、选课记录表course_selection有的还会拆出班级表、院系表。重点看course_selection表它通常有一个联合唯一索引用来防止同一个学生重复选同一门课。-- 查看选课记录表的结构重点关注索引和约束 SHOW CREATE TABLE course_selection;如果脚本里没有这个唯一索引你要自己补上否则并发选课时会出现重复记录这是答辩时最容易被追问的点。-- 为选课记录表添加联合唯一索引防止重复选课 ALTER TABLE course_selection ADD UNIQUE KEY uk_student_course (student_id, course_id);参数说明student_id和course_id是选课记录表里的两个外键字段联合唯一索引保证同一学生对同一课程只能有一条记录。执行前先确认表里没有重复数据否则索引创建会失败。2.3 后端配置修改与启动验证数据库建好后回到 Java 后端工程。打开配置文件把数据库地址、用户名、密码改成你本机的实际值。如果源码用的是application.properties格式通常是键值对如果是db.properties格式类似。改完后检查端口号后端默认可能是 8080 或 8081确认这个端口没有被其他程序占用。# 数据库连接配置示例按本机实际情况修改 jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/student_course?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password你的密码注意serverTimezone参数MySQL 8.0 不设置时区会报时区错误这是新手最常见的翻车点之一。useUnicode和characterEncoding保证中文不乱码。配置改完后用 Maven 编译并启动。如果是 Maven 项目在工程根目录执行# 清理并编译跳过测试以加快速度 mvn clean package -DskipTests # 启动项目具体命令取决于打包方式 java -jar target/xxx.jar启动过程中盯着控制台日志看到Started ... in x seconds才算成功。如果报Communications link failure说明数据库连不上回头检查 MySQL 服务是否启动、端口是否对、密码是否正确。如果报Table xxx doesnt exist说明脚本没导入完整重新执行导入命令。2.4 小程序端配置与首页跑通后端跑起来后处理小程序端。用微信开发者工具打开小程序目录工具会自动读取app.json里的页面配置。找到请求后端的配置文件通常叫config.js或request.js把里面的接口地址改成你本机的后端地址。// 小程序端接口地址配置本地调试用 localhost 或本机 IP const BASE_URL http://localhost:8080/api; // 封装请求函数统一处理错误 function request(url, method, data) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL url, method: method, data: data, success: res resolve(res.data), fail: err reject(err) }); }); }参数说明BASE_URL是后端接口前缀本地调试时如果开发者工具和浏览器不在同一台机器要把localhost换成局域网 IP。wx.request是小程序发起网络请求的 API注意它默认有域名校验本地调试需要在开发者工具里勾选「不校验合法域名」。编译小程序如果首页能正常加载课程列表说明前后端已经打通。如果首页空白打开开发者工具的调试面板看 Network 请求大概率是接口地址不对或后端没启动。3. 选课核心逻辑名额扣减、冲突检测与数据一致性怎么落地3.1 选课流程的业务拆解与接口设计选课不是简单往表里插一条记录。一个完整的选课请求要经过这几步验证学生身份、检查课程是否已选、检查课程名额是否已满、检查时间是否冲突、扣减名额、写入选课记录。这六步里任何一步出问题都会导致数据不一致。先看接口设计。后端通常暴露一个POST /api/selection/select接口接收学生 ID 和课程 ID。返回结果要区分成功、重复选课、名额已满、时间冲突这几种状态而不是笼统返回成功或失败。// 选课接口的控制器方法返回统一的结果对象 PostMapping(/select) public Result selectCourse(RequestBody SelectionRequest request) { // 参数校验 if (request.getStudentId() null || request.getCourseId() null) { return Result.error(参数不完整); } // 调用业务层处理选课逻辑 return selectionService.selectCourse(request.getStudentId(), request.getCourseId()); }参数说明SelectionRequest是请求体对象包含studentId和courseId两个字段。Result是统一返回格式包含状态码、消息和数据。业务逻辑全部放在selectionService里控制器只做参数校验和转发。3.2 名额扣减的 SQL 写法与并发问题名额扣减是选课系统里最容易出问题的地方。常见做法是先查课程剩余名额如果大于零就扣减。但这种「先查后改」在并发场景下会翻车两个学生同时查到名额为 1都认为可以选结果扣减后名额变成 -1。正确的做法是用一条 SQL 完成「检查并扣减」利用数据库的行锁保证原子性。-- 原子扣减名额只有剩余名额大于零时才执行更新 UPDATE course SET remaining remaining - 1 WHERE id #{courseId} AND remaining 0;这条 SQL 执行后检查受影响行数。如果返回 1说明扣减成功如果返回 0说明名额已满。在 Java 里用int affected courseMapper.decreaseRemaining(courseId);接收结果再根据affected的值决定后续操作。// 业务层选课逻辑先扣名额再写记录 Transactional public Result selectCourse(Long studentId, Long courseId) { // 检查是否已选 int count selectionMapper.countByStudentAndCourse(studentId, courseId); if (count 0) { return Result.error(您已选过这门课); } // 原子扣减名额 int affected courseMapper.decreaseRemaining(courseId); if (affected 0) { return Result.error(名额已满); } // 写入选课记录 selectionMapper.insert(studentId, courseId); return Result.success(选课成功); }参数说明Transactional注解保证方法内所有数据库操作在同一个事务里任何一步失败都会回滚。countByStudentAndCourse查询是否已有选课记录decreaseRemaining执行原子扣减insert写入记录。注意事务的隔离级别默认的REPEATABLE READ在 MySQL 里配合行锁能挡住大部分并发问题。3.3 时间冲突检测的实现思路时间冲突检测是选课系统里另一个高频需求。每门课有上课时间比如「周一 1-2 节」「周三 3-4 节」。学生选课时要检查新课程的时间是否和已选课程重叠。常见做法是把上课时间存成结构化字段比如day_of_week星期几和time_slot第几节。检测冲突时查询该学生已选课程的时间和新课程做比对。-- 查询学生已选课程的时间安排 SELECT c.day_of_week, c.time_slot FROM course_selection cs JOIN course c ON cs.course_id c.id WHERE cs.student_id #{studentId};拿到已选课程的时间列表后在 Java 里遍历比对。如果新课程的day_of_week和time_slot与已有课程相同就判定为冲突。// 时间冲突检测逻辑 ListCourseTime selectedTimes selectionMapper.getSelectedTimes(studentId); for (CourseTime time : selectedTimes) { if (time.getDayOfWeek().equals(newCourse.getDayOfWeek()) time.getTimeSlot().equals(newCourse.getTimeSlot())) { return Result.error(上课时间冲突); } }参数说明CourseTime是包含dayOfWeek和timeSlot的对象。这种比对方式假设每门课只占一个时间段如果课程跨多个时间段需要把时间存成更细的粒度比如用一张单独的course_schedule表每行记录一个时间段。3.4 数据一致性事务、锁与回滚的配合选课系统的数据一致性靠三样东西保证事务、行锁、唯一索引。事务保证一组操作要么全成功要么全失败行锁保证并发扣减名额时不会超卖唯一索引保证同一学生不会重复选同一门课。这三者要配合使用。事务里先扣名额再写记录如果写记录失败事务回滚名额自动恢复。行锁由UPDATE ... WHERE remaining 0这条语句触发MySQL 会对符合条件的行加排他锁其他事务等待。唯一索引是最后一道防线即使前面逻辑有漏洞数据库层面也会拒绝重复插入。// 完整的选课事务包含异常处理和回滚 Transactional(rollbackFor Exception.class) public Result selectCourse(Long studentId, Long courseId) { try { // 检查重复选课 if (selectionMapper.countByStudentAndCourse(studentId, courseId) 0) { return Result.error(请勿重复选课); } // 原子扣减名额 if (courseMapper.decreaseRemaining(courseId) 0) { return Result.error(名额已满); } // 写入选课记录 selectionMapper.insert(studentId, courseId); return Result.success(选课成功); } catch (DuplicateKeyException e) { // 唯一索引冲突说明并发下重复插入 throw new RuntimeException(选课冲突请重试); } }参数说明rollbackFor Exception.class确保任何异常都触发回滚。DuplicateKeyException是 Spring 对唯一索引冲突的封装捕获后可以给用户更友好的提示。注意不要在事务里做耗时操作比如发短信、调外部接口否则会拉长事务时间增加锁等待。4. 避坑与排查源码跑不起来时先看这五个地方4.1 数据库连接失败从报错信息定位问题现象后端启动时报Communications link failure或Access denied for user。原因通常是三种MySQL 服务没启动、端口不对、密码错误。解决步骤先用mysql -u root -p在命令行登录确认服务正常然后检查配置文件里的端口是不是 3306如果 MySQL 装在非默认端口要同步修改最后确认用户名和密码注意大小写和特殊字符。4.2 中文乱码字符集没对齐的连锁反应现象课程名、学生姓名显示成问号或乱码。原因是数据库、表、连接三处的字符集不一致。解决建库时用utf8mb4建表时继承库的字符集JDBC 连接串里加useUnicodetruecharacterEncodingutf8。如果已经建好表用ALTER TABLE course CONVERT TO CHARACTER SET utf8mb4;修改。4.3 选课名额超卖并发下的经典翻车现象课程剩余名额显示为负数或者选课人数超过课程容量。原因是用了「先查后改」的非原子操作。解决把扣减逻辑改成UPDATE course SET remaining remaining - 1 WHERE id ? AND remaining 0根据受影响行数判断是否成功。同时给选课记录表加联合唯一索引双保险。4.4 小程序请求失败域名校验与地址配置现象小程序首页空白调试面板显示request:fail url not in domain list。原因是微信开发者工具默认校验合法域名本地调试的localhost不在白名单里。解决在开发者工具右上角「详情」→「本地设置」里勾选「不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书」。同时确认BASE_URL指向的后端地址和端口正确。4.5 事务不回滚注解失效的隐蔽陷阱现象选课失败后名额没有恢复数据处于中间状态。原因是Transactional注解失效常见于方法被同类内部调用、方法不是public、异常被捕获后没重新抛出。解决确保事务方法由外部类调用方法是public捕获异常后要么重新抛出要么手动调用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()。5. 从能跑到能讲答辩前把选课系统讲透的三个技巧5.1 用一条选课请求串起所有技术点答辩时老师不会让你逐行念代码但会让你讲清楚一个请求从发出到落库的完整链路。我的习惯是拿「学生点击选课按钮」这个动作当线索从前到后串一遍小程序端wx.request发出 POST 请求后端控制器接收参数业务层开启事务先查重复选课再原子扣减名额然后写入选课记录最后提交事务返回结果。这条链路里涉及 HTTP 协议、Spring 事务、MySQL 行锁、唯一索引每个点都能展开。你不需要背只需要在本地打断点走一遍把每一步的实际数据记下来。5.2 准备三个「如果」来应对追问老师最爱问「如果……怎么办」。提前准备三个场景如果两个学生同时选最后一门课怎么办如果选课成功后数据库挂了怎么办如果学生选完课又退课名额怎么恢复第一个用行锁和原子扣减回答第二个用事务回滚回答第三个用退课接口加名额恢复的 SQL 回答。每个回答都落到具体代码或 SQL 上别只讲概念。-- 退课时恢复名额的 SQL UPDATE course SET remaining remaining 1 WHERE id #{courseId}; -- 删除选课记录 DELETE FROM course_selection WHERE student_id #{studentId} AND course_id #{courseId};这两条语句要放在同一个事务里先删记录再恢复名额或者反过来都行关键是保证原子性。5.3 把论文文档当成索引而不是正文源码包里那份论文文档别直接复制粘贴。把它当成索引文档里提到「基于 Spring Boot 的选课系统设计」你就去代码里找到对应的控制器和服务类提到「MySQL 数据库设计」你就去对照表结构和索引。答辩时老师问什么你能立刻定位到代码位置这比背稿子管用。我一般会在文档目录页用铅笔标上对应的代码文件路径翻到哪一页就知道该打开哪个文件。最后说个血泪经验别等到答辩前一天才跑系统。提前一周把环境搭好把选课、退课、查询三个流程各走十遍把每次的报错和解决过程记在一个文本文件里。这个文件就是你答辩时的底气。希望帮到你。本文还有配套的精品资源点击获取