简介本资源是一份面向高校计算机专业本科生的MySQL课程设计实战项目聚焦学生宿舍管理场景完整呈现数据库设计、Java后端逻辑与Swing桌面界面的三层协同开发过程。适用于数据库原理、Java程序设计及软件工程类课程实践助力初学者掌握从E-R建模、SQL脚本编写、JDBC连接到GUI组件布局的全链路开发能力。压缩包共334个文件含47个核心Java源码如AdminManageComponent、StuManageComponent等模块类、202个编译后class文件、22张系统界面与数据库关系图PNG、9个配置与布局XML以及需求分析、ER图、数据字典、系统说明书等配套文档整体容量112.72MB。目前已有268人学习下载提供开箱即用的可运行jar包、完整IDEA/VS项目结构含sln、iml、vcxproj等、调试所需pdb/ilk等构建产物以及清晰分层的源码组织便于理解模块职责与系统调用流程。1. 学生宿舍管理系统为什么非得用 MySQL Java Swing——不是怀旧是稳、快、可控你手头正赶着一门《数据库原理与应用》或《Java 程序设计》的课程设计老师明确要求必须本地可运行、数据持久化、界面可交互、代码可调试、答辩能讲清每行逻辑。这时候翻开源码仓库看到“MySQLJavaSwing”这个组合别急着划走——它不是过时的代名词而是课程设计场景下经过千人验证的最小可行闭环方案MySQL 提供标准 SQL 语法和事务支持让 ER 图、范式设计、增删改查、多表连接这些教学重点有落点Java 承担业务逻辑编排类结构清晰、异常可捕获、调试器友好学生能真正“看见”数据从 ResultSet 流向 JTable 的全过程Swing 虽不炫酷但零依赖、纯 AWT 绘制、组件生命周期透明JFrame 启动慢JTable 刷新卡JComboBox 数据绑定错——所有问题都能在 IDE 里单步进到源码级。这不是技术选型是教学场景下的生存策略不求高并发但求每一处报错都有明确堆栈不要微服务只要一个 jar 包双击即启不拼前端动画只保毕业答辩时鼠标一点宿管员录入、学生查询、维修派单三个模块全在线。如果你的目标是交一份“能跑、能讲、能改、能延展”的作业那这套组合就是当前最省心、最抗压、最经得起老师现场提问的技术栈。2. 从零搭起系统骨架建库建表 Java 工程初始化 Swing 主窗体落地2.1 用标准 SQL 建好宿舍管理核心四张表字段命名即文档课程设计最怕“建完表就忘外键怎么连”。我们按真实宿舍管理流程反推学生入住 → 分配房间 → 房间归属楼栋 → 楼栋归属校区。对应四张表全部采用小写蛇形命名避免大小写敏感引发的跨平台问题主键统一用id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY时间字段用DATETIME不用 TIMESTAMP规避时区转换玄学-- 1. 校区表一级管理单元 CREATE TABLE campus ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL COMMENT 校区名称如东校区、西校区, address VARCHAR(200) COMMENT 详细地址 ); -- 2. 楼栋表二级管理单元关联校区 CREATE TABLE building ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, campus_id BIGINT UNSIGNED NOT NULL COMMENT 所属校区ID, name VARCHAR(30) NOT NULL COMMENT 楼栋名称如1号公寓、实验楼A座, total_floors TINYINT UNSIGNED COMMENT 总楼层数, FOREIGN KEY (campus_id) REFERENCES campus(id) ON DELETE CASCADE ); -- 3. 房间表三级管理单元关联楼栋 CREATE TABLE room ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, building_id BIGINT UNSIGNED NOT NULL COMMENT 所属楼栋ID, room_number VARCHAR(20) NOT NULL COMMENT 房间号如301、B-205, capacity TINYINT UNSIGNED DEFAULT 4 COMMENT 床位数, status ENUM(空闲,已住,维修中) DEFAULT 空闲, FOREIGN KEY (building_id) REFERENCES building(id) ON DELETE CASCADE ); -- 4. 学生表核心实体关联房间 CREATE TABLE student ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, stu_id VARCHAR(15) NOT NULL UNIQUE COMMENT 学号业务主键, name VARCHAR(20) NOT NULL, gender ENUM(男,女) NOT NULL, major VARCHAR(50), room_id BIGINT UNSIGNED COMMENT 当前入住房间ID可为空未分配, check_in_date DATE COMMENT 入住日期, FOREIGN KEY (room_id) REFERENCES room(id) ON DELETE SET NULL );提示ON DELETE CASCADE和ON DELETE SET NULL不是炫技。课程设计答辩常被问“如果删除一栋楼学生数据怎么处理”——这里直接给出答案楼栋删了其下房间自动删学生 room_id 自动置空。比口头解释强十倍。2.2 创建 Maven 工程并引入三类关键依赖驱动、连接池、UI 基础用 IntelliJ IDEA 新建 Maven 项目pom.xml中只保留最精简依赖避免 Spring Boot 等重量级框架干扰教学本质dependencies !-- MySQL JDBC 驱动必须用 8.x 版本适配 MySQL 5.7 -- dependency groupIdmysql/groupId artifactIdmysql-connector-j/artifactId version8.0.33/version /dependency !-- HikariCP 连接池轻量、高性能、默认配置即开即用 -- dependency groupIdcom.zaxxer/groupId artifactIdHikariCP/artifactId version5.0.1/version /dependency !-- 日志门面SLF4J Simple 实现不引入 logback 复杂配置 -- dependency groupIdorg.slf4j/groupId artifactIdslf4j-simple/artifactId version2.0.9/version /dependency /dependencies参数说明HikariCP 的maximumPoolSize10是安全值课程设计并发不会超 5 人同时操作connectionTimeout3000030秒防网络抖动假死MySQL 驱动useSSLfalseserverTimezoneAsia/Shanghai必加否则 Windows 下中文乱码时区报错双杀。2.3 写出第一个可运行的 Swing 主窗体JFrame BorderLayout 菜单栏不搞复杂布局用最直白的BorderLayout搭骨架菜单栏放四个功能入口后续扩展不改结构public class DormitoryMainFrame extends JFrame { public DormitoryMainFrame() { setTitle(学生宿舍管理系统 v1.0); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout()); // 顶部菜单栏 JMenuBar menuBar new JMenuBar(); JMenu studentMenu new JMenu(学生管理); JMenu roomMenu new JMenu(房间管理); JMenu reportMenu new JMenu(统计报表); JMenu helpMenu new JMenu(帮助); studentMenu.add(new JMenuItem(新增学生)); studentMenu.add(new JMenuItem(查询学生)); roomMenu.add(new JMenuItem(房间状态查看)); reportMenu.add(new JMenuItem(空床位统计)); menuBar.add(studentMenu); menuBar.add(roomMenu); menuBar.add(reportMenu); menuBar.add(helpMenu); setJMenuBar(menuBar); // 中央占位面板后续替换为具体功能面板 JPanel centerPanel new JPanel(new BorderLayout()); centerPanel.add(new JLabel(欢迎使用宿舍管理系统, JLabel.CENTER), BorderLayout.CENTER); add(centerPanel, BorderLayout.CENTER); pack(); setLocationRelativeTo(null); // 居中显示 setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(() - new DormitoryMainFrame()); } }逻辑说明SwingUtilities.invokeLater()是 Swing 线程安全铁律漏写会导致界面卡死或组件不渲染pack()比setSize(800,600)更可靠——它根据内容自动计算最佳尺寸避免 Linux 下字体缩放导致按钮被截断。3. 让数据真正流动起来JDBC 封装 DAO 分层 Swing 表格双向绑定3.1 封装 HikariCP 数据源单例 配置外置杜绝硬编码把数据库连接信息从代码里抽出来新建src/main/resources/db.propertiesjdbc.urljdbc:mysql://localhost:3306/dormitory_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8 jdbc.usernameroot jdbc.password123456 jdbc.pool.maxSize10 jdbc.pool.timeout30000再写一个线程安全的DataSourceFactorypublic class DataSourceFactory { private static volatile HikariDataSource dataSource; public static HikariDataSource getDataSource() { if (dataSource null) { synchronized (DataSourceFactory.class) { if (dataSource null) { HikariConfig config new HikariConfig(db.properties); config.setDriverClassName(com.mysql.cj.jdbc.Driver); dataSource new HikariDataSource(config); } } } return dataSource; } }为什么不用 static final因为课程设计后期可能要切换 SQLite 做离线演示getDataSource()方法留出扩展口子比final static更教学友好。3.2 编写 StudentDAO用 PreparedStatement 防注入用 try-with-resources 保资源释放DAO 层只做一件事把 SQL 和 Java 对象来回转。Student实体类字段严格对齐student表public class Student { private Long id; private String stuId; // 学号 private String name; private String gender; private String major; private Long roomId; // 关联房间ID private LocalDate checkInDate; // getter/setter 省略IDE 自动生成 }StudentDAO的核心方法——带条件分页查询答辩高频考点public class StudentDAO { private final DataSource dataSource DataSourceFactory.getDataSource(); public ListStudent findStudents(String keyword, int page, int pageSize) throws SQLException { String sql SELECT s.id, s.stu_id, s.name, s.gender, s.major, s.room_id, s.check_in_date, r.room_number, b.name as building_name FROM student s LEFT JOIN room r ON s.room_id r.id LEFT JOIN building b ON r.building_id b.id WHERE s.stu_id LIKE ? OR s.name LIKE ? ORDER BY s.id DESC LIMIT ? OFFSET ? ; ListStudent students new ArrayList(); try (Connection conn dataSource.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, % keyword %); ps.setString(2, % keyword %); ps.setInt(3, pageSize); ps.setInt(4, (page - 1) * pageSize); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Student s new Student(); s.setId(rs.getLong(id)); s.setStuId(rs.getString(stu_id)); s.setName(rs.getString(name)); s.setGender(rs.getString(gender)); s.setMajor(rs.getString(major)); s.setRoomId(rs.getLong(room_id)); s.setCheckInDate(rs.getObject(check_in_date, LocalDate.class)); // 关联字段存入临时属性供表格显示用 s.setRoomNumber(rs.getString(room_number)); s.setBuildingName(rs.getString(building_name)); students.add(s); } } } return students; } }参数说明LIMIT ? OFFSET ?是 MySQL 分页标准写法OFFSET从 0 开始所以第一页传page1计算为(1-1)*100rs.getObject(check_in_date, LocalDate.class)直接映射 Java 8 时间类型比rs.getDate().toLocalDate()更简洁安全。3.3 把查询结果塞进 JTable自定义 TableModel 实现动态刷新Swing 表格不是简单new JTable(data)就完事。要用AbstractTableModel实现数据与视图解耦这是答辩时展示“MVC 思想”的黄金机会public class StudentTableModel extends AbstractTableModel { private final String[] columnNames {ID, 学号, 姓名, 性别, 专业, 楼栋, 房间号, 入住日期}; private ListStudent data new ArrayList(); Override public int getRowCount() { return data.size(); } Override public int getColumnCount() { return columnNames.length; } Override public Object getValueAt(int rowIndex, int columnIndex) { Student s data.get(rowIndex); return switch (columnIndex) { case 0 - s.getId(); case 1 - s.getStuId(); case 2 - s.getName(); case 3 - s.getGender(); case 4 - s.getMajor(); case 5 - s.getBuildingName(); // 来自 JOIN 查询 case 6 - s.getRoomNumber(); case 7 - s.getCheckInDate(); default - null; }; } Override public String getColumnName(int column) { return columnNames[column]; } // 外部调用此方法刷新表格 public void setData(ListStudent newData) { this.data newData; fireTableDataChanged(); // 关键通知视图更新 } }血泪经验fireTableDataChanged()漏写是 Swing 表格“查到数据却不显示”的头号原因。很多同学查完ListStudent打印日志确认有数据但表格空白——八成是忘了这行。4. 真正的落地难点Swing 界面交互细节与数据库事务一致性保障4.1 学生入住/调宿操作用事务包裹“查房间 更新学生 更新房间状态”单纯UPDATE student SET room_id?不够。真实业务要求房间不能超员、学生不能重复入住、状态必须同步更新。用Transactional不课程设计就用原生 JDBC 事务public class DormitoryService { private final DataSource dataSource DataSourceFactory.getDataSource(); public boolean assignStudentToRoom(String stuId, Long roomId) throws SQLException { String checkSql SELECT capacity, status FROM room WHERE id ?; String updateStudentSql UPDATE student SET room_id ?, check_in_date ? WHERE stu_id ?; String updateRoomSql UPDATE room SET status ? WHERE id ?; try (Connection conn dataSource.getConnection()) { conn.setAutoCommit(false); // 关键关闭自动提交 // 1. 检查房间是否可用 try (PreparedStatement checkPs conn.prepareStatement(checkSql)) { checkPs.setLong(1, roomId); try (ResultSet rs checkPs.executeQuery()) { if (!rs.next()) { throw new SQLException(房间ID不存在: roomId); } int capacity rs.getInt(capacity); String status rs.getString(status); if (!空闲.equals(status)) { throw new SQLException(房间状态非空闲: status); } // 检查当前入住人数防超员 long currentCount countStudentsInRoom(conn, roomId); if (currentCount capacity) { throw new SQLException(房间已满员容量 capacity); } } } // 2. 更新学生表 try (PreparedStatement ps conn.prepareStatement(updateStudentSql)) { ps.setLong(1, roomId); ps.setDate(2, Date.valueOf(LocalDate.now())); ps.setString(3, stuId); int affected ps.executeUpdate(); if (affected 0) { throw new SQLException(未找到学号 stuId); } } // 3. 更新房间状态为“已住” try (PreparedStatement ps conn.prepareStatement(updateRoomSql)) { ps.setString(1, 已住); ps.setLong(2, roomId); ps.executeUpdate(); } conn.commit(); // 全部成功才提交 return true; } catch (SQLException e) { // 任何一步失败回滚 if (conn ! null) { try { conn.rollback(); } catch (SQLException rollbackEx) { rollbackEx.printStackTrace(); } } throw e; } } private long countStudentsInRoom(Connection conn, Long roomId) throws SQLException { String sql SELECT COUNT(*) FROM student WHERE room_id ?; try (PreparedStatement ps conn.prepareStatement(sql)) { ps.setLong(1, roomId); try (ResultSet rs ps.executeQuery()) { rs.next(); return rs.getLong(1); } } } }为什么不用 ORM因为课程设计要考你“事务怎么控制”而不是“怎么配 Hibernate”。这段代码里setAutoCommit(false)、commit()、rollback()三要素齐全答辩时老师问“如果第三步更新房间失败学生表会怎样”你能指着catch块说“立刻回滚学生表不变”。4.2 Swing 表单输入校验用 InputVerifier 做实时约束比 JOptionPane 弹窗更专业学号不能纯数字、姓名不能含符号、房间号格式需匹配正则——这些不能等用户点“保存”才报错。用InputVerifier绑定到 JTextFieldJTextField stuIdField new JTextField(15); stuIdField.setInputVerifier(new InputVerifier() { Override public boolean verify(JComponent input) { JTextField field (JTextField) input; String text field.getText().trim(); if (text.isEmpty()) { JOptionPane.showMessageDialog(null, 学号不能为空); return false; } if (!text.matches(^[a-zA-Z]\\d{8,12}$)) { // 例S20210001 JOptionPane.showMessageDialog(null, 学号格式错误字母开头8-12位数字); return false; } return true; } });注意InputVerifier.verify()在焦点离开组件时触发比DocumentListener更符合表单语义matches()用正则而非contains()避免误判。4.3 防止 Swing 界面假死耗时操作必须丢进 SwingWorker查询全校学生列表可能耗时 200ms如果直接在 EDT事件调度线程执行界面会卡顿。用SwingWorker异步加载private void loadStudentTable() { // 显示加载中提示 tablePanel.removeAll(); tablePanel.add(new JLabel(加载中..., JLabel.CENTER)); tablePanel.revalidate(); tablePanel.repaint(); new SwingWorkerListStudent, Void() { Override protected ListStudent doInBackground() throws Exception { return new StudentDAO().findStudents(, 1, 50); // 查全部 } Override protected void done() { try { ListStudent students get(); StudentTableModel model new StudentTableModel(); model.setData(students); JTable table new JTable(model); tablePanel.removeAll(); tablePanel.add(new JScrollPane(table)); tablePanel.revalidate(); } catch (Exception ex) { JOptionPane.showMessageDialog(null, 加载失败 ex.getMessage()); } } }.execute(); }避坑点doInBackground()里不能操作任何 Swing 组件done()方法在 EDT 中执行可安全更新 UI。漏掉execute()调用任务永远不会启动。5. 避坑指南课程设计答辩前必查的 5 个致命细节5.1 现象程序启动报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver原因MySQL 8.x 驱动类名已从com.mysql.jdbc.Driver改为com.mysql.cj.jdbc.Driver而部分老教程或复制的代码仍写旧名或 Maven 依赖未正确下载检查~/.m2/repository/mysql/下是否有mysql-connector-j-8.0.33.jar。解决确认pom.xml中驱动版本为8.0.33且DataSourceFactory中config.setDriverClassName(com.mysql.cj.jdbc.Driver)显式指定若用 IDE 运行右键项目 →Maven→Reload project。5.2 现象中文插入 MySQL 后变成???或查询时name字段显示乱码原因MySQL 服务端、数据库、表、连接 URL 四层字符集不一致。常见是建库时没指定CHARSETutf8mb4或连接 URL 漏了characterEncodingutf8。解决建库时执行CREATE DATABASE dormitory_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;检查db.properties中jdbc.url包含characterEncodingutf8在 MySQL 客户端执行SHOW VARIABLES LIKE character_set%;确保character_set_client/server/connnection均为utf8mb4。5.3 现象JTable 点击列标题排序后数据错乱或双击某行无法弹出编辑窗体原因Swing 默认JTable不支持排序需手动启用TableRowSorter而双击事件监听器未正确绑定到JTable或监听了JScrollPane导致坐标错乱。解决TableRowSorterTableModel sorter new TableRowSorter(table.getModel()); table.setRowSorter(sorter); // 双击监听必须加在 table 上不是 scrollPane table.addMouseListener(new MouseAdapter() { Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() 2) { int row table.rowAtPoint(e.getPoint()); if (row 0) { int modelRow table.convertRowIndexToModel(row); // 转换为模型行号 Student selected model.getData().get(modelRow); new StudentEditDialog(frame, selected).setVisible(true); } } } });5.4 现象导出 Excel 报表时抛NoClassDefFoundError: org/apache/poi/ss/usermodel/Workbook原因课程设计文档要求“支持导出”但未在pom.xml中添加 Apache POI 依赖或添加了却未勾选“Add as Library”IDEA 中常见。解决添加依赖仅需基础包dependency groupIdorg.apache.poi/groupId artifactIdpoi/artifactId version5.2.4/version /dependency dependency groupIdorg.apache.poi/groupId artifactIdpoi-ooxml/artifactId version5.2.4/version /dependency并在导出方法中用XSSFWorkbook.xlsx而非HSSFWorkbook.xls后者已废弃。5.5 现象答辩现场演示时点击“删除学生”后该学生仍显示在表格中原因DAO 层执行了DELETE FROM student WHERE id?但JTable的TableModel未调用fireTableRowsDeleted()通知界面刷新或删除后未重新查询数据库再setData()。解决删除操作完成后必须强制刷新表格数据// 删除后立即重载 ListStudent refreshed studentDAO.findStudents(, 1, 50); tableModel.setData(refreshed); // 或更优在 StudentTableModel 中增加 removeRow(Long id) 方法内部调用 fireTableRowsDeleted()6. 让你的课程设计脱颖而出三个可立即落地的加分技巧6.1 用 PlantUML 自动生成 ER 图嵌入课程设计文档不手画、不截图别再用 PowerPoint 画 ER 图。用纯文本描述让工具自动生成标准图——这才是工程师思维。在src/main/resources/er-diagram.puml中写startuml 设置主题 skinparam defaultFontName Microsoft YaHei skinparam rectangle { BackgroundColorEntity LightBlue BorderColorEntity Blue } entity campus as campus { * id PK -- name -- address } entity building as building { * id PK -- campus_id FK -- name -- total_floors } entity room as room { * id PK -- building_id FK -- room_number -- capacity -- status } entity student as student { * id PK -- stu_id UK -- name -- gender -- major -- room_id FK -- check_in_date } campus ||--o{ building : belongs to building ||--o{ room : has room ||--o{ student : hosts enduml然后用 PlantUML Online Server 粘贴生成 PNG或本地安装 PlantUML 插件一键导出。答辩 PPT 里放这张图老师一眼看出你懂关系建模且不靠美工糊弄。6.2 为每个 DAO 方法写 JUnit 5 测试用例证明逻辑可验证非摆设课程设计文档常要求“测试方案”但多数人只写“已测试”。不如真写两个测试用 H2 内存数据库隔离测试环境class StudentDAOTest { private StudentDAO studentDAO; BeforeEach void setUp() { // 使用 H2 内存库避免污染本地 MySQL String jdbcUrl jdbc:h2:mem:testdb;DB_CLOSE_DELAY-1; JdbcDataSource ds new JdbcDataSource(); ds.setURL(jdbcUrl); ds.setUser(sa); ds.setPassword(); // 初始化表结构复用建表 SQL try (Connection conn ds.getConnection(); Statement stmt conn.createStatement()) { stmt.execute(CREATE TABLE student (id BIGINT AUTO_INCREMENT PRIMARY KEY, stu_id VARCHAR(15), name VARCHAR(20))); stmt.execute(INSERT INTO student(stu_id, name) VALUES (S20210001, 张三)); } studentDAO new StudentDAO(ds); } Test void shouldFindStudentByName() { ListStudent result studentDAO.findStudents(张三, 1, 10); assertEquals(1, result.size()); assertEquals(张三, result.get(0).getName()); } }价值点答辩时打开这个测试类点绿色三角运行当场证明“我的查询逻辑是受控的、可重复验证的”比说一百遍“我测试过了”有力得多。6.3 给 Swing 界面加一个“夜间模式”开关30 行代码惊艳全场不是为了炫技而是展示你理解 UI 可访问性。用UIManager动态切换 LookAndFeelJCheckBoxMenuItem darkModeItem new JCheckBoxMenuItem(深色模式); darkModeItem.addActionListener(e - { boolean isDark darkModeItem.isSelected(); try { if (isDark) { UIManager.setLookAndFeel(javax.swing.plaf.nimbus.NimbusLookAndFeel); // 覆盖 Nimbus 默认色太蓝设为灰黑系 UIDefaults defaults UIManager.getDefaults(); defaults.put(control, new Color(60, 63, 65)); defaults.put(info, new Color(60, 63, 65)); } else { UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); } SwingUtilities.updateComponentTreeUI(this); // 关键刷新整个窗体 } catch (Exception ex) { ex.printStackTrace(); } }); viewMenu.add(darkModeItem);为什么有效SwingUtilities.updateComponentTreeUI()会递归重绘所有组件包括菜单、表格、按钮Nimbus是 JDK 自带、无需额外依赖的现代风格切换后表格背景变暗、文字变亮护眼又专业。答辩时演示这个开关老师会记住“这学生考虑到了用户体验”。最后说句实在话我带过三届课程设计见过太多同学花两周调 Maven 依赖、三天改乱码、一天背答辩稿……其实核心就三件事表结构经得起范式推敲、Java 代码有 try-with-resources 和事务、Swing 界面能响应式刷新。剩下的都是锦上添花。把这三件事做扎实你的系统就能在教室电脑上稳稳跑起来答辩时鼠标一点数据流转如呼吸般自然——那种踏实感比任何炫酷特效都让人信服。希望帮到你。本文还有配套的精品资源点击获取