Java Swing写的毕业选题桌面工具:带MySQL数据库、源码和表设计文档
简介:一个开箱即用的本地毕业设计选题分配系统,用Java Swing开发,通过JDBC直连本地MySQL数据库,支持学生自主选题、教师发布课题、管理员审核与批量分配、选题状态实时查询等功能。项目结构清晰:src目录包含全部带注释的Java源码,lib目录集成所需依赖,studentassignment.sql脚本可一键导入初始化数据,配套提供Word和Markdown双格式数据库设计文档,详细说明学生表、教师表、课题表、分配记录表等字段含义、主外键关系及业务规则(如每人限选1题、教师出题数上限等)。运行只需JDK 8或更高版本 + 本地MySQL服务,不依赖Tomcat或其他Web容器,适合高校课程设计实践、毕业设计参考或教学演示场景。代码模块划分明确,界面使用标准Swing组件构建,无第三方UI框架,便于理解底层逻辑、调试修改或拓展功能,比如增加导出Excel、邮件通知或权限分级。
1. 项目概述:为什么一个“老派”的Swing桌面工具,反而成了毕设选题管理的最优解?
你可能刚点开这个项目时心里嘀咕:“都2024年了,还用Swing?不是早该被JavaFX或Web前端取代了吗?”——这恰恰是我带过三届毕业设计、审过上百份毕设源码后,最想跟你掰开揉碎讲清楚的第一件事。不是技术越新越好,而是问题越准,解法越稳。 这个系统解决的,从来就不是“炫技”,而是高校教务场景里一个极其具体、极其高频、极其容易翻车的痛点:如何让学生在不崩服务器、不卡浏览器、不等老师手动Excel汇总的情况下,完成一轮干净利落、可追溯、可审计的毕业设计选题流程。
它用的是JDK 8+和本地MySQL,意味着你不需要配Tomcat、不用搞Nginx反向代理、不用学Spring Boot的自动装配、更不用碰Docker容器化部署——这些对一个两周就要交初稿、三天就要跑通Demo的大四学生来说,全是干扰项。而Swing在这里的价值,恰恰在于它的“笨重感”带来的确定性:一个JFrame就是一个窗口,一个JTable就真是一张表,一个JDBC Connection就是一条实打实的数据库连接。没有中间商赚差价,没有框架帮你“猜意图”,所有逻辑都在你眼皮底下,改一行代码,界面就动一下,查一条SQL,结果就刷出来。这种透明度,是任何现代Web框架在教学初期都难以提供的。
关键词里的“Swing选题系统”、“Java毕设源码”、“MySQL选题数据库”、“JDBC桌面应用”,不是堆砌术语,而是四个精准锚点:它定位清晰(Swing,非Web)、用途明确(毕设参考,非商用产品)、数据根基扎实(MySQL,非内存H2或文件存储)、通信方式直白(JDBC,非REST API或消息队列)。它不追求高并发,但要求事务强一致;不强调UI动画,但必须字段校验严丝合缝;不玩微服务拆分,但模块边界必须像刀切豆腐一样分明——学生模块只管选题逻辑,教师模块只管出题审核,管理员模块只管分配与统计。这种“克制”,正是它能成为课程设计范本的核心原因。如果你正为毕设选题发愁,或者需要一个能讲清楚“从界面按钮点击到数据库记录插入”全链路的示例,那它不是备选,而是首选。
2. 整体架构与设计思路:为什么放弃Web,坚持“桌面+直连”这条看似复古的路?
2.1 场景倒推:高校教务的真实约束,决定了技术栈的取舍
很多同学一上来就想做“基于Spring Boot + Vue的选题平台”,想法很好,但落地时会撞上几堵看不见的墙:第一堵是环境不可控。你的毕设答辩现场,很可能只有台装着Win10、连着校园网的笔记本,预装环境只有JDK和MySQL Workbench。你不可能在现场临时拉起一个Docker集群,也不可能指望答辩老师打开Chrome去访问http://localhost:8080。第二堵是时间成本黑洞。光是配置application.yml里的数据库URL、解决MyBatis的@Select注解报错、调试Vue组件的响应式失效,就能吃掉你一周的有效开发时间。而这个Swing系统,双击StudentAssignment.jar就启动,输入本地MySQL账号密码,点“初始化数据库”,再点“登录”,整个流程5分钟内搞定。这不是偷懒,是把有限精力聚焦在业务逻辑本身——比如,“学生提交选题后,如何防止他反复刷新页面重复提交?”这个问题,在Swing里,你只需要在按钮点击事件里加一个setEnabled(false),再在数据库操作完成后setEnabled(true);而在Web里,你得考虑前端防抖、后端幂等性、分布式锁……复杂度直接升维。
所以,架构决策的第一条铁律是:一切以“最小可行演示”(MVP)为终点,而非以“技术先进性”为标尺。 Swing + JDBC + MySQL的组合,恰好踩在了这个MVP的黄金平衡点上:它足够简单,让新手能看懂每一行;它足够完整,覆盖了CRUD、事务、权限、状态流转等核心概念;它足够独立,不依赖外部服务,打包即走。
2.2 模块划分:四个角色,四套视图,一套数据模型
系统严格遵循“角色驱动界面”的设计哲学,没有试图用一个万能界面适配所有人,而是为学生、教师、管理员、系统维护者(你本人)分别定制了入口和功能集。这种划分不是为了炫技,而是源于真实业务流:
-
学生端:核心诉求是“看到题、选上题、知道结果”。界面极度精简,顶部是课题列表
JTable,支持按专业、难度、状态筛选;下方是“申请选题”按钮,点击后弹出确认对话框,防止误操作;右侧是个人选题状态面板,实时显示“已申请/已审核/已分配/已确认”四种状态。这里的关键细节是:学生提交后,按钮立刻置灰,后台同时执行两条SQL——一条插入assignment_request表,一条更新topic表的current_applicants计数器。这两条操作必须包裹在同一个JDBC事务里,否则会出现“学生看到提交成功,但课题名额没减”的数据不一致。源码里你能看到Connection.setAutoCommit(false)和connection.commit()的成对出现,这就是教科书级的事务控制实践。 -
教师端:核心诉求是“出好题、管好题、审好题”。界面左侧是课题发布表单(标题、描述、难度、所需技能、截止日期),右侧是自己发布的课题列表,带“编辑”、“下架”、“审核学生申请”三个操作列。特别值得注意的是“审核”功能:当教师点击某条申请的“通过”按钮时,系统不会直接分配,而是先检查该学生是否已存在有效分配记录(
SELECT COUNT(*) FROM assignment WHERE student_id = ? AND status IN ('allocated', 'confirmed')),如果已有,则弹窗提示“该生已有课题,请先取消原有分配”。这个看似简单的判断,背后是业务规则的硬编码——每人限选1题,这是教务处的铁律,必须在代码里写死,而不是靠用户自觉。 -
管理员端:这是系统的“大脑”,功能最重。包含三大板块:一是“课题池管理”,可批量导入Excel格式的课题清单(源码里提供了
ExcelImporter工具类,用Apache POI解析);二是“智能分配”,点击按钮后,系统按预设策略(如:优先满足学生第一志愿、教师课题容量均衡、跨专业调剂)自动生成分配方案,并生成预览表格供人工复核;三是“全局统计”,用JFreeChart绘制柱状图,展示各专业选题率、各教师课题饱和度、未分配学生名单等。这里的“智能分配”算法其实很朴素:它本质是一个带权重的贪心匹配,源码里AllocationEngine.java的calculateBestMatch()方法,用不到50行代码就实现了核心逻辑,比任何复杂的机器学习模型都更适合教学演示——因为它可读、可调试、可修改。 -
系统维护者视角:也就是你,开发者。
src目录下的包结构就是你的作战地图:ui包放所有界面类,dao包放所有数据库访问对象(每个表对应一个DAO,如StudentDao.java),model包定义实体类(Student.java,Topic.java),service包封装业务逻辑(AssignmentService.java处理分配规则),util包存放工具类(DBUtil.java管理连接池,Validator.java做字段校验)。这种分层不是为了贴“三层架构”的标签,而是当你需要“给学生增加邮箱字段”时,你只需改三处:Student.java加属性、StudentDao.java加updateEmail()方法、StudentPanel.java加输入框——路径清晰,无脑可执行。
2.3 数据库设计:一张ER图,撑起整个业务世界的骨架
studentassignment.sql脚本和配套的Word/Markdown文档,是这个项目的另一大宝藏。它没有用任何ORM的“逆向工程”噱头,而是手把手教你如何从零开始设计一个符合教务逻辑的关系型数据库。我们来拆解最关键的四张表:
-
student表:主键id(BIGINT自增),核心字段有name(VARCHAR 50)、student_id(唯一学号,UNIQUE NOT NULL)、major(专业,用于后续按专业筛选课题)、email(预留字段,方便未来扩展邮件通知)。这里有个易错点:很多同学会把student_id设为主键,但这是危险的——学号理论上可能变更(如转专业重编),而主键一旦设定就不该变。所以用id作主键,student_id作业务唯一键,既保证了数据稳定性,又满足了业务查询需求。 -
teacher表:结构类似,teacher_id是工号,name、department(院系),关键字段是max_topics(整型,默认值5),这个字段直接支撑了“教师出题数上限”的业务规则。在教师发布课题时,DAO层会先执行SELECT max_topics, COUNT(*) FROM teacher t JOIN topic tp ON t.id = tp.teacher_id WHERE t.id = ? GROUP BY t.max_topics,对比两者,超限则拒绝发布。这个查询在TeacherDao.java的canPublishMoreTopics()方法里,是典型的“用数据库约束代替应用层判断”的最佳实践。 -
topic表:这是核心枢纽。字段包括title(课题名)、description(描述)、difficulty(难度1-5)、required_skills(JSON格式存储,如["Java", "MySQL"],便于前端解析展示)、teacher_id(外键关联teacher.id)、status(ENUM类型:’draft’,’published’,’closed’)、current_applicants(当前申请人数,用于实时显示剩余名额)。注意required_skills用JSON而非单独建skill表,是因为技能是弱实体,变动频繁且无需深度关联查询,JSON存储简洁高效,JDBC原生支持ResultSet.getString()直接读取。 -
assignment表:记录分配结果。字段有student_id、topic_id、teacher_id(冗余存储,避免多表JOIN)、status(ENUM:’pending’,’allocated’,’confirmed’,’rejected’)、created_at(申请时间)、updated_at(最后更新时间)。这里status的状态机设计是重点:学生申请后是pending,教师审核通过变成allocated,学生确认后才是confirmed。这个三态流转,完美模拟了真实教务流程,也让你在写状态查询逻辑时,必须思考WHERE status IN ('allocated', 'confirmed')和WHERE status = 'confirmed'的区别——前者是“已分配”,后者才是“最终确认”。
整个数据库设计文档里,最值得你逐字研读的是“业务规则约束”章节。它明确写出:“assignment表中,(student_id, status)联合唯一,确保同一学生在同一状态下只能有一条记录”,这直接防止了学生重复提交;“topic表的current_applicants字段,必须由应用层在每次申请/取消时原子性更新,禁止直接UPDATE”,因为并发场景下,两个学生同时申请,可能导致计数器只加1而非2,源码里用UPDATE topic SET current_applicants = current_applicants + 1 WHERE id = ? AND current_applicants < max_capacity这条带条件的UPDATE语句,就是乐观锁的朴素实现。
3. 核心细节解析与实操要点:从双击运行到读懂每一行注释
3.1 环境准备:三步到位,拒绝“环境配置地狱”
很多同学卡在第一步:下载了源码,双击StudentAssignment.jar没反应,或者报ClassNotFoundException。别慌,这90%是环境没配对。按这个顺序来,保你5分钟搞定:
第一步:确认JDK版本
打开命令行,输入java -version。输出必须是1.8.x或更高(如11.0.20)。如果显示bash: java: command not found,说明JDK没装或没配PATH。去Oracle官网或Adoptium下载JDK 8或11,安装时勾选“添加到PATH”。Windows用户注意:不要只装JRE,必须装JDK,因为编译源码需要javac。
第二步:启动本地MySQL
推荐用XAMPP或Docker Desktop。XAMPP最傻瓜:下载安装后,启动Apache和MySQL服务,打开http://localhost/phpmyadmin,用默认账号root/空密码登录。Docker用户执行:
docker run --name my-mysql -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 -d mysql:8.0
关键点:记住你的MySQL root密码(脚本里默认是123456),以及端口(默认3306)。如果端口被占用,改成-p 3307:3306,然后在代码里同步修改DBUtil.java中的URL。
第三步:导入数据库
打开phpMyAdmin或MySQL Workbench,新建数据库studentassignment(字符集选utf8mb4_unicode_ci),然后点击“导入”,选择项目根目录下的studentassignment.sql文件,执行。成功后,你会看到student, teacher, topic, assignment四张表。此时,打开src/util/DBUtil.java,找到这一行:
private static final String URL = "jdbc:mysql://localhost:3306/studentassignment?useSSL=false&serverTimezone=UTC";
确认localhost、3306、studentassignment都和你实际环境一致。如果MySQL装在虚拟机里,localhost要换成虚拟机IP。
提示:如果导入SQL时报错“Unknown collation: ‘utf8mb4_0900_ai_ci’”,说明你用的是MySQL 8.0+,而SQL脚本是为5.7写的。解决方案:用文本编辑器打开
studentassignment.sql,全局替换utf8mb4_0900_ai_ci为utf8mb4_unicode_ci,再导入。这是MySQL版本兼容性的经典坑,我带学生时至少遇到过20次。
3.2 源码结构精读:src目录下的黄金三角
src目录是整个项目的灵魂,它的组织方式直接决定了你二次开发的效率。我们聚焦三个最常修改的包:
ui包:界面即逻辑,按钮即入口LoginFrame.java是第一个接触的类。它不只是一张登录表单,更是权限路由的总开关。看它的loginButton.addActionListener()方法:
String username = usernameField.getText().trim();
String password = new String(passwordField.getPassword());
// 校验空值
if (username.isEmpty() || password.isEmpty()) {
JOptionPane.showMessageDialog(this, "用户名和密码不能为空!");
return;
}
// 调用Service层验证
User user = UserService.login(username, password);
if (user != null) {
// 根据user.getRole()决定跳转
switch (user.getRole()) {
case "student": new StudentMainFrame(user).setVisible(true); break;
case "teacher": new TeacherMainFrame(user).setVisible(true); break;
case "admin": new AdminMainFrame(user).setVisible(true); break;
}
this.dispose(); // 关闭登录窗
}
这段代码揭示了Swing开发的精髓:事件驱动,状态导向。每一个按钮点击,都是一个明确的业务动作起点。如果你想增加“忘记密码”功能,就在LoginFrame.java里加一个JButton,写一个actionPerformed方法,调用UserService.sendResetEmail()——路径清晰,毫无歧义。
dao包:SQL即API,每一条都是数据命脉TopicDao.java是高频修改对象。它的getPublishedTopics()方法长这样:
public List<Topic> getPublishedTopics(String major, Integer difficulty) {
String sql = "SELECT * FROM topic WHERE status = 'published'";
List<Object> params = new ArrayList<>();
if (major != null && !major.isEmpty()) {
sql += " AND major = ?";
params.add(major);
}
if (difficulty != null) {
sql += " AND difficulty = ?";
params.add(difficulty);
}
sql += " ORDER BY created_at DESC LIMIT 50";
return query(sql, params.toArray(), Topic.class);
}
注意两点:一是动态SQL拼接,用List<Object>收集参数,彻底规避SQL注入;二是LIMIT 50,防止课题太多导致界面卡死。这是生产级思维的体现——永远假设数据量会增长。如果你想按“所需技能”筛选,就加一个if (skills != null)分支,sql += " AND required_skills LIKE ?",params.add("%" + skill + "%")。改起来,就是复制粘贴加一行的事。
service包:业务规则的圣殿,所有“应该”在这里定义AssignmentService.java里的allocateTopics()方法,是管理员“智能分配”的核心。它不直接操作数据库,而是协调多个DAO:
public AllocationResult allocateTopics() {
// 1. 获取所有待分配学生(status='pending')
List<Student> pendingStudents = studentDao.findPendingStudents();
// 2. 获取所有可分配课题(status='published'且有空余名额)
List<Topic> availableTopics = topicDao.findAvailableTopics();
// 3. 执行匹配算法(贪心策略)
Map<Student, Topic> allocationMap = matchingEngine.match(pendingStudents, availableTopics);
// 4. 批量写入assignment表,并更新topic计数器
assignmentDao.batchInsert(allocationMap);
topicDao.batchUpdateApplicantCount(allocationMap.values());
return new AllocationResult(allocationMap.size(), "分配成功");
}
这个方法的价值在于:它把“匹配逻辑”和“数据持久化”完全解耦。matchingEngine可以是简单的按志愿排序,也可以是你自己写的基于学生GPA加权的算法。只要它返回Map<Student, Topic>,allocateTopics()就能无缝对接。这就是面向接口编程的力量——你改算法,不影响DAO;你换DAO实现(比如改成MongoDB),不影响算法。
3.3 数据库表设计文档:不只是字段列表,更是业务契约
毕业设计选题管理系统数据库表设计文档.doc和.md,我建议你打印出来,放在手边。它不是摆设,而是你和导师沟通、和同学协作的共同语言。以assignment表为例,文档里这样写:
| 字段名 | 类型 | 允许NULL | 默认值 | 描述 | 业务约束 |
|---|---|---|---|---|---|
| id | BIGINT PK | NO | - | 主键,自增 | - |
| student_id | BIGINT FK | NO | - | 关联student.id | 外键ON DELETE CASCADE |
| topic_id | BIGINT FK | NO | - | 关联topic.id | 外键ON DELETE RESTRICT(课题删除前必须清空分配) |
| status | ENUM | NO | ‘pending’ | 分配状态 | 只能是’pending’,’allocated’,’confirmed’,’rejected’ |
| created_at | DATETIME | NO | CURRENT_TIMESTAMP | 创建时间 | - |
| updated_at | DATETIME | NO | CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 最后更新时间 | - |
看到“外键ON DELETE RESTRICT”这条约束,你就明白:为什么在AdminMainFrame.java里,删除课题的按钮逻辑是先assignmentDao.deleteByTopicId(topicId),再topicDao.delete(topicId)。如果顺序颠倒,数据库会直接抛异常Cannot delete or update a parent row: a foreign key constraint fails。这个文档,就是你的“防错说明书”。
注意:文档里所有“业务约束”栏的内容,都必须在代码里有对应实现。比如“status只能是四个值”,在
Assignment.java实体类里,status字段的setter方法就有校验:java public void setStatus(String status) { if (!Arrays.asList("pending", "allocated", "confirmed", "rejected").contains(status)) { throw new IllegalArgumentException("非法状态值: " + status); } this.status = status; }
这叫“防御性编程”,是毕设答辩时,导师最爱问“你怎么保证数据不脏”的标准答案。
4. 实操过程与核心环节实现:从零开始,亲手跑通一次选题全流程
4.1 初始化:一键导入,让数据库从空白到满血
studentassignment.sql脚本是整个系统的基石。它不只是建表,更是一套完整的初始数据快照。我们来亲手执行一遍,理解它背后的深意:
-
打开MySQL客户端(phpMyAdmin或命令行)
命令行用户执行:mysql -u root -p,输入密码后进入。 -
创建数据库并选择
sql CREATE DATABASE studentassignment CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE studentassignment; -
执行SQL脚本
如果用命令行,执行:source /path/to/studentassignment.sql(替换为你的实际路径)。如果用phpMyAdmin,直接在“导入”页上传。
脚本执行后,你会看到四张表,以及关键的初始数据:
- student表里有3条测试学生记录(学号S001-S003),专业分别是“计算机科学”、“软件工程”、“人工智能”。
- teacher表里有2条记录(T001-T002),max_topics分别是5和3。
- topic表里有6条课题,其中T001发布了4个,T002发布了2个,全部status='published',current_applicants=0。
- assignment表为空,因为还没人申请。
这个初始状态,就是你演示的完美起点。它确保了第一次运行时,学生能看到课题,教师能看到自己的发布记录,管理员能看到待分配池。为什么要有初始数据? 因为纯空库会让第一次演示变得无比尴尬:学生登录后看到“暂无课题”,教师点“我的课题”一片空白——这无法体现系统价值。初始数据,是给演示注入生命力的第一剂强心针。
4.2 学生选题:从浏览到确认的完整闭环
现在,让我们化身学生S001,走一遍选题流程:
- 启动程序:双击
StudentAssignment.jar,或命令行执行java -jar StudentAssignment.jar。 - 登录:用户名
S001,密码123456(初始数据里预设的)。 - 浏览课题:主界面左侧
JTable自动加载所有status='published'的课题。你可以用顶部的JComboBox按专业筛选(选“计算机科学”),看到T001发布的“基于Swing的选题系统”排在第一位。 -
申请选题:选中该课题,点击“申请选题”按钮。此时,程序会执行:
- 弹出确认对话框:“确定申请《基于Swing的选题系统》吗?此操作不可撤销。”
- 用户点击“是”后,按钮立即setEnabled(false),界面显示“申请中…”。
- 后台开启事务,执行两条SQL:sql INSERT INTO assignment (student_id, topic_id, status, created_at) VALUES (1, 1, 'pending', NOW()); UPDATE topic SET current_applicants = current_applicants + 1 WHERE id = 1;
- 事务提交后,刷新表格,该课题的“剩余名额”从5变成4,学生状态面板显示“已申请”。 -
查看状态:右侧面板清晰显示“当前状态:已申请”,并列出申请时间。如果想取消,点击“取消申请”,程序会执行
DELETE FROM assignment WHERE student_id = 1 AND status = 'pending',并回滚current_applicants计数器。
这个流程里,最值得你抠细节的是事务的边界。在AssignmentDao.java的createRequest()方法里,你能看到:
public void createRequest(Long studentId, Long topicId) throws SQLException {
String sql = "INSERT INTO assignment (student_id, topic_id, status, created_at) VALUES (?, ?, 'pending', NOW())";
executeUpdate(sql, studentId, topicId); // 第一步:插入申请
sql = "UPDATE topic SET current_applicants = current_applicants + 1 WHERE id = ? AND current_applicants < (SELECT max_capacity FROM teacher t JOIN topic tp ON t.id = tp.teacher_id WHERE tp.id = ?)";
executeUpdate(sql, topicId, topicId); // 第二步:更新名额,带容量检查
}
注意第二条UPDATE里的子查询:它确保了即使教师临时把max_topics从5改成3,系统也能在更新时发现current_applicants已达上限,从而让UPDATE影响行为0行,事务回滚。这就是用SQL能力兜底业务规则的高级玩法。
4.3 教师审核:从发布到终审的权限闭环
切换身份,用T001/123456登录教师端:
-
发布课题:点击“发布新课题”,填写标题、描述、难度(选4)、所需技能(填
["Swing", "JDBC"]),选择专业“计算机科学”,点击“发布”。后台执行:sql INSERT INTO topic (title, description, difficulty, required_skills, teacher_id, status, created_at) VALUES (?, ?, ?, ?, 1, 'published', NOW());
发布后,课题立刻出现在“我的课题”列表里,状态为“已发布”。 -
审核学生申请:在“学生申请”选项卡,看到S001的申请,状态为“待审核”。点击“通过”按钮,程序执行:
- 先查SELECT COUNT(*) FROM assignment WHERE student_id = 1 AND status IN ('allocated', 'confirmed'),确认S001无其他有效分配。
- 再执行UPDATE assignment SET status = 'allocated', updated_at = NOW() WHERE id = ?。
- 同时,发送一条系统日志到system_log表(文档里虽未列,但源码中有),记录“T001于2024-05-20 14:30:00审核通过S001的申请”。 -
学生确认:S001重新登录,看到状态变为“已分配”,旁边多了一个“确认选题”按钮。点击后,状态变为“已确认”,
assignment表的status更新为'confirmed'。此时,该课题的current_applicants不再变化,因为分配已锁定。
这个闭环的设计,体现了教务管理的严肃性:发布是起点,审核是授权,确认是承诺。 每一步都有状态标记,每一步都可追溯。你在写毕设论文时,“系统状态机设计”这一节,可以直接拿这个status枚举和流转图去填充。
4.4 管理员分配:从手动到智能的效率跃迁
最后,用admin/123456登录管理员端,体验终极权力:
-
课题池管理:点击“批量导入”,选择一个Excel文件(项目里提供了
sample_topics.xlsx示例)。ExcelImporter.java会解析每一行,对title、difficulty等字段做非空校验,对difficulty做范围校验(1-5),校验失败的行会高亮显示在预览表格里,让你人工修正。这比直接LOAD DATA INFILE安全得多。 -
智能分配:点击“执行分配”,程序弹出进度条。后台
AllocationEngine.java的match()方法启动:
- 它遍历所有status='pending'的学生。
- 对每个学生,按其志愿顺序(student_preference表,文档里有说明)查找匹配课题。
- 匹配规则:课题major匹配学生专业,difficulty不超过学生能力(假设学生GPA>3.5可选难度4+),required_skills包含学生掌握技能(用LIKE模糊匹配)。
- 找到后,加入分配映射,同时标记该课题current_applicants++,防止被重复分配。 -
结果预览与确认:分配完成后,弹出
JDialog显示预览表格,列出“学生-课题-教师”三元组。你可以勾选任意行,点击“撤销分配”,或直接点击“全部确认”,触发batchInsert()写入数据库。
这个功能的价值,不在于算法多炫酷,而在于它把一个原本需要管理员花半天Excel手工拖拽的重复劳动,压缩到10秒内完成,并且100%可复现、可审计。你的毕设答辩PPT里,“提升管理效率XX%”的数据,就来自这里。
5. 常见问题与排查技巧实录:那些我在实验室里修过的深夜Bug
5.1 连接不上MySQL?先查这五件事
这是最高频问题,我整理了一份速查表,按发生概率排序:
| 现象 | 可能原因 | 排查命令/步骤 | 解决方案 |
|---|---|---|---|
Communications link failure |
MySQL服务没启动 | Windows:任务管理器→服务→MySQL;Mac:brew services list \| grep mysql |
启动MySQL服务 |
Access denied for user 'root'@'localhost' |
密码错误或用户权限不足 | mysql -u root -p,输密码;若失败,重置密码 |
用mysqld --skip-grant-tables启动,然后UPDATE mysql.user SET authentication_string=PASSWORD('123456') WHERE User='root'; FLUSH PRIVILEGES; |
Unknown database 'studentassignment' |
数据库没创建或名字拼错 | SHOW DATABASES; |
执行CREATE DATABASE studentassignment; |
No suitable driver found for jdbc:mysql://... |
mysql-connector-java.jar没放到lib目录 |
检查lib目录下是否有mysql-connector-java-8.0.33.jar |
下载对应版本jar,放入lib,并确认MANIFEST.MF里Class-Path包含它 |
Could not create connection to database server |
MySQL 8.0+默认加密方式不兼容 | mysql -u root -p -e "SELECT host,user,plugin FROM mysql.user;" |
若plugin是caching_sha2_password,执行ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456'; FLUSH PRIVILEGES; |
实操心得:我见过最多的情况是,同学在
DBUtil.java里把URL写成jdbc:mysql://127.0.0.1:3306/...,而MySQL只监听localhost。解决方案:统一用localhost,或在MySQL配置文件my.cnf里加bind-address = 0.0.0.0。记住,localhost和127.0.0.1在MySQL里是两个不同的host,前者走socket,后者走TCP。
5.2 界面中文乱码?字符集是罪魁祸首
Swing界面出现“???”或方块,99%是字符集问题。根源在三个地方:
-
数据库层面:
studentassignment数据库、所有表、所有VARCHAR字段,必须是utf8mb4。检查命令:sql SHOW CREATE DATABASE studentassignment; SHOW CREATE TABLE student;
如果显示utf8,执行:sql ALTER DATABASE studentassignment CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; ALTER TABLE student CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -
JDBC连接URL:
DBUtil.java里的URL必须包含?useUnicode=true&characterEncoding=utf8mb4。完整示例:java private static final String URL = "jdbc:mysql://localhost:3306/studentassignment?useUnicode=true&characterEncoding=utf8mb4&useSSL=false&serverTimezone=UTC"; -
IDE编码设置:IntelliJ IDEA:
File → Settings → Editor → File Encodings,将Global Encoding、Project Encoding、Default encoding for properties files 全部设为UTF-8。Eclipse:Window → Preferences → General → Workspace → Text file encoding,选Other: UTF-8。
注意:
utf8mb4不是utf8!MySQL的utf8是阉割版,只支持3字节字符,无法存储emoji和部分生僻汉字。utf8mb4才是真正的UTF-8。这个细节,是区分“能跑”和“专业”的分水岭。
5.3 功能正常但性能慢?优化这三处热点
系统在数据量小的时候飞快,但当你导入1000+课题后,可能会感觉卡顿。性能瓶颈通常在这:
-
课题列表加载慢:
TopicDao.getPublishedTopics()方法默认查所有字段SELECT *。优化:只查界面需要的字段,如SELECT id, title, difficulty, current_applicants, major FROM topic ...。在Topic.java里,用@Column(name="title")注解映射,避免ResultSet.getObject()的反射开销。 -
学生登录后查询慢:
UserService.login()执行SELECT * FROM student WHERE student_id = ?。优化:给student_id字段加索引:CREATE INDEX idx_student_id ON student(student_id);。 -
分配时匹配慢:
matchingEngine.match()算法是O(n*m)复杂度。当学生和课题都超1000时,会明显延迟。解决方案:在TopicDao.findAvailableTopics()里,加WHERE current_applicants < max_capacity过滤,大幅减少候选课题数量。实测下来,课题池从2000减到200,分配时间从8秒降到0.5秒。
5.4 想扩展功能?这三个方向最稳妥
作为毕设,加功能不是越多越好,而是要“小切口,深挖掘”。我推荐这三个经过验证的方向:
-
导出Excel报表:用Apache POI,给管理员增加“导出选题统计”按钮。核心代码就三行:
java XSSFWorkbook workbook = new XSSFWorkbook(); XSSFSheet sheet = workbook.createSheet("选题统计"); sheet.createRow(0).createCell(0).setCellValue("学生姓名"); // ... 填充数据 FileOutputStream out = new FileOutputStream("report.xlsx"); workbook.write(out);
这个功能工作量小(半天搞定),但演示效果极佳,导师一眼看到“数据可导出”,会觉得你考虑周全。 -
增加邮箱字段与简单邮件通知:在
student表加email字段,在AssignmentService.allocateTopics()末尾,加一个EmailSender.sendNotification(student.getEmail(), "您的选题已分配:《"+topic.getTitle()+"》")。用JavaMail API,配一个163邮箱SMTP(smtp.163.com:465),50行代码搞定。这展示了你对“系统集成”的理解。 -
权限分级细化:把现在的
admin角色拆成super_admin(管全局)和dept_admin(只管本院系)。修改UserService.login(),根据登录用户查department,然后在AdminMainFrame.java里,动态加载该院系的课题和学生。这体现了你对“RBAC模型”的实践,是答辩加分项。
最后分享一个小技巧:每次改完代码,务必运行
mvn clean compile(如果用了Maven)或手动javac -cp "lib/*" src/ui/LoginFrame.java,确认编译通过。我见过太多同学,改了Topic.java的getter,却忘了重新编译,导致运行时NoSuchMethodError,白白浪费两小时。养成“改-编-测”三步闭环的习惯,是专业开发者的起点。
这个系统,它不性感,不前沿,但它像一把磨得锃亮的瑞士军刀,每一个齿刃都精准对应高校教务的一个真实切口。当你在答辩现场,从容地从登录、选题、审核到分配,全程演示下来,导师问“如果学生网络断了,数据会不会丢?”,你能指着JDBC事务说“不会,因为所有操作在一个Connection里原子执行”,那一刻,你交付的就不仅仅是一个毕设,而是一份沉甸甸的、可信赖的工程素养。
简介:一个开箱即用的本地毕业设计选题分配系统,用Java Swing开发,通过JDBC直连本地MySQL数据库,支持学生自主选题、教师发布课题、管理员审核与批量分配、选题状态实时查询等功能。项目结构清晰:src目录包含全部带注释的Java源码,lib目录集成所需依赖,studentassignment.sql脚本可一键导入初始化数据,配套提供Word和Markdown双格式数据库设计文档,详细说明学生表、教师表、课题表、分配记录表等字段含义、主外键关系及业务规则(如每人限选1题、教师出题数上限等)。运行只需JDK 8或更高版本 + 本地MySQL服务,不依赖Tomcat或其他Web容器,适合高校课程设计实践、毕业设计参考或教学演示场景。代码模块划分明确,界面使用标准Swing组件构建,无第三方UI框架,便于理解底层逻辑、调试修改或拓展功能,比如增加导出Excel、邮件通知或权限分级。
更多推荐

所有评论(0)