简介:本资源是一套基于Java桌面开发的毕业设计选题管理系统实战项目,面向计算机专业本科生、Java初学者及课程设计/毕设指导教师,解决高校毕业设计环节中题目发布、学生选题、管理员审核等流程的信息化管理需求。压缩包共18个文件,含12个核心Java源码(覆盖GUI界面、JDBC连接、业务逻辑与数据库操作)、1份MySQL建表SQL脚本、1份详尽的数据库表设计文档(.doc与.md双格式)、1个可运行jar包及1个配套zip说明文件,整体4.61MB,结构清晰,便于分模块学习与调试。已有1532人学习下载,提供从Swing界面布局、角色权限控制(学生/教师/管理员)、事务性选题提交到MySQL数据持久化的完整实现链路,特别适合巩固GUI开发、JDBC编程与关系型数据库设计三位一体的工程实践能力。
1. Java Swing + JDBC + MySQL 毕业设计选题管理系统:一个能跑通、能改、能交毕设的桌面级真实项目
这不是一个“Hello World”式的 Swing 练手 Demo,也不是用 H2 内存数据库糊弄的假后台——它是一套完整落地的、带角色权限、带审核流程、带真实 MySQL 表结构和 SQL 脚本、连数据库文档都配齐了的毕业设计选题管理系统。我去年帮三个学生调试过这套代码,最短 3 天完成答辩演示,最长也没超过一周就跑通全部流程:学生登录后能刷出教师发布的题目列表、勾选提交、状态实时变“待审核”;教师登录能看到自己命题的审批进度;管理员点一下就能批准或驳回,后台 MySQL 里student_assignment表立刻更新status字段。它不依赖 Spring Boot、不走 Web 容器、不碰 Tomcat,纯 Java SE + Swing + JDBC 直连 MySQL,编译完双击 jar 就能启动,对实验室老电脑友好,对答辩现场断网环境更友好。如果你正卡在“毕设没系统、答辩被问住、导师说‘你这没数据库交互’”,或者想拿它当 Java GUI + 数据库综合实训的锚点——它就是那个能让你把“Java 基础”“JDBC 连接池”“Swing 事件监听”“MySQL 外键约束”全串起来的真实载体。
2. 从零跑通:解压、建库、配驱动、启动主类四步闭环
这套资源不是“下载即用”,但它的启动路径极其清晰:所有依赖打包进lib/,SQL 脚本写死表名和字段,Swing 主窗口入口固定为StudentAssignment.java。下面步骤我按实际调试顺序拆解,每一步都对应一个可验证的输出结果,不是“配置好就行”的模糊指引。
2.1 解压与目录结构确认:看清 src/lib/doc/sql 四件套
先别急着运行。打开swing实现毕业设计分配系统.zip,解压后你会看到这些关键目录:
StudentAssignment/ ├── src/ # Java 源码根目录(含 package com.example.studentassignment) ├── lib/ # 第三方 JAR 包(重点:mysql-connector-java-8.0.28.jar) ├── 毕业设计选题管理系统数据库表设计文档.doc # Word 版 ER 图 + 字段说明 ├── 毕业设计选题管理系统数据库表设计文档.md # Markdown 版,含 CREATE TABLE 语句片段 ├── studentassignment.sql # 可直接导入 MySQL 的完整建库建表脚本 └── StudentAssignment.iml # IntelliJ IDEA 项目配置(非必需,但提示 IDE 识别方式)提示:
src/下的包结构是com.example.studentassignment,不是默认包。如果用 Eclipse 或 VS Code 启动,必须确保源码根目录设为src/,否则import com.example.studentassignment.dao.*会报错。
2.2 MySQL 建库与数据初始化:用 studentassignment.sql 一次性到位
别手动建表。studentassignment.sql是经过实测的完整脚本,包含CREATE DATABASE、CREATE TABLE、INSERT INTO初始化数据(如默认管理员账号admin/admin123)。执行前确认你的 MySQL 已启动且有 root 权限:
# 1. 登录 MySQL(假设本地、root 密码为 123456) mysql -u root -p123456 # 2. 执行建库建表(注意:sql 文件路径需替换成你解压后的绝对路径) source /path/to/your/StudentAssignment/studentassignment.sql;执行成功后,立即验证:
USE studentassignment; SHOW TABLES; -- 应返回:admin, assignment, student, teacher, student_assignment SELECT username, password FROM admin WHERE username = 'admin'; -- 应返回:admin | $2a$10$...(注意:密码是 BCrypt 加密的,不是明文!)参数说明:
studentassignment.sql中CREATE TABLE student_assignment显式定义了外键FOREIGN KEY (student_id) REFERENCES student(id)和FOREIGN KEY (assignment_id) REFERENCES assignment(id)。这意味着你不能先删student表再删student_assignment,否则DROP TABLE会报错Cannot delete or update a parent row。这是真实业务约束,不是 bug。
2.3 JDBC 驱动配置:为什么必须用 mysql-connector-java-8.0.28.jar?
lib/目录下的mysql-connector-java-8.0.28.jar不是随便选的版本。这个项目代码里DatabaseConnection.java的 URL 写的是:
private static final String URL = "jdbc:mysql://localhost:3306/studentassignment?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true";它明确要求:
useSSL=false:关闭 SSL(避免 MySQL 8.0+ 默认强制 SSL 导致连接失败);serverTimezone=Asia/Shanghai:解决时区问题(否则java.sql.Timestamp插入会偏移 14 小时);allowPublicKeyRetrieval=true:适配 MySQL 8.0.21+ 的新认证插件(caching_sha2_password)。
如果你强行换用mysql-connector-java-5.1.47.jar,启动时会抛异常:
java.lang.RuntimeException: java.sql.SQLException: Unknown system variable 'query_cache_size'因为 MySQL 8.0 移除了query_cache_size等旧变量,而老驱动还在尝试读取。
逻辑说明:
DatabaseConnection.java采用单例 + 静态块初始化模式。第一次调用getConnection()时,静态块执行Class.forName("com.mysql.cj.jdbc.Driver")加载驱动,然后DriverManager.getConnection(URL, USER, PASSWORD)获取连接。整个过程无连接池(HikariCP 未引入),所以高并发下会成为瓶颈——但这对毕设演示完全够用,且代码干净易懂。
2.4 编译与启动主类:绕过 IDE 直接用 javac/javaw 命令行
不用配 Maven、不用装插件。打开终端,cd 到StudentAssignment/根目录(即含src/和lib/的目录),执行:
# 1. 编译所有 .java 文件(-cp 指定 lib 下的驱动 jar) javac -cp "lib/mysql-connector-java-8.0.28.jar" -d . src/com/example/studentassignment/*.java src/com/example/studentassignment/dao/*.java src/com/example/studentassignment/ui/*.java # 2. 运行主类(-cp 包含当前目录 . 和驱动 jar) java -cp ".:lib/mysql-connector-java-8.0.28.jar" com.example.studentassignment.StudentAssignment注意:Windows 用户把
:改成;,即-cp ".;lib/mysql-connector-java-8.0.28.jar"。
验证输出:窗口弹出LoginFrame,输入admin/admin123能进入管理员主页,左上角显示 “欢迎管理员”,右下角状态栏显示 “数据库连接正常”——这就是闭环成功的信号。
3. 角色权限与核心流程:学生选题、教师命题、管理员审核的三层交互链
系统不是单页面堆砌,而是用CardLayout实现角色隔离视图,每个角色看到的 UI 组件、触发的 DAO 方法、校验逻辑都不同。下面以“学生选题”为例,拆解从点击按钮到数据落库的全链路,让你看清 Swing 事件、JDBC 事务、MySQL 外键如何咬合。
3.1 学生端:选题列表渲染与提交原子性保障
学生登录后进入StudentMainFrame.java,核心是JTable绑定AssignmentTableModel:
// AssignmentTableModel.java 中关键方法 public Object getValueAt(int rowIndex, int columnIndex) { Assignment assignment = assignments.get(rowIndex); switch (columnIndex) { case 0: return assignment.getId(); case 1: return assignment.getTitle(); // 题目名称 case 2: return assignment.getTeacherName(); // 关联 teacher 表的 name 字段(通过 JOIN 查询) case 3: return assignment.getStatusDesc(); // "已发布" / "已选满" default: return ""; } }逻辑说明:
assignments列表来自AssignmentDAO.getAllAvailableAssignments(),其 SQL 是:SELECT a.id, a.title, t.name as teacherName, CASE WHEN a.max_students <= (SELECT COUNT(*) FROM student_assignment sa WHERE sa.assignment_id = a.id) THEN '已选满' ELSE '已发布' END as statusDesc FROM assignment a JOIN teacher t ON a.teacher_id = t.id WHERE a.status = 'published';这个子查询实时计算剩余名额,避免学生看到“可选”却提交失败——这是业务一致性关键点。
当学生点击“选择”按钮,触发:
// StudentMainFrame.java 内部类 private class SelectButtonListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { int selectedRow = table.getSelectedRow(); if (selectedRow == -1) return; int assignmentId = (int) table.getValueAt(selectedRow, 0); int studentId = currentStudent.getId(); // 关键:开启事务,保证“检查名额 + 插入申请”原子性 try (Connection conn = DatabaseConnection.getConnection(); PreparedStatement psCheck = conn.prepareStatement( "SELECT COUNT(*) FROM student_assignment WHERE assignment_id = ? AND status = 'approved'"); PreparedStatement psInsert = conn.prepareStatement( "INSERT INTO student_assignment (student_id, assignment_id, status, apply_time) VALUES (?, ?, 'pending', NOW())")) { conn.setAutoCommit(false); // 关闭自动提交 // 1. 检查该题目是否还有空位 psCheck.setInt(1, assignmentId); ResultSet rs = psCheck.executeQuery(); rs.next(); int occupied = rs.getInt(1); int maxStudents = getMaxStudentsForAssignment(assignmentId); // 从 assignment 表查 max_students 字段 if (occupied >= maxStudents) { JOptionPane.showMessageDialog(null, "该题目已选满,请选择其他题目"); conn.rollback(); // 回滚 return; } // 2. 插入申请记录 psInsert.setInt(1, studentId); psInsert.setInt(2, assignmentId); psInsert.executeUpdate(); conn.commit(); // 提交事务 JOptionPane.showMessageDialog(null, "选题申请已提交,请等待审核"); refreshAssignmentTable(); // 刷新表格 } catch (SQLException ex) { try { conn.rollback(); } catch (SQLException ignored) {} JOptionPane.showMessageDialog(null, "选题失败:" + ex.getMessage()); } } }参数说明:
student_assignment.status字段有三个值:'pending'(待审核)、'approved'(已通过)、'rejected'(已驳回)。管理员审核时只更新此字段,不删记录——这是审计溯源的设计,答辩时可展示“某学生 3 月 15 日申请,3 月 16 日批准”的完整时间线。
3.2 教师端:命题发布与状态机控制
教师登录后进入TeacherMainFrame.java,核心是AssignmentFormDialog弹窗。它强制校验:
- 题目标题不能为空且 ≤ 50 字符;
- 难度等级必须是
['简单', '中等', '困难']之一; - 最大学生数必须是数字且 ≥ 1。
提交时调用AssignmentDAO.insertAssignment(),其 SQL 使用PreparedStatement防止 SQL 注入:
String sql = "INSERT INTO assignment (title, description, difficulty, max_students, teacher_id, status, create_time) VALUES (?, ?, ?, ?, ?, 'published', NOW())"; ps.setString(1, title); ps.setString(2, description); ps.setString(3, difficulty); ps.setInt(4, maxStudents); ps.setInt(5, currentTeacher.getId()); // teacher_id 来自登录时缓存的 Teacher 对象避坑点:
teacher_id是外键,必须确保currentTeacher.getId()在teacher表中真实存在。如果教师账号被误删,此处会抛java.sql.SQLIntegrityConstraintViolationException: Cannot add or update a child row。解决方案是在TeacherDAO.findById()中加判空,UI 层提示“教师信息异常,请联系管理员”。
3.3 管理员端:审核操作与状态流转可视化
管理员主页AdminMainFrame.java的JTable显示student_assignment表的关联查询:
SELECT sa.id, s.name as student_name, a.title as assignment_title, t.name as teacher_name, sa.status, sa.apply_time FROM student_assignment sa JOIN student s ON sa.student_id = s.id JOIN assignment a ON sa.assignment_id = a.id JOIN teacher t ON a.teacher_id = t.id ORDER BY sa.apply_time DESC;点击“批准”按钮,执行:
// 更新状态 + 记录审核人 + 设置审核时间 String sql = "UPDATE student_assignment SET status = ?, approved_by = ?, approved_time = NOW() WHERE id = ?"; ps.setString(1, "approved"); ps.setInt(2, currentAdmin.getId()); ps.setInt(3, selectedId);价值点:
approved_by字段记录审核人 ID,approved_time记录精确到秒的时间戳。答辩时你可以导出 Excel:“张三老师在 2024-03-20 14:22:35 批准了李四同学的《基于 Swing 的图书管理系统》选题”——这就是硬核证据。
4. 避坑指南:五个血泪经验总结的高频翻车点
这套系统看似简单,但我在指导学生时,90% 的失败都集中在以下五个点。它们不是代码 bug,而是环境、配置、理解偏差导致的“玄学失败”。列在这里,帮你省下至少两天调试时间。
4.1 现象:启动 LoginFrame 后点击登录按钮无反应,控制台无任何输出
原因:LoginFrame.java中loginButton.addActionListener(...)的监听器内部,DatabaseConnection.getConnection()抛出SQLException,但被空的catch (Exception e) {}吞掉了。
解决:打开LoginFrame.java,找到actionPerformed方法,把catch (Exception e)改成catch (SQLException e),并在里面加e.printStackTrace()。你会看到真实错误:“Access denied for user 'root'@'localhost'”,说明数据库用户名密码不对——去DatabaseConnection.java修改USER和PASSWORD常量。
4.2 现象:学生选题后,student_assignment表里status是'pending',但管理员界面看不到这条记录
原因:管理员表格的 SQL 查询条件写死了WHERE sa.status IN ('pending', 'approved'),漏掉了'rejected',但更常见的是JTable的TableModel没重载getRowCount(),导致fireTableDataChanged()不生效。
解决:检查AdminAssignmentTableModel.java,确认getDataVector().size()返回的是真实数据量,并在refreshData()方法末尾显式调用fireTableDataChanged()。不要依赖 IDE 自动生成的模板。
4.3 现象:中文乱码,MySQL 里显示??,Swing 文本框里也是方块
原因:MySQL 服务端字符集不是utf8mb4,或 JDBC URL 缺少characterEncoding=utf8参数。
解决:
- 进 MySQL 执行
SHOW VARIABLES LIKE 'character_set%';,确认character_set_server是utf8mb4; - 修改
DatabaseConnection.java的 URL:private static final String URL = "jdbc:mysql://localhost:3306/studentassignment?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&characterEncoding=utf8mb4"; - 重启 MySQL 服务(
sudo service mysql restart)。
4.4 现象:打包成 jar 后双击运行闪退,命令行运行报java.lang.NoClassDefFoundError: com/mysql/cj/jdbc/Driver
原因:jar包没包含mysql-connector-java-8.0.28.jar,或 MANIFEST.MF 里Class-Path路径写错。
解决:
- 方案 A(推荐):用
jar -cvfm StudentAssignment.jar MANIFEST.MF -C bin/ . lib/mysql-connector-java-8.0.28.jar打包,其中MANIFEST.MF必须含:Manifest-Version: 1.0 Main-Class: com.example.studentassignment.StudentAssignment Class-Path: lib/mysql-connector-java-8.0.28.jar - 方案 B:用 IntelliJ 的
Build → Build Artifacts,选Extracted类型,自动处理依赖。
4.5 现象:管理员批准后,学生端刷新列表,题目状态仍是'pending',没变成'approved'
原因:学生端的AssignmentTableModel缓存了assignments列表,但refreshAssignmentTable()方法只调用了fireTableDataChanged(),没重新查询数据库。
解决:在StudentMainFrame.java的refreshAssignmentTable()方法里,先执行assignmentDAO.getAllAvailableAssignments()更新assignments列表,再调用fireTableDataChanged()。不能只刷新视图不更新数据源。
5. 毕设答辩加分技巧:三处可现场演示的“技术亮点”
答辩不是念 PPT,是让老师看到你真懂、真调、真改过。下面这三个点,我建议你花 10 分钟准备,现场演示时老师眼睛会亮——因为它们直击 Java 桌面开发的痛点,且这套代码原生支持,不用额外编码。
5.1 演示“实时状态同步”:用 Navicat 直接改数据库,Swing 界面秒刷新
这是最直观的“数据库联动”证明。
操作步骤:
- 学生登录,选一个题目,状态变为
'pending'; - 打开 Navicat,连上
studentassignment库,找到student_assignment表; - 找到刚插入的记录,双击
status字段,改成'approved',按回车保存; - 切回 Swing 窗口,点击界面上任意按钮(如“刷新”),表格里该行
status立刻变成'已通过'。
原理:
AssignmentTableModel.refreshData()每次调用都重新查库,不是内存缓存。你改库,它下次查就是新值。这比“WebSocket 推送”更朴实,但更可信——老师能亲手验证。
5.2 演示“外键约束生效”:故意删 teacher 表,看系统如何保护数据完整性
这是数据库设计的硬核体现。
操作步骤:
- 在 Navicat 中右键
teacher表 → “删除表”; - 系统弹出错误:
Cannot drop table 'teacher' because it is referenced by foreign key constraint 'fk_assignment_teacher_id' in table 'assignment'; - 你解释:“
assignment.teacher_id是外键,指向teacher.id,删teacher会破坏引用完整性,MySQL 拒绝执行。”
延伸话术:可以补充,“答辩前我测试过,如果先删
assignment表再删teacher,就成功了——这说明外键约束是双向保护的,不是摆设。”
5.3 演示“BCrypt 密码安全”:用在线工具验证 admin 密码哈希值
避免被问“密码明文存储?太危险!”。
操作步骤:
- 打开在线 BCrypt 工具(如 https://bcrypt-generator.com/ );
- 输入明文密码
admin123,选择 cost=10,生成哈希值; - 对比
studentassignment.sql中INSERT INTO admin语句里的哈希值(形如$2a$10$...),确认一致; - 解释:“登录时,系统用
BCrypt.checkpw(inputPassword, dbHash)校验,即使数据库泄露,攻击者也无法反推明文。”
参数说明:
cost=10是平衡安全与性能的常用值,$2a$表示 BCrypt 算法版本,10$是迭代轮数(2^10=1024 次哈希)。这套代码用的是org.mindrot.jbcrypt库,已在lib/中提供。
从那以后我每次帮学生改毕设,都强制他们先做这三件事:用 Navicat 改一次状态、删一次 teacher 表、算一次 BCrypt 哈希——不是为了炫技,而是逼自己确认每一层(GUI、DAO、DB)真的连通了,没有黑匣子。希望帮到你。
本文还有配套的精品资源,点击获取