资讯动态

基于MySQL+Java+Swing的学生宿舍管理系统课设实战指南

发布时间:2026/10/4 5:57:17 来源:尧图企业网站定制
简介面向高校数据库课程设计与Java实训场景这份资料是一套基于MySQL与Java Swing开发的学生宿舍管理系统覆盖学生信息管理、宿舍分配、管理员权限控制、系统登录等核心功能模块适合计算机专业学生用于课程设计参考或在此基础上二次扩展。压缩包共334个文件包含47个Java源文件、202个class编译文件以及XML配置、PNG界面截图和数据库脚本整体约108.12MBJava源码用于业务逻辑分析class为编译产物XML和PNG分别辅助说明界面布局与运行效果源码与数据库文件齐全便于直接还原项目环境。已有192人学习下载。通过源码可梳理Swing界面与MySQL交互的编码逻辑借助截图把握界面布局理解管理员、学生等角色的功能划分同时数据库脚本也能帮助快速搭建数据表结构为课程设计答辩或毕业设计提供可落地的完整方案。1. mysqlJavaswing学生宿舍管理系统课程设计里最能拿分的CRUD闭环基于mysqlJavaswing的学生宿舍管理系统是课程设计里出现频率极高的一类题目。别觉得题目老它恰好把数据库设计、JDBC访问、GUI事件这三块本科阶段绕不开的能力串在了一个场景里管理员登录后把学生入住、换宿、退宿、查询这些日常操作做成桌面上能点的按钮背后全部落在MySQL的表里这就是一套完整的增删改查闭环。它能解决什么对正在做课程设计或期末大作业的学生来说它是一份可以直接运行的完整样例照着改就能变成自己的东西对想练Java基础的入门者来说它是一个把Java基础、数据库增删改查、Swing组件绑定到一起的练习靶子。我的建议和很多教程相反先别急着读代码先把数据库脚本跑起来。数据库立住了代码怎么读都不会偏。2. 技术栈分工与最小工程结构MySQL存什么、Java算什么、Swing画什么这套系统的名字已经把技术栈写死了但选型背后的理由值得先说清楚。很多同学交上来的课设用的是SQLite单文件库跑是能跑答辩时老师问一句“你的数据和程序怎么分开部署”就卡住了。MySQL的好处是它是真正的服务端数据库有独立的服务进程、账号权限、字符集和事务概念这些都是课程设计答辩时能讲满两分钟的点。Java这边不用框架直接用原生JDBC虽然代码啰嗦但每一行都在明面上老师问到底层连接怎么建立的你能直接指给他看。Swing则胜在它是桌面GUI没有浏览器兼容、跨域、CSS布局这些破事组件拖一拖、事件挂一挂界面就能跑。2.1 选型理由为什么是这三件套而不是SQLite或MyBatis先说数据库。SQLite确实省事一个文件就是整个库但它的并发能力和账号体系都很弱课程设计要的是“像个正式系统”MySQL在这点上更占优势。你照着mysql安装配置教程把服务端装好建一个专门的dorm库再配一个独立账号这套流程本身就是答辩素材。再说数据访问层。现在生产环境确实很少写原生JDBC但这门课的教学目标就是JDBC本身。如果你直接上MyBatis或Spring Data JPA等于把最核心的得分点藏进了框架黑匣子老师一问“PreparedStatement怎么防注入”“事务怎么控制”你答不上来反而扣分。Swing也一样它和Web后端完全是两条路线但课程设计只要求桌面端Swing的MVC结构——JTable自带Model和View分离——刚好和数据库分层对应上。2.2 最小工程结构拿到源代码先找四个目录和一个入口类一套标准的宿舍管理系统源码目录结构通常长这样dorm-system/ ├── src/ │ └── com/dorm/ │ ├── view/ # JFrame界面MainFrame在这里 │ ├── dao/ # JDBC数据访问增删改查都在这 │ ├── model/ # Student、Room这些实体类 │ └── util/ # DbUtil连接工具类 ├── sql/ │ └── dorm.sql # 建库建表脚本含测试数据 └── lib/ └── mysql-connector-java-x.x.x.jar # MySQL驱动拿到源代码后第一件事不是点开MainFrame狂看而是先确认lib目录里有没有驱动jar。很多同学下载的源码包忘了带驱动或者驱动版本和本机MySQL对不上一运行就报ClassNotFoundException这种问题占了课设求助帖的三分之一。驱动放好后改动最少的地方就是util包下的DbUtil类的写法几乎千篇一律public class DbUtil { private static final String URL jdbc:mysql://localhost:3306/dorm ?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new RuntimeException(找不到MySQL驱动检查lib目录, e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }这段代码有四个点要留意。第一如果你的MySQL是5.7及以下驱动类名要写成com.mysql.jdbc.Driver如果是8.0必须用com.mysql.cj.jdbc.Driver。第二URL里的localhost:3306指的是本机MySQL端口改密码后要同步改PASSWORD。第三useSSLfalse必须带上否则MySQL 8.0默认会尝试SSL握手容易报ssl连接错误。第四characterEncodingutf8解决中文乱码后面的章节还会细讲。这个类写完整个系统的数据库访问入口就通了。2.3 Swing界面层JTabbedPane、JTable和做导航的JTree界面层用到的Swing组件比较固定外层一个JFrame里面放JTabbedPane做页签常见的是“学生管理”“宿舍管理”“入住登记”三个页签。每个页签的核心控件是JTable用来展示数据库查出来的数据。学生列表页还需要一个搜索框按学号或姓名过滤。宿舍楼栋导航这层常见做法是用JTree做一个“楼栋-房间”的树形结构点某个房间节点右侧表格就刷出该房间的学生。这里有个容易被示例带偏的坑网上搜swing相关示例时经常看到带勾选框的tree那个组件不是Swing标准控件是别人封装的三方库需要额外引入jar包。宿舍管理场景里你根本不需要在树节点上打勾点选导航就够了引入那个组件只会让你多一个依赖、多一份排查工作量。界面层的原则是能用标准组件解决的问题绝不引额外依赖。3. 数据库设计先行五张表、三条外键和测试数据脚本先把这个跑通再谈代码数据库脚本是整个课设的地基。见过太多人先写Java代码写到一半发现表结构对不上又回头改SQL翻车概率极高。正确顺序是先把dorm库建好表结构确定下来再写DAO层代码。这套系统的核心数据模型可以用五张表覆盖楼栋表、房间表、学生表、管理员表、入住日志表。3.1 宿舍管理的数据模型楼栋、房间、学生和入住日志怎么关联表名作用关键字段tb_building楼栋基础信息id、nametb_room房间与床位容量id、building_id、room_no、total_beds、used_bedstb_student学生基本信息与当前住宿状态id、student_no、name、gender、phone、room_id、statustb_admin系统登录账号id、username、passwordtb_checkin_log入住/换宿/退宿的历史记录id、student_id、room_id、action、oper_time关联关系上房间属于楼栋所以tb_room通过building_id外键指向tb_building学生当前住在某个房间所以tb_student通过room_id外键指向tb_room同时冗余一个building_id避免每次都要连表查楼栋名。入住日志表单独拆出来是为了保留历史记录——学生退宿后他住过哪些房间还得查得到不能只改学生表当前状态了事。3.2 建库建表SQL字符集、外键和InnoDB一个都不能少把这个脚本存成dorm.sql在命令行用source命令导入或者在Navicat里直接运行CREATE DATABASE IF NOT EXISTS dorm DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE dorm; CREATE TABLE tb_building ( id TINYINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(20) NOT NULL UNIQUE ) ENGINEInnoDB; CREATE TABLE tb_room ( id INT PRIMARY KEY AUTO_INCREMENT, building_id TINYINT NOT NULL, room_no VARCHAR(20) NOT NULL, total_beds TINYINT NOT NULL DEFAULT 4, used_beds TINYINT NOT NULL DEFAULT 0, UNIQUE KEY uk_room_no (room_no), KEY idx_room_building (building_id), CONSTRAINT fk_room_building FOREIGN KEY (building_id) REFERENCES tb_building(id) ) ENGINEInnoDB; CREATE TABLE tb_student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL, name VARCHAR(50) NOT NULL, gender ENUM(男,女) NOT NULL, phone VARCHAR(11), building_id TINYINT, room_id INT, bed_no TINYINT, status ENUM(in,out) NOT NULL DEFAULT in, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_student_no (student_no), KEY idx_stu_room (room_id), CONSTRAINT fk_stu_building FOREIGN KEY (building_id) REFERENCES tb_building(id), CONSTRAINT fk_stu_room FOREIGN KEY (room_id) REFERENCES tb_room(id) ) ENGINEInnoDB; CREATE TABLE tb_admin ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(30) NOT NULL UNIQUE, password VARCHAR(32) NOT NULL ) ENGINEInnoDB; CREATE TABLE tb_checkin_log ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, room_id INT, action VARCHAR(10) NOT NULL, oper_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_log_student (student_id), CONSTRAINT fk_log_student FOREIGN KEY (student_id) REFERENCES tb_student(id), CONSTRAINT fk_log_room FOREIGN KEY (room_id) REFERENCES tb_room(id) ) ENGINEInnoDB;这里几个容易被判卷老师挑刺的细节。第一建库时直接指定DEFAULT CHARACTER SET utf8mb4不要在表级别再逐个指定避免有的表是utf8、有的是gbk。utf8mb4兼容所有中文和emoji而老的utf8在MySQL 8.0里是utf8mb3的别名遇到emoji直接存不进去。第二所有表都用InnoDB引擎MyISAM不支持外键和事务用了就前功尽弃。第三外键命名用fk_前缀加表名加字段名答辩时一眼能看出约束关系。第四student_no上建了唯一索引这是学号查重的关键重复插入时会直接报主键冲突。3.3 用存储过程批量生成200条测试数据DELIMITER和room_id随机的边界空表没法演示手写200条INSERT又太蠢。课程设计的常规做法是写一个存储过程批量生成。先插楼栋和房间再生成学生INSERT INTO tb_building (name) VALUES (1号楼), (2号楼); INSERT INTO tb_room (building_id, room_no, total_beds) VALUES (1, 101, 4), (1, 102, 4), (2, 201, 4), (2, 202, 4); DELIMITER $$ CREATE PROCEDURE sp_gen_students(IN p_cnt INT) BEGIN DECLARE i INT DEFAULT 0; WHILE i p_cnt DO INSERT INTO tb_student (student_no, name, gender, phone, room_id, status) VALUES (CONCAT(2025, LPAD(i, 5, 0)), CONCAT(学生, i), IF(RAND() 0.5, 男, 女), CONCAT(13, LPAD(FLOOR(RAND() * 999999999), 9, 0)), FLOOR(1 RAND() * 4), in); SET i i 1; END WHILE; END$$ DELIMITER ; CALL sp_gen_students(200);DELIMITER $$这段是很多新手看不懂的地方。MySQL命令行默认用分号作为语句结束符但存储过程体内部有大量分号如果不用DELIMITER改临时结束符命令行会在第一个分号处就把语句截断导致语法错误。在Navicat里运行这段脚本时也要保持同样写法。生成完数据后rooms表的used_beds还都是0缺最后一步校准UPDATE tb_room r SET used_beds ( SELECT COUNT(*) FROM tb_student s WHERE s.room_id r.id );这条UPDATE把每个房间的实际入住人数同步到used_beds字段。如果不做这一步界面上显示的房间空床位数就是假的演示时被老师看穿就很尴尬。4. 用JDBC把Java接上MySQL连接参数、DAO模板和事务边界数据库脚本跑通后接下来做Java和MySQL之间的桥。这一章是整个课设的技术核心也是答辩时老师最爱追问的部分。连接怎么建、SQL怎么传参、操作失败怎么回滚、界面线程怎么不卡死每一件都有标准答案。4.1 连接URL怎么写useSSL、serverTimezone、characterEncoding各管什么事DbUtil里的连接URL不是随便拼的三段参数各管一件事参数作用不设的后果useSSLfalse关闭安全套接字连接MySQL 8.0默认尝试SSL握手报ssl连接错误serverTimezoneAsia/Shanghai指定服务器时区报差8小时或CST识别不了的错误characterEncodingutf8指定客户端字符集中文全部变成?????rewriteBatchedStatementstrue开启批量SQL优化批量插入200条数据时一条条执行慢驱动类名也有讲究。MySQL 5.x用的是com.mysql.jdbc.DriverMySQL 8.0换成了com.mysql.cj.jdbc.Driver。如果你拿到的源码里写的是老驱动类名连8.0库时虽然能跑但会打一大段过期警告反过来5.x的驱动去连8.0的库直接认证失败。所以先确认本机MySQL版本再决定驱动jar和类名。4.2 DAO层标准写法PreparedStatement防注入和ResultSet映射DAO层不要用Statement拼字符串必须用PreparedStatement这是答辩必考点。查询某个房间的学生列表标准写法是这样public ListStudent listByRoom(int roomId) throws SQLException { String sql SELECT student_no, name, gender, phone FROM tb_student WHERE room_id ? AND status in ORDER BY student_no; ListStudent result new ArrayList(); try (Connection conn DbUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, roomId); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Student s new Student(); s.setStudentNo(rs.getString(student_no)); s.setName(rs.getString(name)); s.setGender(rs.getString(gender)); s.setPhone(rs.getString(phone)); result.add(s); } } } return result; }两个细节值得说。第一try-with-resources写法会在代码块结束时自动关闭Connection、PreparedStatement和ResultSet不用手写一堆finally。如果你拿到的源码是手写close的也可以但要确保关闭顺序是ResultSet先、Statement后、Connection最后反了会报连接泄漏。第二ORDER BY student_no这个排序我放在SQL里做而不是查出List之后再用Collections.sort。数据量小的时候两种都没问题但放SQL里语义更清楚老师问“你的列表怎么做到按学号有序”时答“数据库层排序”比“Java层排序”更专业。4.3 入住登记为什么必须开事务一个update和一个insert要么都成要么都败入住登记这个功能涉及两步写操作更新房间的used_beds加1再往tb_student插入一条学生记录。这两步之间不能有“只成功一半”的情况否则房间显示满了但学生表里没这个人或者学生住进去了但床位没扣。解决手段就是数据库事务public void checkIn(Student s, int roomId) throws Exception { try (Connection conn DbUtil.getConnection()) { conn.setAutoCommit(false); try { String updateRoom UPDATE tb_room SET used_beds used_beds 1 WHERE id ? AND used_beds total_beds; try (PreparedStatement psRoom conn.prepareStatement(updateRoom)) { psRoom.setInt(1, roomId); if (psRoom.executeUpdate() 0) { throw new SQLException(房间不存在或床位已满); } } String insertStudent INSERT INTO tb_student (student_no, name, gender, phone, room_id, status) VALUES (?, ?, ?, ?, ?, in); try (PreparedStatement psStudent conn.prepareStatement(insertStudent)) { psStudent.setString(1, s.getStudentNo()); psStudent.setString(2, s.getName()); psStudent.setString(3, s.getGender()); psStudent.setString(4, s.getPhone()); psStudent.setInt(5, roomId); psStudent.executeUpdate(); } conn.commit(); } catch (Exception e) { conn.rollback(); throw e; } } }这个方法的代码量不大但每一行都是踩坑换来的。setAutoCommit(false)必须在执行第一条UPDATE之前调用否则JDBC默认每条SQL自动提交事务就形同虚设。UPDATE语句里带上used_beds total_beds这个条件并且用executeUpdate()的返回值判断是否真的更新了行比先SELECT再判断“房间剩几个床位”更安全——两个管理员同时操作最后一张床时SELECT出来的数字可能是过期的而UPDATE条件直接在数据库层面挡住超卖。最后catch里rollback只要两步中任何一步抛异常房间床位和学生记录都不会留下半截数据。4.4 Swing表格刷新不要在事件线程里跑SQLSwing有一个单线程规则所有UI组件的操作必须在事件分发线程EDT里执行所有耗时的数据库操作不能在EDT里执行。新手最容易犯的错就是在按钮点击事件里直接调用DAO方法查数据库。数据量小的时候感觉不到问题一旦测试数据灌到两百条、网络稍有波动界面就白屏拖不动。点击查询按钮的标准写法是SwingWorker后台线程加done回调JButton searchBtn new JButton(查询); searchBtn.addActionListener(e - { searchBtn.setEnabled(false); new SwingWorkerListStudent, Void() { Override protected ListStudent doInBackground() { return studentDao.listAll(); } Override protected void done() { try { ListStudent list get(); DefaultTableModel model (DefaultTableModel) table.getModel(); model.setRowCount(0); for (Student s : list) { model.addRow(new Object[]{ s.getStudentNo(), s.getName(), s.getGender(), s.getPhone() }); } } catch (Exception ex) { JOptionPane.showMessageDialog(frame, 查询失败: ex.getMessage()); } finally { searchBtn.setEnabled(true); } } }.execute(); });SwingWorker的doInBackground方法跑在后台线程里执行DAO查询done方法在查询结束后自动切回事件线程这时才允许操作JTable。表格的DefaultTableModel先setRowCount(0)清空旧数据再一行行addRow这是刷新表格的固定套路。查询按钮在后台任务执行期间setEnabled(false)防止用户连续点出多个线程同时查库这个细节在演示时很加分老师会觉得你考虑了并发。5. 高频翻车排查乱码、界面卡死、连接超时和打包后连不上库把上面四章内容做完系统已经能跑了。但能跑和能演示是两回事有些坑只会在特定时刻爆发比如换一台电脑、隔了一个中午、或者打成jar包发给老师检查的时候。这一章列出的五个问题是我看过大量课设和跑过的项目里翻车率最高的每一条都是现象、原因、解决三段式。5.1 中文乱码界面显示?????问题不一定在代码现象界面上学生姓名和宿舍楼栋名全部变成问号但数字和英文正常。 原因常见的有三个。第一JDBC连接URL里没加characterEncodingutf8客户端和数据库之间的字符集没对齐。第二建库脚本用的是默认字符集建出来的库是latin1或gbk存进去就是乱码的种子。第三IDE编译环境编码不对源码文件本身是UTF-8但IDE用GBK编译中文字符串直接变成乱码。 解决按顺序排查。先看db库的字符集执行SHOW CREATE DATABASE dorm确认是utf8mb4再检查DbUtil的URL参数最后在IDE的Run Configuration里加上-Dfile.encodingUTF-8重启应用。三处全统一成UTF-8后这个问题基本绝迹。5.2 点击查询窗口卡死SQL在工作线程跑界面才不僵现象点“查询全部学生”按钮后窗口立刻无响应鼠标拖不动标题栏显示“未响应”。 原因按钮事件里直接执行了studentDao.listAll()数据库查询阻塞了事件分发线程EDT。EDT一阻塞整个窗体的重绘、鼠标事件、键盘事件全排队看起来就是死机。 解决把耗时查询移进SwingWorker按4.4的写法改造。还有一类隐藏的卡死是SQL本身慢比如没建索引的全表扫描两千条数据就卡两三秒——检查tb_student的room_id和student_no上有没有索引没有就补上。5.3 上午能连下午报Communications link failurewait_timeout把空闲连接断了现象早上打开课设程序一切正常中午去吃饭下午回来点按钮报“Communications link failure”或者“Connection is not available”。 原因MySQL服务端的wait_timeout默认是8小时但教学机房和云数据库经常配成更短的值。你的程序持有连接但一直空闲MySQL觉得这个连接没人用主动把它断掉Java这边还拿旧连接去执行SQL自然报错。 解决课设阶段最简单的方案是不缓存连接每次DAO操作都通过DbUtil.getConnection()新建连接用完即关。如果你上了连接池比如Druid一定要配testOnBorrowtrue和validationQuerySELECT 1让每次从池里拿连接时先探活。不要在课设里手工搞“全局单例Connection”那是给自己挖坑。5.4 IDEA里能跑打成jar就ClassNotFoundException驱动没打进去现象在IDEA里点运行一切正常但打成可执行jar后双击运行报ClassNotFoundException: com.mysql.cj.jdbc.Driver。 原因IDEA里运行时会自动把lib目录下的驱动jar加进classpath但打jar包时默认配置不会把外部jar包打进你的产物里。换一台没有这个驱动的电脑跑起来必挂。 解决IDEA里Project Structure找到Artifacts在Output Layout里把mysql-connector-java的jar包拖进“AVAILABLE ELEMENTS”再到包结构里确认它出现在JAR的根目录或lib目录下。如果你用的是Maven在pom.xml里配maven-shade-plugin打fat jar把依赖一块儿带进去。打包后还有一个连带坑连接URL里的localhost在新电脑上不一定指向你的MySQL交到别的机器前记得改成目标机器的IP。5.5 删宿舍报外键错误完整性约束和软删除的取舍现象删除一个还有学生入住的房间程序抛“Cannot delete or update a parent row: a foreign key constraint fails”。 原因这不是Bug是外键约束在工作。tb_student里有记录的room_id指向这个房间数据库不允许你删掉它否则学生记录的room_id就成悬空引用。 解决两种路线。一是业务上先处理学生把该房间的学生全部迁走或标记为out状态再删房间二是给tb_student的room_id字段设为ON DELETE SET NULL删房间时自动置空。但我不推荐第二种宿舍管理场景里“强制删除还有学生的房间”本身就不合理不如显式检查房间剩余人数有余床才允许删。另一种更工程化的做法是给房间加deleted字段做软删除界面默认过滤已删除记录保留历史数据可追溯答辩时说出来很加分。6. 答辩前把三个业务场景走一遍收尾细节决定印象分代码写完了坑也踩完了最后一步是验证。别等答辩当天现场点按钮提前把最关键的三条业务链路走通比背十遍PPT都有用。6.1 用三个自检场景验证系统不是“纸面能跑”第一个场景是并发抢床位。开两个客户端窗口同时对一个只剩一张空床的房间执行入住登记。正确结果是两个请求只有一个成功另一个提示房间已满。这个场景能验证4.3那行的UPDATE条件是否真的生效也是老师最喜欢追问的“你考虑过并发吗”。第二个场景是学号重复。往系统里插入一个已存在的student_no正确结果是被数据库的唯一索引拦下界面弹出友好错误提示。如果你的程序在这里直接崩溃或出现系统英文堆栈说明DAO层没有处理SQLException答辩时会被扣分。第三个场景是外键约束。尝试删除一个已分配学生的房间程序应当给出业务层面的提示而不是让数据库异常直接弹到界面上。顺带验证一下尝试给不存在的楼栋插入房间看外键会不会拦住。6.2 上交前五分钟一份六行快查清单检查项容易翻车的地方快查方法数据库脚本换一台干净机器能否一次导入成功source导入dorm.sql后跑三条SELECT连接参数密码是否写死、IP是否为localhost看DbUtil确认和本机MySQL一致驱动jarlib目录是否带着驱动运行前确认jar存在字符集界面、库、URL三处编码输入一个含生僻字的姓名测试事务边界入住流程中途报错是否留下半截数据手动中断一次查used_beds打包产物jar包是否包含驱动解压jar看lib目录最后说点我的个人教训。第一次做课设时我把数据库密码直接写在DbUtil里用的还是root空密码演示当天老师换了台没配好的机器程序当场连不上库。后来学乖了所有连接配置放在一个单独的properties文件里不上传到公共目录答辩前先跑一遍“断网重连”测试。课设可以简单但连接配置、字符集、事务这三件事做得干净答辩印象分能高一个档。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑