☰
Swing+JDBC+MySQL构建毕业设计选题管理系统实战解析
2026/10/14 15:36:00 网站建设 项目流程

简介:面向高校计算机专业学生及毕业设计开发者的Java桌面应用项目,基于Swing构建图形界面,通过JDBC连接MySQL实现学生选题、教师命题、管理员审核、查询统计等完整业务流程。资源共18个文件,包含12个Java源码、SQL建表脚本、JAR依赖包,以及doc和md格式的数据库表设计文档,压缩包整体约4.61MB,结构清晰便于直接导入IDE运行和二次开发。已有1532人浏览学习,适合作为课程设计或毕业设计的参考模板。通过这份资料可以掌握Swing组件布局、JDBC增删改查、MySQL表关系设计等关键技能,同时了解用户角色权限控制、选题审核流程等业务逻辑的实现思路。附带的设计文档能帮助快速理解数据库结构,源码中注释与命名规范也便于阅读和扩展。

1. Swing 毕业设计选题管理系统:一套能直接跑通选题全流程的桌面端代码包

如果你还在用 Excel 收集毕业设计题目、靠聊天记录确认选题结果,那这套基于 Java Swing + JDBC + MySQL 的选题管理系统值得你花半小时看完。它不依赖 Tomcat、不涉及前后端分离,就是一个典型的 C/S 架构桌面应用:Swing 做界面、JDBC 做数据库桥梁、MySQL 做数据落盘,学生、教师、管理员三类角色在同一个客户端里完成命题、选题、审核的完整闭环。特别适合正在做 Java 课程设计、需要快速交付一个能演示的选题模块,或者想学习桌面应用分层写法的开发者。下面我从项目结构、建库脚本、核心代码、角色流程和踩坑记录五个维度拆一遍,最后补充测试数据和安全性这两件容易被忽略的事。

2. 项目结构与技术选型:先把文件清单和关键依赖理清楚

2.1 为什么这种项目适合用 Swing + JDBC 而不是 Web 方案

很多同学一上来就纠结:现在都做 Web 端,为什么还要用 Swing 写桌面程序?我的看法是,选题管理系统这种内部工具,数据量不大、并发不高、使用场景集中在实验室和教务办公室,用 Swing 反而省事。不需要配置 Maven 镜像、不需要处理跨域、不需要部署服务器,双击运行主类就能进入登录窗口。JDBC 是 Java 访问数据库最底层的标准接口,相比 MyBatis 和 Hibernate,它能让你看清楚每条 SQL 是怎么执行的,连接怎么打开、怎么关闭、结果集怎么遍历,这些对理解数据访问层的原理帮助很大。这个资源包里没有引入任何 ORM 框架,所有数据库操作都是裸 JDBC 代码,正好可以看清楚一个真实的 Java 桌面项目是如何组织这些代码的。项目的核心是业务流转,不是炫技,Swing 加 JDBC 的搭配刚好覆盖需求又不显得臃肿。

从学习的角度来看,这个项目的价值在于它把界面、业务、数据访问拆成了三层。界面代码集中在视图部分,业务逻辑独立成方法,数据库操作封装在专门的类里。即使你以后转向 Web 开发,这种分层思想依然适用。从交付的角度来看,Swing 程序打包成 JAR 后可以在任何装了 JDK 的 Windows 电脑上运行,对演示环境要求低,这也是很多课程设计和中小型内部系统仍然采用这种技术栈的原因。

2.2 资源包里的文件各自承担什么职责

拿到压缩包后先别急着导入 IDE,按下面的清单检查一遍:

文件/目录作用使用阶段
StudentAssignment.imlIDEA 模块配置文件导入项目时识别模块结构
srcJava 源码目录阅读和修改的核心区域
lib第三方依赖 jar 包构建运行前必须引入
studentassignment.sql数据库建库建表脚本初始化数据库
数据库表设计文档.md/doc表结构说明理解业务表关系
毕业设计选题管理系统.pdf项目配套说明了解整体设计思路

lib 目录是整个项目的命脉。这个项目使用纯 JDBC 方式连接 MySQL,因此必须依赖 MySQL 的 JDBC 驱动包,通常是一个 mysql-connector-java 版本的 JAR,将其添加到项目的类路径之后,Class.forName 才能正确加载驱动。如果导入项目后运行报错提示找不到驱动类,先检查这个 JAR 是否在 lib 目录下,以及编译器输出路径是否包含了它。

数据库设计文档值得先读一遍。文档里通常包含用户表、题目表、选题记录表三张核心表的字段定义、主外键关系和状态字段的取值约定。状态字段是这类系统的灵魂,题目有“未选/已选/待审核”,选题申请有“待审核/通过/驳回”,不弄清楚这些状态值,后面读代码容易卡住。

3. 从 SQL 脚本到 JDBC 连接:初始化数据库并跑通第一个查询

3.1 使用 studentassignment.sql 完成建库建表

打开 MySQL 客户端,执行以下命令创建数据库:

source /your/path/studentassignment.sql;

执行完成后,用下面的语句确认核心表是否创建成功:

SHOW TABLES; DESC t_user; DESC t_subject; DESC t_selection;

如果脚本执行时报错,最常见的原因是脚本开头没有指定默认数据库。标准的做法是先手动创建库,再切换到对应库下执行脚本,或者修改脚本头部,添加 CREATE DATABASE 和 USE 语句后重新执行。

建表脚本是这套系统的地基。学生、教师、管理员共用一张用户表,通过角色字段区分身份;题目表存储教师发布的命题,包含题目名称、描述、类型、难度等级、可选人数上限等字段;选题记录表存储学生提交的选题申请,包含学生编号、题目编号、申请时间、审核状态等关键信息。外键约束能够防止孤儿数据的产生,但也会在删除某些记录时带来额外限制。设计文档里对这些字段做了详细说明,理解了这些字段后,再去看代码里的 SQL 语句就会顺畅很多。

3.2 修改 JDBC 连接参数并验证数据库驱动

在源码中找到负责数据库连接的类,通常叫 DBUtil 或 DatabaseConnection,核心代码的框架如下:

// DBUtil.java 数据库连接工具类 public class DBUtil { private static final String DRIVER = "com.mysql.cj.jdbc.Driver"; private static final String URL = "jdbc:mysql://localhost:3306/student_assignment?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&characterEncoding=utf-8"; private static final String USERNAME = "root"; private static final String PASSWORD = "123456"; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USERNAME, PASSWORD); } }

以上代码设置了几个关键参数:useSSL=false 用于跳过 SSL 握手验证,提高连接速度;serverTimezone=Asia/Shanghai 是为了解决驱动 8.x 版本连接老版本 MySQL 时容易出现的时区报错;allowPublicKeyRetrieval=true 则专门解决 MySQL 8.0 使用 caching_sha2_password 加密插件时的认证失败问题;characterEncoding=utf-8 保证中文写入数据库不出现乱码。

修改完连接参数后,直接运行一个简单的查询就能验证整条链路是否已经打通:

// TestConnection.java 验证数据库是否连通 public class TestConnection { public static void main(String[] args) { try { Connection conn = DBUtil.getConnection(); System.out.println("连接成功:" + conn); conn.close(); } catch (SQLException e) { System.out.println("连接失败:" + e.getMessage()); } } }

如果看到打印出连接对象信息,说明驱动加载、URL、账号密码都没问题;如果抛出的异常是 Communications link failure 或 Access denied for user,则需要检查 MySQL 服务是否启动、账号密码是否正确、端口是否被占用等因素。管理员账号的密码如果修改过,记得同步更新工具类里的配置。

3.3 登录窗口与角色权限判断

连接数据库成功后,启动类会加载登录窗口。登录逻辑是根据输入的用户名去查询用户表,拿到记录后比对密码字段:

// LoginService.java 登录校验逻辑 public User login(String username, String password) { String sql = "SELECT id, username, password, role FROM t_user WHERE username = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, username); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { String dbPassword = rs.getString("password"); if (dbPassword.equals(password)) { return new User(rs.getInt("id"), username, dbPassword, rs.getString("role")); } } } } catch (SQLException e) { e.printStackTrace(); } return null; }

这里使用 PreparedStatement 传参而不是手动拼接 SQL 字符串,目的就是防止 SQL 注入。登录成功后,系统会根据用户角色字段动态实例化不同的主窗口。学生窗口显示的是可选题目列表和提交申请按钮,教师窗口是命题管理和查看选题情况,管理员窗口是审核列表和账号管理。三个角色共用同一个数据库连接工具,界面层通过判断当前登录用户来渲染相应的菜单。

4. 核心流程与界面跳转:从登录到选题申请如何落地

4.1 学生端:浏览题目列表与提交选题申请

学生登录后看到的第一个页面就是可选题目列表。这个列表用 Swing 的 JTable 组件呈现,表格的列通常包括题目编号、题目名称、指导教师、题目类型、难度等级、当前已选人数和可选人数上限。已选人数达到上限的题目在表格中被标记为不可选状态,防止超额提交。

// SubjectService.java 查询可选题目列表 public List<Subject> getAvailableSubjects() { List<Subject> list = new ArrayList<>(); String sql = "SELECT id, title, teacher_name, type, difficulty, selected_count, max_count " + "FROM t_subject WHERE status = 1 AND selected_count < max_count"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery()) { while (rs.next()) { // 将结果集每一行转换为 Subject 对象,加入 list } } catch (SQLException e) { e.printStackTrace(); } return list; }

表格刷新后,JTable 的数据模型通过 setModel 方法来更新数据。Student 点击“提交选题”按钮时,系统会做三步校验:

// SelectionService.java 提交选题申请 public boolean submitSelection(int studentId, int subjectId) { // 1. 检查该学生是否已经提交过申请 // 2. 检查题目人数是否已满 // 3. 插入一条申请记录,状态为待审核 }

每一步都有一个独立的查询或更新语句,任何一步不满足条件都会弹出提示窗口告诉学生为什么无法提交。学生提交申请之后,在学生端的主窗口里会显示当前的申请状态,可能是待审核、通过或驳回。被驳回的同学可以重新选择其他题目,整体流程形成了一个闭环:教师出题、学生选课、教师确认、管理员监督。

4.2 教师端:发布题目与查看选题情况

教师登录后的主界面里,命题管理相关操作被放在一个单独的面板中。发布题目时,界面通常是一组表单:

// TeacherAction.java 教师发布题目的业务方法 public boolean publishSubject(Subject subject) { String sql = "INSERT INTO t_subject (title, description, type, difficulty, max_count, teacher_name, status) " + "VALUES (?, ?, ?, ?, ?, ?, 1)"; // 使用 DBUtil 获取连接,PreparedStatement 设置参数 // 执行 executeUpdate,返回影响行数大于 0 表示成功 }

SQL 语句中使用status = 1来表示题目处于开放状态,0 表示关闭状态或删除状态。教师只能看到自己发布的题目,因此查询语句的 WHERE 子句要加入 teacher_name 作为过滤条件。教师点击某一条题目时,可以查看所有选择该题目的学生列表和他们的申请状态,如果有学生被驳回,该学生的选择权会自动释放,剩余名额也会恢复。

4.3 管理员端:审核选题申请与系统管理

管理员端的审核逻辑是这个系统里唯一涉及事务处理的地方。事务是保证数据一致性的关键:管理员点击通过某个学生的选题申请时,系统需要同时完成两个操作——把学生申请记录的状态由待审核改为通过,并将对应题目的已选人数加一。这两个操作要么全部成功,要么全部失败。而管理员驳回某个选题时,一般只是把状态改为驳回,不改变已选人数。但如果驳回的是之前已经审核通过的学生,该学生的选择状态被重置为未选,题目容量也要回滚,这时就需要事务操作来保证数据的完整一致。代码实现的关键结构如下:

// AdminService.java 审核通过选题申请 public boolean approveSelection(int selectionId, int subjectId) { Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 关闭自动提交 // 1. 更新选题记录状态为已通过 // 2. 更新题目已选人数 + 1 conn.commit(); // 事务提交 return true; } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } e.printStackTrace(); return false; } }

这里最关键的一个步骤就是 conn.setAutoCommit(false) 关闭自动提交。如果不关闭,每条 SQL 执行完就会立即生效,一旦第二步失败,就会造成“申请状态改了但人数没加上”的数据不一致问题,这在毕业设计这种人数容量敏感的系统中是不能接受的。

管理员账号还承担着重置学生密码、管理教师账号、查看选题统计报表的职能。统计功能一般直接通过 SQL 的聚合函数来实现,比如按题目类型分组统计各个方向的选题人数,或者统计每个教师的命题数量,聚合结果可以在表格或者饼图中展示出来。

5. 避坑与常见问题:导入项目到跑通全流程的踩坑记录

5.1 编码混乱导致中文乱码

导入项目后,连接数据库执行查询,返回的中文题目名称全是乱码。这个问题通常有三处需要同时检查。首先检查数据库表字段的字符集是否为 utf8mb4,然后在 JDBC 连接字符串中加上 characterEncoding=utf-8 参数,最后检查 IDE 的默认编码是否为 UTF-8。有时候只改了其中一处,问题依旧存在。一个服务器端、一个客户端、一个连接层,三层都统一后,中文显示才能恢复正常。这类问题网上讨论很多,但实际排查时往往需要反序检查,比如先看数据库端的字符集,再看连接参数,最后看 IDE 的编码设置。

5.2 MySQL 8.x 驱动连接时报 Public Key Retrieval 错误

使用较新版本的驱动连接 MySQL 8.0 时,如果启用了 caching_sha2_password 加密插件,会抛出 Public Key Retrieval is not allowed 异常。解决办法是在连接参数中显式添加 allowPublicKeyRetrieval=true,并确保 useSSL=false,因为该选项要求禁用 SSL。如果不想改连接代码,也可以把 MySQL 的默认认证插件改回 mysql_native_password,修改后需要重启 MySQL 服务才能生效。对于这个项目来说,直接改连接参数是最省事的方案,也是我实际使用中觉得最可靠的做法。

5.3 驱动 JAR 未正确引入导致运行时报 ClassNotFoundException

在 IDE 中启动主类时,如果直接报 java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver,说明驱动 JAR 没有被添加到项目的类路径中。IDEA 中需要右键 lib 目录下的 JAR 文件,选择 Add as Library,确认后重新运行即可。有人把驱动 JAR 放在了源码目录里,虽然文件出现在项目树中,但没有被识别为依赖库,同样会触发这个异常。不同版本的驱动类名也可能不同,老版本是 com.mysql.jdbc.Driver,新版本是 com.mysql.cj.jdbc.Driver,如果项目里用到的是老驱动,这个点上需要特别注意,两者不能混用。

5.4 修改数据库密码后忘记同步代码

开发和测试会频繁修改 MySQL 密码,一旦数据库密码修改了,而 DBUtil 类中的密码还是旧值,就会出现登录窗口完全无法连接数据库的情况。这类问题不算疑难杂症,但很容易反复出现,每改一次数据库密码,就要同步修改 DBUtil 中的密码配置。建议把数据库连接参数集中放在配置文件中,而不是散落在多个类的常量里。如果这个项目把连接参数写死在 DBUtil 里,那修改时只需改动一处即可。有些项目存在多个数据库工具类,改漏一个就会出现某个功能连接成功、另一个失败的现象。

5.5 JTable 刷新后界面仍然显示旧数据

很多同学在提交选题或审核通过后,调用数据更新方法操作了数据库,但表格界面没有任何变化。原因是只更新了数据库,没有重新调用表格的数据刷新方法来重绘界面。正确做法是在数据变更成功后重新执行一次查询并重新设置 TableModel。此外,Swing 是单线程模型,所有界面更新都要在事件分发线程上完成,如果直接在普通线程中修改表格数据,会出现界面不同步的问题。最简单的处理方式是在业务方法执行完毕后,再调用一次刷新列表的方法来同步显示。

6. 附带的增删改查与安全加固:让系统更稳定也更可信

6.1 用测试数据验证选题容量与状态流转的逻辑是否符合预期

studentassignment.sql 脚本里通常包含了一批测试数据,分别模拟了三类账号和若干条题目记录的状态。这些数据虽然看起来只是几条 INSERT 语句,但它们对验证系统逻辑有实际价值。比如用学生账号尝试重复选题时,系统应当弹出“你已经选择过题目”的提示,而不是插入第二条记录;用教师账号发布题目时,难度等级字段只能接受预设值,不能随便填一个数字进去。这些异常路径都是需要在测试阶段重点验证的,测试数据设置得好,验证效率也会更高。

6.2 用 PreparedStatement 防止 SQL 注入

SQL 注入是这类老式 Java 系统中最容易被忽视的安全漏洞。原始做法是直接用字符串拼接 SQL 查询语句,如"SELECT * FROM t_user WHERE username = '" + username + "'",如果用户在用户名输入框中输入' OR '1'='1,查询条件就会被恒真表达式绕过,从而以他人身份登录系统。项目代码中统一使用 PreparedStatement 进行参数占位符传参,实际执行时,参数值会被单独处理和转义,不会被解析成 SQL 语言的一部分,这样用户输入里即使包含单引号或 SQL 关键字,也会被当作普通字符串处理,不会改变查询语义。拿这个项目练手时,可以额外搜索一下 PreparedStatement 与 Statement 在使用方式上的区别,这能帮助你理解参数化查询在实际场景中的价值。

6.3 用事务保证选题人数与申请状态的一致性

前文提到管理员审核通过时需要同时更新选题记录的状态和题目容量,这两个操作必须要么同时成功、要么同时失败。这个系统使用 MySQL 的 InnoDB 引擎来保证事务支持,在事务中执行多条 SQL 语句时,一个比较实用的方法是:先查一遍数据,再决定下一步操作,中间用事务把查询结果和后续动作绑定起来。比如提交选题申请时,先查当前人数,再插入记录,如果这两个操作之间刚好有其他人也提交了同一道题,就可能出现人数超限的情况。可以通过给题目表加锁或使用条件更新语句来避免这种差异。这个系统里的表使用的是 InnoDB 且开启了事务,因此出现差异的概率相对低,但仍然值得注意。

6.4 最后的检查:把数据库脚本、文档和代码包整理成完整交付物

代码全部测试通过后,我习惯再把 studentassignment.sql 重新导入到一个全新的空库中,验证脚本能否一次性彻底执行。这一步能排查掉只在自己电脑上碰巧能跑、换个环境就报错的问题。数据库设计文档也要同步检查一遍,确认里面的字段名与实际建表脚本的一致性;如果后来新增过题目类型或状态字段,文档忘了更新,会给后续接手的人留下隐患。资源包里附带的说明文档这时就派上用场了,可以按照里面的步骤从零开始跑一遍完整流程,确认没有遗漏环节。

从那以后我每次拿到类似的课程设计代码包,都会强制自己完整走一遍从导入项目到重新建库的流程,再抽几个核心方法把 SQL 语句打开仔细看一遍,从根上规避“换台电脑就翻车”的情况。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询