☰
MySQL+Java+Swing宿舍管理系统实战:从数据库设计到桌面应用开发
2026/10/9 6:00:35 网站建设 项目流程

简介:这份资源是面向高校计算机相关专业学生的MySQL课程设计参考方案,以Java Swing为界面框架、MySQL为后台数据库,实现了一套学生宿舍管理系统。适合正在完成数据库课程设计、Java桌面应用实训或毕业设计初稿的读者,可帮助理解从需求建模、表结构设计到界面交互的完整实现链路。压缩包共334个文件,约108.12MB,其中47个java源文件承载核心业务逻辑,202个class为编译产物,另有22个png界面素材、9个xml配置及若干工程与构建文件,并附带db数据库文件与jar依赖,便于直接导入运行与二次修改。资源围绕管理员管理、宿舍管理、学生管理、学生宿舍分配等模块展开,包含多组管理组件与接口类,结构清晰,可作为课程设计模板参考,也能用于学习Swing事件处理、JDBC连接与增删改查的落地写法。目前已有192人学习下载。

1. 从一份“能跑起来”的宿舍管理系统说起:MySQL+Java+Swing 到底能做出什么

很多同学做课程设计时,第一反应是去搜“学生宿舍管理系统 源代码”,下载下来发现要么跑不起来,要么数据库对不上,要么界面丑得没法演示。我当年第一次做这个题目,也是从一堆压缩包里翻出来的代码,结果 MySQL 连不上、Swing 界面乱码、增删改查全是坑。后来自己从头搭了一遍,才明白这套技术栈真正的价值:它不追求高并发、不追求分布式,而是用最直接的方式把“数据库设计 + 桌面端交互 + 业务逻辑”串起来,让你在两周内交出一个能演示、能答辩、能写进简历的完整项目。

MySQL 负责数据持久化,Java 负责业务逻辑,Swing 负责桌面交互,三者组合起来就是一个典型的 C/S 架构桌面应用。它适合谁?适合正在学 Java 基础、刚接触 JDBC、需要完成课程设计或毕业设计的同学,也适合想用一个小项目把“面向对象编程 Java”和“MySQL 数据库常用命令”串起来练手的开发者。你不需要会 Spring Boot,也不需要懂前端框架,只要会写类、会写 SQL,就能把这个系统跑起来。接下来我会按“数据库怎么建 → 后端怎么连 → 界面怎么画 → 坑怎么避”的顺序,把每个环节的参数和代码都摊开讲。

2. 数据库设计:从宿舍表到入住记录,MySQL 建表与初始化数据

2.1 先想清楚实体关系,再动手写 CREATE TABLE

宿舍管理系统的核心实体其实就四个:学生、宿舍楼、房间、入住记录。很多同学一上来就建一张大表,把所有字段塞进去,结果查询时各种冗余和更新异常。正确的做法是先画 ER 图,再转成关系表。我一般会这样设计:

  • student表:学号、姓名、性别、班级、联系电话
  • dorm_building表:楼号、楼层数、管理员
  • dorm_room表:房间号、所属楼号、床位数、已住人数
  • check_in表:记录 ID、学号、房间号、入住日期、退房日期

其中check_in是关联表,用来解决学生和房间的多对多关系。下面是我常用的建表 SQL,直接可以在 MySQL 5.7 或 8.0 里执行:

-- 创建数据库,字符集用 utf8mb4 避免中文乱码 CREATE DATABASE dorm_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE dorm_db; -- 学生表 CREATE TABLE student ( student_id VARCHAR(20) PRIMARY KEY COMMENT '学号', name VARCHAR(50) NOT NULL COMMENT '姓名', gender ENUM('男','女') DEFAULT '男' COMMENT '性别', class_name VARCHAR(50) COMMENT '班级', phone VARCHAR(20) COMMENT '联系电话' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 宿舍楼表 CREATE TABLE dorm_building ( building_id VARCHAR(10) PRIMARY KEY COMMENT '楼号', floors INT NOT NULL COMMENT '楼层数', manager VARCHAR(50) COMMENT '管理员' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 房间表 CREATE TABLE dorm_room ( room_id VARCHAR(10) PRIMARY KEY COMMENT '房间号', building_id VARCHAR(10) COMMENT '所属楼号', bed_count INT DEFAULT 4 COMMENT '床位数', occupied_count INT DEFAULT 0 COMMENT '已住人数', FOREIGN KEY (building_id) REFERENCES dorm_building(building_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 入住记录表 CREATE TABLE check_in ( id INT AUTO_INCREMENT PRIMARY KEY, student_id VARCHAR(20), room_id VARCHAR(10), check_in_date DATE, check_out_date DATE, FOREIGN KEY (student_id) REFERENCES student(student_id), FOREIGN KEY (room_id) REFERENCES dorm_room(room_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这段 SQL 里几个关键点:utf8mb4是必须的,否则中文姓名和班级会变成问号;ENUM类型适合性别这种固定选项;外键约束保证入住记录不会引用不存在的学生或房间。如果你用的是 MySQL 8.0,utf8mb4_general_ci可以换成utf8mb4_0900_ai_ci,性能更好。

2.2 初始化数据与常用查询:让系统一启动就有东西可看

建完表之后,插入一些测试数据,方便调试界面。我通常会插 3 栋楼、每栋楼 5 个房间、10 个学生和对应的入住记录:

INSERT INTO dorm_building VALUES ('A', 6, '张老师'), ('B', 6, '李老师'), ('C', 4, '王老师'); INSERT INTO dorm_room VALUES ('A101', 'A', 4, 2), ('A102', 'A', 4, 0), ('A103', 'A', 4, 1), ('B201', 'B', 4, 3), ('B202', 'B', 4, 0), ('C301', 'C', 6, 2); INSERT INTO student VALUES ('2021001', '张三', '男', '计科2101', '13800000001'), ('2021002', '李四', '女', '软工2102', '13800000002'), ('2021003', '王五', '男', '计科2101', '13800000003'); INSERT INTO check_in (student_id, room_id, check_in_date) VALUES ('2021001', 'A101', '2023-09-01'), ('2021002', 'A101', '2023-09-01'), ('2021003', 'A103', '2023-09-02');

日常开发中最常用的查询是“查某个房间住了哪些人”和“查某个学生住哪个房间”。前者用 JOIN:

SELECT s.name, s.student_id, c.check_in_date FROM check_in c JOIN student s ON c.student_id = s.student_id WHERE c.room_id = 'A101' AND c.check_out_date IS NULL;

后者反过来查就行。这里注意check_out_date IS NULL表示当前仍在住,退房后要更新这个字段而不是删除记录,方便查历史。很多同学直接 DELETE,结果答辩时被问“怎么查退房记录”就卡住了。

提示:MySQL 安装时如果选的是 8.0,默认认证插件是caching_sha2_password,老版本的 JDBC 驱动可能连不上。解决办法是在 MySQL 里执行ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码';,或者升级 JDBC 驱动到 8.0 以上。

3. Java 后端:用 JDBC 把 MySQL 和 Swing 串起来

3.1 环境配置与 JDBC 连接:三个必须改的参数

在写代码之前,先把环境搭好。你需要 JDK 8 或 11(Swing 对高版本 JDK 兼容性还行,但 8 最稳)、MySQL 5.7 或 8.0、以及 MySQL 的 JDBC 驱动包mysql-connector-java-8.0.xx.jar。在 IDEA 或 Eclipse 里新建项目,把 jar 包加到 classpath 里。如果你用 Maven,加这个依赖:

<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency>

连接数据库的工具类我一般写成单例,避免每次操作都重新连接。核心是三个参数:URL、用户名、密码。URL 里要指定时区和字符集:

import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBUtil { private static final String URL = "jdbc:mysql://localhost:3306/dorm_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"; private static final String USER = "root"; private static final String PASSWORD = "你的密码"; static { try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }

serverTimezone=Asia/Shanghai是必须的,否则 MySQL 8.0 会报时区错误;useSSL=false在本地开发时关掉,避免证书警告;characterEncoding=utf8保证中文不乱码。如果你用的是 MySQL 5.7,驱动类名可以写com.mysql.jdbc.Driver,但 8.0 的驱动兼容 5.7,所以统一用com.mysql.cj.jdbc.Driver就行。

3.2 学生管理模块的增删改查:从 DAO 到 Swing 表格

后端逻辑我习惯按 DAO 模式写,每个实体一个 DAO 类。以学生为例,先写一个Student实体类,字段和数据库一一对应,然后写StudentDao,里面放增删改查方法。查询所有学生的方法返回List<Student>,Swing 的JTable可以直接用这个列表填充:

import java.sql.*; import java.util.ArrayList; import java.util.List; public class StudentDao { public List<Student> findAll() { List<Student> list = new ArrayList<>(); String sql = "SELECT * FROM student"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery()) { while (rs.next()) { Student s = new Student(); s.setStudentId(rs.getString("student_id")); s.setName(rs.getString("name")); s.setGender(rs.getString("gender")); s.setClassName(rs.getString("class_name")); s.setPhone(rs.getString("phone")); list.add(s); } } catch (SQLException e) { e.printStackTrace(); } return list; } public boolean add(Student s) { String sql = "INSERT INTO student VALUES (?, ?, ?, ?, ?)"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, s.getStudentId()); ps.setString(2, s.getName()); ps.setString(3, s.getGender()); ps.setString(4, s.getClassName()); ps.setString(5, s.getPhone()); return ps.executeUpdate() > 0; } catch (SQLException e) { e.printStackTrace(); return false; } } public boolean delete(String studentId) { String sql = "DELETE FROM student WHERE student_id = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, studentId); return ps.executeUpdate() > 0; } catch (SQLException e) { e.printStackTrace(); return false; } } }

这里用PreparedStatement而不是Statement,是为了防止 SQL 注入,也避免拼接字符串时引号出错。try-with-resources自动关闭连接,不用手动写finally。Swing 那边,用一个DefaultTableModel把List<Student>转成表格数据:

String[] columnNames = {"学号", "姓名", "性别", "班级", "电话"}; DefaultTableModel model = new DefaultTableModel(columnNames, 0); List<Student> students = studentDao.findAll(); for (Student s : students) { model.addRow(new Object[]{s.getStudentId(), s.getName(), s.getGender(), s.getClassName(), s.getPhone()}); } JTable table = new JTable(model);

这样界面一打开就能看到所有学生,点“删除”按钮时拿到选中行的学号,调studentDao.delete(),然后刷新表格。整个流程很直白,没有框架的“黑匣子”,每一步都能调试。

3.3 入住与退房:事务处理与界面联动

入住和退房涉及两张表:check_in插入记录,dorm_room更新occupied_count。这两个操作必须在一个事务里,否则可能出现“记录插入了但房间人数没变”的脏数据。我一般这样写:

public boolean checkIn(String studentId, String roomId) { String sql1 = "INSERT INTO check_in (student_id, room_id, check_in_date) VALUES (?, ?, CURDATE())"; String sql2 = "UPDATE dorm_room SET occupied_count = occupied_count + 1 WHERE room_id = ? AND occupied_count < bed_count"; Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 try (PreparedStatement ps1 = conn.prepareStatement(sql1); PreparedStatement ps2 = conn.prepareStatement(sql2)) { ps1.setString(1, studentId); ps1.setString(2, roomId); ps1.executeUpdate(); ps2.setString(1, roomId); int rows = ps2.executeUpdate(); if (rows == 0) { conn.rollback(); return false; // 房间已满 } conn.commit(); return true; } } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } e.printStackTrace(); return false; } finally { if (conn != null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }

关键点是setAutoCommit(false)和rollback()。如果房间已满,UPDATE语句的WHERE条件不满足,返回 0 行,这时候回滚插入操作,保证数据一致。Swing 界面上,入住按钮点击后弹一个对话框选学生和房间,退房按钮则更新check_out_date并减少occupied_count。这些交互逻辑虽然简单,但能体现你对事务的理解,答辩时是个加分项。

4. Swing 界面:用 JFrame + JTable 做出能演示的桌面端

4.1 主界面布局:BorderLayout + JTabbedPane 分模块

Swing 的布局管理器是很多人的噩梦,但其实只要记住几个常用的就够了。主界面我用BorderLayout,中间放JTabbedPane,每个标签页是一个模块:学生管理、宿舍管理、入住退房、统计查询。顶部放一个JMenuBar,底部放状态栏。代码骨架:

import javax.swing.*; import java.awt.*; public class MainFrame extends JFrame { public MainFrame() { setTitle("学生宿舍管理系统"); setSize(900, 600); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); // 居中显示 JTabbedPane tabbedPane = new JTabbedPane(); tabbedPane.addTab("学生管理", new StudentPanel()); tabbedPane.addTab("宿舍管理", new RoomPanel()); tabbedPane.addTab("入住退房", new CheckInPanel()); tabbedPane.addTab("统计查询", new StatsPanel()); add(tabbedPane, BorderLayout.CENTER); JLabel statusBar = new JLabel(" 就绪"); add(statusBar, BorderLayout.SOUTH); } public static void main(String[] args) { SwingUtilities.invokeLater(() -> { new MainFrame().setVisible(true); }); } }

SwingUtilities.invokeLater是必须的,保证界面在事件调度线程里创建,避免线程安全问题。每个Panel继承JPanel,里面放表格、按钮、输入框。学生管理面板的布局用BorderLayout,北部放操作按钮,中部放JScrollPane包裹的JTable。

4.2 表格刷新与事件监听:让按钮真正起作用

Swing 的事件监听是匿名内部类或 Lambda 表达式。以“删除学生”为例,按钮点击后先弹确认框,再调 DAO,最后刷新表格:

JButton deleteBtn = new JButton("删除"); deleteBtn.addActionListener(e -> { int selectedRow = table.getSelectedRow(); if (selectedRow == -1) { JOptionPane.showMessageDialog(this, "请先选中一行"); return; } String studentId = (String) table.getValueAt(selectedRow, 0); int confirm = JOptionPane.showConfirmDialog(this, "确定删除学号 " + studentId + " 吗?"); if (confirm == JOptionPane.YES_OPTION) { if (studentDao.delete(studentId)) { refreshTable(); // 重新查询并填充表格 JOptionPane.showMessageDialog(this, "删除成功"); } else { JOptionPane.showMessageDialog(this, "删除失败,可能该学生有入住记录"); } } });

refreshTable()方法就是清空DefaultTableModel再重新addRow。这里有个血泪经验:如果学生表有外键关联到check_in,直接删学生会报外键约束错误。解决办法是先删入住记录,或者用ON DELETE CASCADE。我一般会在删除前查一下有没有未退房的记录,有就提示先退房。

4.3 统计查询面板:用 SQL 聚合函数做数据展示

统计查询是答辩时最容易出彩的地方。我通常做两个功能:按楼栋统计入住人数、按班级统计住宿分布。SQL 用GROUP BY和COUNT:

SELECT b.building_id, COUNT(c.id) AS student_count FROM dorm_building b LEFT JOIN dorm_room r ON b.building_id = r.building_id LEFT JOIN check_in c ON r.room_id = c.room_id AND c.check_out_date IS NULL GROUP BY b.building_id;

Swing 这边用一个只读的JTable展示结果,列名动态设置。如果想让界面好看点,可以加个JFreeChart画柱状图,但课程设计不要求,用表格就够了。注意LEFT JOIN保证没有入住记录的楼栋也显示 0,而不是被过滤掉。

5. 避坑与排查:课程设计里最容易翻车的 5 个地方

5.1 中文乱码:数据库、连接、界面三处都要设 UTF-8

现象:学生姓名显示成??或乱码。原因通常是数据库字符集不是utf8mb4,或者 JDBC URL 没加characterEncoding=utf8,或者 Swing 组件的字体不支持中文。解决:建库时指定DEFAULT CHARACTER SET utf8mb4,URL 里加characterEncoding=utf8,Swing 用默认字体一般没问题,如果乱码就设new Font("微软雅黑", Font.PLAIN, 14)。

5.2 驱动加载失败:ClassNotFoundException 的三种可能

现象:运行时报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver。原因:jar 包没加到 classpath、Maven 依赖没刷新、或者驱动类名写错(5.7 用com.mysql.jdbc.Driver)。解决:检查项目结构里的 Libraries,Maven 项目执行mvn clean install,确认 jar 包在WEB-INF/lib或模块依赖里。

5.3 时区报错:MySQL 8.0 的 serverTimezone 必须写

现象:连接时抛The server time zone value '?D1ú±ê×?ê±??' is unrecognized。原因:MySQL 8.0 默认时区是系统时区,JDBC 驱动无法识别中文时区名。解决:URL 里加serverTimezone=Asia/Shanghai,或者改成GMT%2B8。

5.4 外键约束导致删除失败:先查关联再删

现象:删除学生时提示Cannot delete or update a parent row: a foreign key constraint fails。原因:check_in表里有该学生的入住记录。解决:先删check_in里的记录,或者把外键改成ON DELETE CASCADE。我一般在前端提示“该学生有入住记录,请先退房”,避免误删。

5.5 界面卡死:数据库操作别放在事件调度线程

现象:点击查询按钮后界面无响应,过几秒才恢复。原因:ActionListener里直接调 DAO,数据库查询耗时阻塞了 EDT。解决:用SwingWorker把数据库操作放到后台线程,完成后在done()方法里更新表格。课程设计数据量小,不卡也行,但这是个好习惯。

6. 进阶技巧:把课程设计变成能写进简历的项目

如果你想让这个系统在答辩时更有说服力,可以加两个小功能:登录权限和导出 Excel。登录权限用一张user表,存用户名、密码(MD5 加密)、角色(管理员/宿管)。登录成功后根据角色禁用某些按钮。导出 Excel 用 Apache POI,把JTable的数据写成.xlsx文件,代码大概 20 行:

import org.apache.poi.xssf.usermodel.*; import java.io.FileOutputStream; public void exportToExcel(JTable table, String filePath) throws Exception { XSSFWorkbook workbook = new XSSFWorkbook(); XSSFSheet sheet = workbook.createSheet("学生信息"); XSSFRow header = sheet.createRow(0); for (int i = 0; i < table.getColumnCount(); i++) { header.createCell(i).setCellValue(table.getColumnName(i)); } for (int i = 0; i < table.getRowCount(); i++) { XSSFRow row = sheet.createRow(i + 1); for (int j = 0; j < table.getColumnCount(); j++) { row.createCell(j).setCellValue(table.getValueAt(i, j).toString()); } } try (FileOutputStream fos = new FileOutputStream(filePath)) { workbook.write(fos); } workbook.close(); }

POI 的依赖是org.apache.poi:poi-ooxml:5.2.3。导出功能在答辩时很讨喜,老师一看就知道你不仅会增删改查,还能处理文件流。另一个技巧是把数据库配置写到db.properties文件里,用Properties类加载,这样换电脑演示时不用改代码,只改配置文件就行。

验证系统是否完整,我一般跑一个 checklist:能登录、能增删改查学生、能分配房间、能退房、能统计、能导出。全部通过,这个项目就稳了。最后说个我自己的习惯:每次改完代码,先跑一遍mvn clean package或者手动编译,确认没有编译错误再调界面。数据库操作前先备份一份 SQL 文件,改坏了直接还原。这些习惯让我在 deadline 前少熬了好几个通宵。希望帮到你。

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

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

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

立即咨询