Java学生宿舍管理系统实战:Spring Boot+MySQL从设计到部署
发布时间:2026/9/28 1:21:47来源:尧图网络
简介这是一套面向高校计算机专业学生与Java Web初学者、课程设计或毕业设计开发者的学生宿舍管理系统完整项目包围绕住宿信息管理、宿舍与床位分配、日常行为记录、权限控制等场景提供可运行的后端逻辑与数据库方案。压缩包共661个文件约77.19MB以400个js、46个css、28个less与28个scss等前端资源为主辅以35个java源码、40个class、8个xml与8个html并含2个sql脚本、5个mp4辅导视频、部署文档及两套适配MySQL5与MySQL8的工程包覆盖MVC分层、DAO数据访问、Servlet与JSP交互等实现细节。目前已有1615人学习下载。读者可据此获得完整源码、数据库结构、部署指南与视频讲解用于快速搭建环境、理解模块划分与排错思路提升数据库设计与Java Web开发能力。1. 从一份 Java 课设压缩包说起学生宿舍管理系统到底要解决什么每年毕业季计算机相关专业的群里都会刷屏同一类文件——「基于 Java 的学生宿舍管理系统设计与实现源代码数据库部署文档辅导视频.zip」。很多人第一反应是「课设作业」但真正做过一线开发的人会告诉你这类系统的骨架和中小型管理后台几乎一模一样用户角色分层、宿舍楼栋与床位建模、入住退宿的状态流转、数据增删改查、权限校验、报表导出。它之所以被反复选作课程设计案例不是因为简单而是因为它把 Java Web 开发里最核心的几件事全串了一遍。这篇文章不讲空话我会按一个真实可落地的思路把「学生宿舍管理系统」从技术选型、数据库设计、核心模块编码到部署上线、常见翻车点一层层拆开。适合三类人正在做 Java 课程设计、需要一套能跑通的完整方案的学生想用一个小型项目练手 Spring Boot MySQL 的转行者以及需要快速交付一个宿舍管理内部工具的开发者。读完你应该能自己从零搭出一套能登录、能分配床位、能查空寝、能导出报表的系统而不是只会改别人压缩包里的代码。2. 技术选型与数据库设计为什么这套组合最稳2.1 后端框架选 Spring Boot 而不是原生 Servlet很多人拿到课设第一反应是用 JSP Servlet 硬写理由是「老师只教了这个」。但从工程角度看原生 Servlet 做宿舍管理系统会遇到几个绕不开的麻烦每个请求都要手动解析参数、手动管理数据库连接、手动处理事务代码量翻倍还容易出 bug。Spring Boot 把这些全部封装好一个RestController就能暴露接口Transactional就能保证退宿时床位状态和入住记录同时更新。我一般推荐的组合是Spring Boot 2.7.xJava 8 兼容性最好课设环境普遍是 JDK 8 或 11 MyBatis-Plus比 JPA 更贴近 SQL方便课设答辩时讲清楚每条语句 MySQL 8.0 Thymeleaf 或 Vue 二选一。如果时间紧、只想快速出效果用 Thymeleaf 服务端渲染不用单独起前端工程如果想让简历好看一点用 Vue3 Element Plus 前后端分离但部署会多一个 Nginx 环节。选型理由说白了就三条生态成熟、资料多、出问题能搜到答案。宿舍管理系统这种业务不复杂但角色和状态多的项目最怕的是框架本身出玄学问题Spring Boot 的稳定性正好避开这个坑。2.2 数据库表设计五张核心表撑起整个系统数据库是整个系统的地基表设计错了后面全是血泪。宿舍管理系统的核心实体其实就五个用户、角色、楼栋、宿舍、床位再加一张入住记录表做状态流转。下面是我常用的建表 SQL直接可以在 MySQL 8.0 里执行。-- 用户表学生和管理员共用用 role 区分 CREATE TABLE sys_user ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, username VARCHAR(50) NOT NULL COMMENT 学号/工号登录名, password VARCHAR(100) NOT NULL COMMENT BCrypt 加密后的密码, real_name VARCHAR(50) NOT NULL COMMENT 真实姓名, role TINYINT NOT NULL DEFAULT 0 COMMENT 0学生 1宿管 2管理员, gender TINYINT DEFAULT 0 COMMENT 0男 1女, phone VARCHAR(20) DEFAULT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 楼栋表 CREATE TABLE building ( id BIGINT NOT NULL AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 如 1号楼, gender_type TINYINT NOT NULL COMMENT 0男生楼 1女生楼, floor_count INT NOT NULL DEFAULT 6, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT楼栋表; -- 宿舍表 CREATE TABLE dorm ( id BIGINT NOT NULL AUTO_INCREMENT, building_id BIGINT NOT NULL, room_no VARCHAR(20) NOT NULL COMMENT 如 302, capacity INT NOT NULL DEFAULT 4 COMMENT 可住人数, occupied INT NOT NULL DEFAULT 0 COMMENT 已住人数, PRIMARY KEY (id), UNIQUE KEY uk_building_room (building_id,room_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT宿舍表; -- 床位表一个床位一条记录状态流转的核心 CREATE TABLE bed ( id BIGINT NOT NULL AUTO_INCREMENT, dorm_id BIGINT NOT NULL, bed_no INT NOT NULL COMMENT 床位号 1-4, status TINYINT NOT NULL DEFAULT 0 COMMENT 0空闲 1已入住 2维修中, PRIMARY KEY (id), UNIQUE KEY uk_dorm_bed (dorm_id,bed_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT床位表; -- 入住记录表谁在哪个床位、什么时候入住退宿 CREATE TABLE checkin_record ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL, bed_id BIGINT NOT NULL, checkin_time DATETIME NOT NULL, checkout_time DATETIME DEFAULT NULL, status TINYINT NOT NULL DEFAULT 1 COMMENT 1在住 0已退宿, PRIMARY KEY (id), KEY idx_user (user_id), KEY idx_bed (bed_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT入住记录表;这段 SQL 有几个设计点值得说清楚。第一bed表把床位拆成独立记录而不是在dorm表里存一个「已住人数」字段就完事。原因是宿舍分配本质是「抢床位」如果只存人数两个人同时申请同一个宿舍时会出现超卖拆成床位后可以用唯一索引 状态更新来保证一致性。第二checkin_record表用status标记在住和已退宿退宿时不删记录保留历史方便后面做住宿统计。第三sys_user的密码字段长度给到 100因为 BCrypt 加密后的字符串是 60 位留足余量。参数上capacity默认 4 是本科宿舍常见配置如果做研究生 2 人间或 6 人间改这个值即可床位初始化时按 capacity 循环插入。字符集统一用utf8mb4避免学生姓名里有生僻字或 emoji 时插入失败。2.3 角色权限怎么划分才不返工宿舍管理系统的角色一般分三层学生只能看自己的入住信息、申请换宿宿管能管理本楼栋的床位、审批换宿管理员能管所有楼栋、导入学生名单、导出报表。很多人一开始图省事只做「学生/管理员」两种角色做到一半发现宿管要独立权限回头改代码非常痛苦。我的做法是在sys_user.role里预留三个值权限校验用 Spring Security 或简单的拦截器 注解。如果课设时间紧用拦截器判断 role 就够了不必上完整的 Spring Security否则配置量会拖慢进度。关键是角色字段和权限判断的入口要提前留好后面加角色只是加分支不用动表结构。3. 核心模块编码从登录到床位分配的最小闭环3.1 登录与密码加密别再用明文存密码登录是整个系统的入口也是最容易出安全问题的地方。课设里最常见的翻车是密码明文存数据库答辩时被老师一问就尴尬。正确做法是用 BCrypt 加密Spring Security 自带的BCryptPasswordEncoder就能用不引入额外依赖。Service public class UserService { Autowired private UserMapper userMapper; // BCrypt 编码器强度默认 10够用且性能可接受 private final BCryptPasswordEncoder encoder new BCryptPasswordEncoder(); public User login(String username, String rawPassword) { // 1. 按用户名查用户查不到直接返回 null不暴露用户不存在 User user userMapper.selectByUsername(username); if (user null) { return null; } // 2. 用 matches 比对明文和密文BCrypt 每次加密结果不同但都能匹配 if (!encoder.matches(rawPassword, user.getPassword())) { return null; } return user; } public void register(User user, String rawPassword) { // 注册时加密存储 user.setPassword(encoder.encode(rawPassword)); userMapper.insert(user); } }逻辑说明matches方法内部会从密文中解析出盐值再用同样的盐加密明文比对所以同一个密码每次encode结果不同但都能验证通过。参数上BCrypt 强度默认 10加密一次约 50-100ms登录场景完全够用如果调到 12 以上单次加密会超过 300ms高并发下会拖慢登录接口课设没必要。注意不要把encoder每次 new 出来虽然 BCryptPasswordEncoder 是无状态的但养成注入单例的习惯更好。另外登录失败时统一返回「用户名或密码错误」不要分别提示避免被枚举用户名。3.2 床位分配用事务和状态机避免超卖床位分配是宿舍管理系统最核心也最容易出 bug 的模块。典型场景是一个宿舍 4 个床位两个学生同时点「申请入住」如果代码写得随意可能出现 5 个人住进 4 人间。解决办法是把「查空床位」和「更新床位状态」放在同一个事务里并且用数据库的行锁或乐观锁保证原子性。Service public class BedService { Autowired private BedMapper bedMapper; Autowired private CheckinRecordMapper recordMapper; Transactional(rollbackFor Exception.class) public void assignBed(Long userId, Long dormId) { // 1. 查询该宿舍第一个空闲床位FOR UPDATE 加行锁 Bed bed bedMapper.selectFreeBedForUpdate(dormId); if (bed null) { throw new BizException(该宿舍已满请选择其他宿舍); } // 2. 更新床位状态为空闲-已入住带 status 条件防止并发覆盖 int updated bedMapper.updateStatus(bed.getId(), 0, 1); if (updated 0) { // 说明在查和改之间被别人抢了事务回滚 throw new BizException(床位刚被占用请重试); } // 3. 写入住记录 CheckinRecord record new CheckinRecord(); record.setUserId(userId); record.setBedId(bed.getId()); record.setCheckinTime(new Date()); record.setStatus(1); recordMapper.insert(record); } }对应的 Mapper SQL 关键两条!-- 查询空闲床位并加行锁注意 FOR UPDATE 必须在事务中才生效 -- select idselectFreeBedForUpdate resultTypeBed SELECT * FROM bed WHERE dorm_id #{dormId} AND status 0 ORDER BY bed_no ASC LIMIT 1 FOR UPDATE /select !-- 带旧状态条件的更新返回影响行数用于判断是否抢到 -- update idupdateStatus UPDATE bed SET status #{newStatus} WHERE id #{id} AND status #{oldStatus} /update逻辑说明FOR UPDATE会在事务提交前锁住查询到的行其他事务查同一行会阻塞从而避免两个请求拿到同一个床位。updateStatus里带status oldStatus条件是第二道保险即使锁没生效更新影响行数为 0 也能让事务回滚。参数上rollbackFor Exception.class保证任何异常都回滚不要只写默认的 RuntimeException否则自定义的受检异常不会触发回滚。这套写法在课设答辩时是加分项因为它体现了对并发和数据一致性的理解比单纯 CRUD 高一个层次。3.3 退宿与换宿状态流转要留后悔药退宿看起来简单把床位状态改回空闲、入住记录标记已退宿就行。但实际业务里有两个坑一是退宿后床位可能马上被别人入住历史记录要能追溯二是换宿本质是「先退后住」如果中间失败会导致学生无床可住。我的做法是换宿接口里先执行退宿逻辑再执行分配逻辑整个过程包在一个事务里。如果新床位分配失败事务回滚学生还是住在原床位不会出现「退了没地方住」的情况。退宿时checkin_record不删除只把status改成 0、填上checkout_time这样后面做「本学期住宿人数统计」时能按时间范围查。Transactional(rollbackFor Exception.class) public void changeBed(Long userId, Long newDormId) { // 1. 查当前在住记录 CheckinRecord current recordMapper.selectActiveByUser(userId); if (current null) { throw new BizException(当前没有在住记录); } // 2. 释放原床位 bedMapper.updateStatus(current.getBedId(), 1, 0); current.setStatus(0); current.setCheckoutTime(new Date()); recordMapper.updateById(current); // 3. 分配新床位失败则整体回滚 assignBed(userId, newDormId); }参数说明selectActiveByUser查的是status 1的记录一个学生同时只能有一条在住记录这个约束建议在业务层保证数据库层可以加唯一索引(user_id, status)的变体但 MySQL 不支持部分索引所以用业务校验即可。4. 部署上线从本地跑通到别人能访问4.1 本地环境搭建与依赖安装部署文档里最常见的问题是「在我电脑上能跑」换台机器就报错。根因通常是 JDK 版本、MySQL 版本、Maven 依赖没对齐。我一般把环境要求写死在 README 里并用 Maven 的maven-compiler-plugin锁定编译版本。# 1. 确认 JDK 版本必须是 8 或 1117 会有模块化问题 java -version # 2. 确认 Maven 版本 mvn -v # 3. 创建数据库并导入表结构 mysql -u root -p -e CREATE DATABASE dorm_db DEFAULT CHARSET utf8mb4; mysql -u root -p dorm_db sql/schema.sql # 4. 修改 application.yml 里的数据库连接 # spring.datasource.url: jdbc:mysql://localhost:3306/dorm_db?useSSLfalseserverTimezoneAsia/Shanghai # spring.datasource.username: root # spring.datasource.password: 你的密码 # 5. 打包并运行 mvn clean package -DskipTests java -jar target/dorm-system-1.0.jar逻辑说明serverTimezoneAsia/Shanghai必须加否则 MySQL 8.0 驱动会报时区错误这是最高频的翻车点。useSSLfalse在本地开发可以关掉生产环境建议开启。-DskipTests跳过测试加速打包课设阶段测试用例通常没写全跳过能省时间。参数上数据库连接池用默认的 HikariCP 即可maximum-pool-size默认 10课设并发量完全够。如果部署到云服务器把server.port改成 80 或 8080注意安全组要放行对应端口。4.2 打包成可执行 jar 与常见启动报错Spring Boot 默认打包成可执行 jar内嵌 Tomcatjava -jar直接跑。但有几个报错几乎每个人都会遇到提前知道能省半天时间。第一个是「Port 8080 was already in use」说明端口被占用用netstat -ano | findstr 8080Windows或lsof -i:8080Linux/Mac找到进程杀掉或者改server.port。第二个是「Access denied for user rootlocalhost」密码错了或 MySQL 没启动先确认mysql -u root -p能登录。第三个是「Table dorm_db.sys_user doesnt exist」schema.sql 没导入或导入了错误的库重新执行导入命令。如果要把系统部署到服务器让别人访问最简方案是买一台云服务器装 JDK 和 MySQL把 jar 传上去nohup java -jar xxx.jar 后台运行。想更规范一点用 Docker 打包但课设阶段没必要反而增加学习成本。4.3 数据库备份与迁移的实用做法部署文档里经常忽略备份但宿舍数据一旦丢了就是事故。MySQL 自带mysqldump一条命令就能导出整个库# 导出结构和数据 mysqldump -u root -p dorm_db backup_$(date %Y%m%d).sql # 只导出结构用于初始化 mysqldump -u root -p --no-data dorm_db schema.sql # 恢复 mysql -u root -p dorm_db backup_20250101.sql参数说明--no-data只导表结构适合放进项目作为初始化脚本不加则连数据一起导适合备份。如果数据量大加--single-transaction避免锁表。课设阶段数据量小直接全量导出即可。5. 避坑与排查那些让我熬夜的翻车现场5.1 中文乱码从数据库到页面的全链路排查现象学生姓名显示成问号或乱码。原因通常有三层数据库字符集不是 utf8mb4、连接 URL 没指定编码、页面响应头编码不对。解决顺序是先查数据库SHOW CREATE TABLE sys_user看字符集再查连接 URL 加characterEncodingutf8最后查 Spring Boot 的server.servlet.encoding.charsetutf-8。三层都对齐基本不会再乱。5.2 事务不生效方法内部调用是元凶现象退宿时床位状态改了但入住记录没更新数据不一致。原因是在同一个类里用this.assignBed()调用带Transactional的方法Spring 的代理没生效。解决办法是把事务方法抽到另一个 Service 里或者注入自己Autowired private BedService self;用self.assignBed()调用。这个坑我踩过不止一次记住事务注解只在外部调用时生效。5.3 时间字段差 8 小时时区配置漏了一处现象入住时间存进去是下午 3 点查出来变成早上 7 点。原因是 JVM 时区和 MySQL 时区不一致。解决是在application.yml的 JDBC URL 加serverTimezoneAsia/Shanghai同时在启动参数加-Duser.timezoneAsia/Shanghai。两处都配才能保证从数据库到 Java 对象时间一致。5.4 分页查询总数不对count 语句写错现象分页显示共 100 条实际只有 20 条。原因是 MyBatis-Plus 的分页插件没配置或者自定义 count 语句里带了LIMIT。解决是确认配置了PaginationInnerInterceptor自定义 SQL 的 count 语句去掉 order by 和 limit。这个坑在列表页特别明显答辩时被问到很尴尬。5.5 静态资源 404打包后路径变了现象本地跑页面样式正常打成 jar 后 CSS 和 JS 全 404。原因是静态资源放在了src/main/webapp而不是src/main/resources/static。Spring Boot 打包成 jar 后不识别 webapp 目录必须放 static 下。解决是把资源挪到resources/static引用路径用/css/style.css这种绝对路径。6. 进阶技巧把课设做成能写进简历的项目如果你不想只交个课设而是想让这个宿舍管理系统在面试里能聊有几个投入产出比很高的进阶点。第一是加操作日志用 AOP 拦截所有增删改接口记录操作人、时间、参数存一张operation_log表。面试官问「你怎么做审计」时这就是现成答案。第二是加数据导出用 EasyExcel 把住宿名单导出成 Excel比 POI 简单很多代码量少还不容易 OOM。// AOP 记录操作日志的核心切面 Aspect Component public class LogAspect { Autowired private OperationLogMapper logMapper; // 拦截所有带 Log 注解的方法 Around(annotation(logAnnotation)) public Object around(ProceedingJoinPoint point, Log logAnnotation) throws Throwable { long start System.currentTimeMillis(); Object result point.proceed(); // 记录方法名、耗时、参数 OperationLog log new OperationLog(); log.setMethod(point.getSignature().getName()); log.setCost(System.currentTimeMillis() - start); log.setParams(Arrays.toString(point.getArgs())); log.setCreateTime(new Date()); logMapper.insert(log); return result; } }参数说明Around能拿到方法执行前后的完整信息point.proceed()是实际执行业务方法。注意日志表插入不要和业务方法共用事务否则业务回滚日志也没了可以在切面方法上加Transactional(propagation Propagation.REQUIRES_NEW)开新事务。第三是加接口文档用 SpringDoc 或 Knife4j启动后自动生成 Swagger 页面面试时直接打开给面试官看接口设计比口头描述有说服力。第四是把项目部署到云服务器并配个域名简历上写「在线可访问」比「本地运行」高一个档次。我自己做这类项目最大的教训是不要一上来就追求功能全先把登录、床位分配、退宿这条主链路跑通再逐步加报表、日志、导出。主链路稳了后面加功能都是锦上添花主链路有并发 bug功能再多也是空中楼阁。另外数据库表设计阶段多花两小时想清楚状态流转比编码阶段花两天改 bug 划算得多。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网