ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Java Swing + MySQL 运动会管理系统:课程设计实战与避坑指南

Java Swing + MySQL 运动会管理系统:课程设计实战与避坑指南 简介这是一套面向高校计算机与数据库课程学习者的Java课程设计资源以田径运动会管理系统为完整案例适合正在准备数据库课设、Java实训或需要参考Swing桌面应用开发的学生。系统基于Java Swing与MySQL实现覆盖运动员、参赛团体、比赛安排与裁判信息的录入查询支持按编号或姓名模糊检索并能统计团体金牌数量、关联查询运动员个人信息与上轮成绩录入全能赛成绩后自动计算总分并排序同时对数据合法性、查询无结果及数据库连接异常给出提示。资源包共74个文件约2.2MB包含12个java源码、48个class编译文件、5个xml配置、1个sql建库脚本以及课程报告docx文档另附jar依赖与项目配置文件结构完整可直接导入IDEA或Eclipse配合MySQL5.7以上运行。目前已有188人学习配套报告与源码便于快速理解需求分析、表结构设计与功能实现思路。1. 从一份课程设计说起Java Swing MySQL 运动会管理系统到底解决什么问题每年一到期末计算机专业的学生和刚转行的开发者都会撞上同一类需求做一个带界面的信息管理系统要有增删改查、要有数据库、最好还能附一份课程报告。运动会管理系统就是其中最典型的选题之一——它比学生管理系统多了一层「赛事」逻辑比图书管理多了一层「成绩」计算既不会简单到没东西写也不会复杂到做不完。用 Java Swing 做桌面端、MySQL 做数据存储是这类课程设计里最稳的技术组合Swing 是 JDK 自带的不用配前端构建工具MySQL 生态成熟mysql安装教程8.0、mysql安装配置教程这类搜索能直接解决环境问题而 Java 本身又是java课程设计案例源码里出现频率最高的语言。这套系统真正要解决的核心问题有三个第一把运动员、项目、报名、成绩这四类数据用关系表管起来避免用 Excel 手工统计时反复出现的重名、漏报、成绩算错第二用 Swing 做一个能实际点得动的界面让「报名」「录入成绩」「查排名」这些操作不依赖命令行第三把整个开发过程整理成课程报告讲清楚需求分析、E-R 图、表结构设计和关键代码。适合读这篇的人很明确正在做 Java 课程设计的学生、想拿一个完整桌面项目练手的 Java 初学者以及需要一套可复用 CRUD 模板的开发者。下面我按「先立住数据模型再打通界面最后处理坑」的顺序把这条路走一遍。2. 数据模型先立住运动会管理系统的表结构与 MySQL 建库脚本很多人做课程设计时习惯先拖界面结果做到一半发现字段不够、外键对不上回头改代码改到崩溃。血泪经验是先把 E-R 关系和建表脚本定死界面只是数据的投影。运动会管理系统的实体并不复杂核心就四张表——运动员、比赛项目、报名记录、成绩记录。其中报名和成绩是典型的「多对多桥接表」一个运动员可以报多个项目一个项目也可以有多名运动员。2.1 四张核心表的关系与字段设计先把关系理清楚athlete运动员和event比赛项目是多对多靠registration报名连接registration再和result成绩一对一或一对多关联取决于你要不要记录预赛、决赛多轮成绩。字段设计上运动员表要有学号/工号做唯一标识项目表要有项目类型田赛/径赛和计分方式时间越短越好还是距离越远越好这一点直接决定后面排名怎么排。表名关键字段说明athleteid, sno, name, gender, dept, phonesno 唯一dept 用于按院系统计eventid, name, type, unit, sort_ruletype 区分田赛/径赛sort_rule 决定升序还是降序排名registrationid, athlete_id, event_id, reg_time联合唯一约束防止重复报名resultid, reg_id, score, rank_no, record_timescore 存原始成绩rank_no 可计算后回写sort_rule这个字段是很多人会漏掉的。径赛比如 100 米成绩越小越好田赛比如跳远成绩越大越好如果不把这个规则存进数据库排名逻辑就得写死在代码里换个项目就要改代码。我一般用1表示升序越小越好2表示降序越大越好。2.2 可直接执行的建库建表脚本下面这段 SQL 可以直接在 MySQL 8.0 里跑注意字符集用utf8mb4否则运动员姓名里的生僻字会变问号。执行前先确认 MySQL 服务已经起来mysql安装教程里常见的error 2002 (hy000)基本都是服务没启动或 socket 路径不对。-- 创建数据库字符集必须用 utf8mb4 CREATE DATABASE sports_meet DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE sports_meet; -- 运动员表 CREATE TABLE athlete ( id INT PRIMARY KEY AUTO_INCREMENT, sno VARCHAR(20) NOT NULL UNIQUE COMMENT 学号/工号, name VARCHAR(50) NOT NULL, gender CHAR(1) DEFAULT M COMMENT M男 F女, dept VARCHAR(50), phone VARCHAR(20) ) ENGINEInnoDB; -- 比赛项目表 CREATE TABLE event ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, type VARCHAR(10) COMMENT track田赛 field径赛, unit VARCHAR(10) COMMENT 秒/米/分, sort_rule TINYINT DEFAULT 1 COMMENT 1升序 2降序 ) ENGINEInnoDB; -- 报名表联合唯一防止重复报名 CREATE TABLE registration ( id INT PRIMARY KEY AUTO_INCREMENT, athlete_id INT NOT NULL, event_id INT NOT NULL, reg_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_ath_event (athlete_id, event_id), FOREIGN KEY (athlete_id) REFERENCES athlete(id), FOREIGN KEY (event_id) REFERENCES event(id) ) ENGINEInnoDB; -- 成绩表 CREATE TABLE result ( id INT PRIMARY KEY AUTO_INCREMENT, reg_id INT NOT NULL, score DECIMAL(10,2), rank_no INT DEFAULT 0, record_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (reg_id) REFERENCES registration(id) ) ENGINEInnoDB;脚本里几个参数值得单独说DECIMAL(10,2)而不是FLOAT是因为成绩要参与排名比较浮点误差可能导致两个本该并列的成绩分出先后uk_ath_event联合唯一索引把「同一人重复报同一项目」这种脏数据挡在数据库层比在 Java 里写 if 判断可靠得多外键约束保证删运动员时不会留下孤儿报名记录。建完表后建议插几条测试数据用mysql workbench使用教程里教的图形化方式核对一下字段类型比纯命令行直观。2.3 JDBC 连接与连接池的最小配置Swing 是单机桌面程序连接数不多但每次操作都DriverManager.getConnection会很慢而且容易忘记关闭导致连接泄漏。常见做法是引一个轻量连接池比如 HikariCP或者自己写一个简单的单例连接管理。下面这个工具类是课程设计里够用的最小版本注意 JDBC URL 里的时区和 SSL 参数——MySQL 8.0 默认要求 SSL本地开发可以显式关掉否则会报mysql jdbc usessl 与 sslmode相关的连接错误。public class DBUtil { // MySQL 8.0 驱动类名5.x 是 com.mysql.jdbc.Driver private static final String URL jdbc:mysql://localhost:3306/sports_meet?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8; private static final String USER root; private static final String PWD 你的密码; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new RuntimeException(MySQL 驱动未加载, e); } } public static Connection getConn() throws SQLException { return DriverManager.getConnection(URL, USER, PWD); } // 统一关闭避免连接泄漏 public static void close(AutoCloseable... cs) { for (AutoCloseable c : cs) { if (c ! null) try { c.close(); } catch (Exception ignored) {} } } }serverTimezone不写会报时区错误characterEncodingutf8不写中文会乱码这两个是新手最常翻车的地方。useSSLfalse只用于本地开发生产环境要按实际证书配置。连接池如果要用把getConn换成从HikariDataSource取即可其余代码不用动。3. Swing 界面怎么搭从主窗口到报名、成绩录入的完整交互数据层通了之后界面就是把这些表「翻译」成用户能点的东西。Swing 的坑在于布局管理器和事件线程——很多人写出来的窗口一拉伸就乱、点按钮卡死根因都在这里。这一章把主框架、表格绑定、成绩排名三块讲透。3.1 主窗口布局与 JTable 绑定数据主窗口我一般用BorderLayout打底北边放标题和菜单栏西边放功能导航运动员管理、项目管理、报名、成绩中间放JTable展示数据。JTable必须配DefaultTableModel否则数据更新后界面不刷新。下面是一个运动员管理面板的最小实现包含查询和表格填充。public class AthletePanel extends JPanel { private JTable table; private DefaultTableModel model; private JTextField keywordField; public AthletePanel() { setLayout(new BorderLayout()); // 顶部查询栏 JPanel top new JPanel(new FlowLayout(FlowLayout.LEFT)); keywordField new JTextField(12); JButton searchBtn new JButton(查询); top.add(new JLabel(姓名/学号:)); top.add(keywordField); top.add(searchBtn); add(top, BorderLayout.NORTH); // 表格模型列名固定 model new DefaultTableModel( new Object[]{ID, 学号, 姓名, 性别, 院系, 电话}, 0) { Override public boolean isCellEditable(int r, int c) { return false; } }; table new JTable(model); add(new JScrollPane(table), BorderLayout.CENTER); searchBtn.addActionListener(e - loadData(keywordField.getText().trim())); loadData(); } private void loadData(String kw) { model.setRowCount(0); // 清空旧数据 String sql SELECT id,sno,name,gender,dept,phone FROM athlete WHERE name LIKE ? OR sno LIKE ?; try (Connection conn DBUtil.getConn(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, % kw %); ps.setString(2, % kw %); ResultSet rs ps.executeQuery(); while (rs.next()) { model.addRow(new Object[]{ rs.getInt(id), rs.getString(sno), rs.getString(name), rs.getString(gender), rs.getString(dept), rs.getString(phone) }); } } catch (SQLException ex) { JOptionPane.showMessageDialog(this, 查询失败: ex.getMessage()); } } }isCellEditable返回false是为了防止用户直接在表格里改数据却不落库造成界面和数据库不一致——这是很隐蔽的 bug。PreparedStatement用?占位不仅防注入还能让 MySQL 复用执行计划。查询用LIKE加前后通配符空关键字时等价于查全部。3.2 报名与成绩录入的事件处理报名按钮的逻辑要处理「重复报名」和「项目已满」两种情况。重复报名靠数据库唯一索引兜底捕获SQLIntegrityConstraintViolationException后提示用户即可比先查后插更可靠并发下先查后插仍有窗口。成绩录入则要注意录入后要立刻重算该项目所有成绩的排名并回写rank_no否则排名永远是旧的。// 报名直接插入靠唯一索引防重 public boolean register(int athleteId, int eventId) { String sql INSERT INTO registration(athlete_id, event_id) VALUES(?,?); try (Connection conn DBUtil.getConn(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, athleteId); ps.setInt(2, eventId); ps.executeUpdate(); return true; } catch (SQLIntegrityConstraintViolationException dup) { JOptionPane.showMessageDialog(null, 该运动员已报名此项目); return false; } catch (SQLException e) { JOptionPane.showMessageDialog(null, 报名失败: e.getMessage()); return false; } }排名重算用一条UPDATE配合窗口函数MySQL 8.0 支持就能搞定不用在 Java 里循环。sort_rule决定ORDER BY的方向这里用CASE WHEN动态切换避免写两条 SQL。-- 重算某项目排名sort_rule1 升序2 降序 UPDATE result r JOIN ( SELECT r2.id, ROW_NUMBER() OVER ( ORDER BY CASE WHEN e.sort_rule 1 THEN r2.score END ASC, CASE WHEN e.sort_rule 2 THEN r2.score END DESC ) AS rk FROM result r2 JOIN registration reg ON r2.reg_id reg.id JOIN event e ON reg.event_id e.id WHERE reg.event_id ? ) t ON r.id t.id SET r.rank_no t.rk;ROW_NUMBER()保证名次连续不跳号如果允许并列可以用RANK()。CASE WHEN里两个分支只有一个会生效另一个为NULLMySQL 排序时NULL不影响有效分支的顺序。这条 SQL 在成绩录入后调用一次即可参数是项目 ID。3.3 用 SwingWorker 避免界面卡死Swing 的所有界面更新都必须在事件分发线程EDT里做而数据库查询是耗时操作。如果在按钮回调里直接查库数据量大时窗口会「假死」这是 Swing 最经典的玄学问题。正确做法是把查询放进SwingWorker的doInBackground结果在done里回填表格。new SwingWorkerListObject[], Void() { Override protected ListObject[] doInBackground() throws Exception { ListObject[] rows new ArrayList(); // 这里执行耗时查询不在 EDT try (Connection conn DBUtil.getConn(); PreparedStatement ps conn.prepareStatement(SELECT * FROM athlete)) { ResultSet rs ps.executeQuery(); while (rs.next()) { rows.add(new Object[]{rs.getInt(id), rs.getString(name)}); } } return rows; } Override protected void done() { try { model.setRowCount(0); for (Object[] row : get()) model.addRow(row); } catch (Exception e) { e.printStackTrace(); } } }.execute();doInBackground在后台线程跑done自动回到 EDT这样界面既不卡也不会抛「不在 EDT 更新组件」的异常。记住一条只要操作可能超过 100 毫秒就丢给SwingWorker。4. 避坑与排查课程设计里最容易翻车的五个地方这一章全是踩坑记录每条按「现象 → 原因 → 解决」写都是我在做这类系统时真实遇到过的。现象一程序启动报No suitable driver found for jdbc:mysql。原因通常是 MySQL 驱动 jar 没加进项目或者加了但版本和 URL 不匹配5.x 驱动连 8.0 库。解决确认mysql-connector-j的 jar 在 classpath 里8.0 用com.mysql.cj.jdbc.DriverURL 带serverTimezone。现象二中文姓名存进数据库变成???。原因是建库时字符集用了latin1或utf8三字节生僻字存不下。解决库、表、连接三处统一utf8mb4JDBC URL 加characterEncodingutf8。现象三点查询按钮界面卡住几秒无响应。原因是在 EDT 里直接查库。解决按 3.3 用SwingWorker或者至少把查询放独立线程再SwingUtilities.invokeLater回填。现象四成绩排名每次都要手动刷新才对。原因是录入成绩后没重算rank_no。解决录入成功后立即执行 3.2 里的排名重算 SQL或者干脆不存rank_no查询时用窗口函数实时算。现象五删除运动员后报名记录还在查成绩报空指针。原因是外键没设级联或删除时没清理子表。解决外键加ON DELETE CASCADE或者在 Java 里先删registration再删athlete用事务包起来。提示课程报告里把这几条坑写进「测试与问题分析」章节比只贴成功截图更能体现工作量答辩时也有的聊。5. 进阶与验证把课程报告写扎实的两个技巧系统能跑起来只是及格线课程报告要拿高分得让评审看到你「想过边界」。第一个技巧是给关键操作加事务。报名和成绩录入如果涉及多表写入一定要用conn.setAutoCommit(false)包起来失败时rollback否则中途出错会留下半截数据。第二个技巧是准备一份可复现的测试数据脚本把 20 个运动员、5 个项目、若干报名和成绩一次性插进去这样演示时不用现场手敲也方便验证排名逻辑对不对。验证排名是否正确最直接的办法是手工算一遍再和系统对比。比如男子 100 米sort_rule1成绩 12.5、11.8、13.1 三人系统排名应该是 11.8 第一、12.5 第二、13.1 第三。如果系统给出反的顺序八成是sort_rule读错了或者CASE WHEN写反了。我一般会在result表里故意插一条score为NULL的记录看系统会不会崩——能优雅跳过空成绩的代码才算真正处理了边界。报告的结构建议按「需求分析 → 系统设计E-R 图、表结构→ 详细实现关键代码 说明→ 测试与问题分析 → 不足与改进」来写其中「不足与改进」别写套话写具体的比如「当前排名用ROW_NUMBER不支持并列后续可换RANK」「界面未做权限区分管理员和普通用户共用一套菜单」。这些具体点比「系统还有待完善」有说服力得多。最后说个我自己的习惯每做完一个模块先把数据库里的数据用SELECT捞出来看一眼确认落库正确再写下一个界面。我早期图快界面全写完才联调结果一个字段类型对不上改了整整一下午。先数据后界面这个顺序别省。希望帮到你。本文还有配套的精品资源点击获取
返回列表