资讯详情 Java Web学生宿舍管理系统:Spring Boot+MySQL核心设计与实操
📅 2026/10/9 5:47:10
简介面向计算机相关专业与Java Web初学者的一份毕业设计源码包内容为基于JavaWeb的学生宿舍管理系统。系统分为学生、系统管理员、寝室管理员三类角色支持学生信息登记、寝室位置安排、缺勤记录与统计、用户权限控制等典型宿舍管理功能界面设计简约操作流程清晰代码完整可直接运行适合毕业设计、课程设计及答辩参考。资源压缩后仅3.74MB共1315个文件其中以Java源文件、JSP页面、JavaScript脚本和CSS样式居多并附带SQL数据库脚本、项目配置文件和前端静态资源导入数据库后即可启动使用数据库表结构和业务逻辑完备。目前已有497人学习下载对想学习Java Web分层架构、登录权限实现、增删改查及数据库设计的同学来说这份资料提供了完整的参考实现便于按模块阅读、修改和二次开发。1. 这个系统到底解决什么问题从一张床位到整栋宿舍楼的数字化拿到一个“基于Java web的学生宿舍管理系统源码数据库毕业设计.zip”我见过太多人做的第一件事是解压后直接点运行然后被一串数据库连接报错劝退。你真正要面对的不是一个“能不能跑起来”的代码包而是一套把学生登记、宿舍分配、入住退宿、水电统计跑通的业务模型。这类系统的外壳是 Spring Boot里子是 MySQL 的几张业务表源码把业务翻译成 Controller、Service、Mapper 三层协同。读明白它等于同时补了 Java 后端开发、数据库设计、毕业答辩三条线。适合准备毕设的学生也适合刚入职想找一套完整 CRUD 练手的 Java 工程师。2. 技术选型与数据库设计为什么是 Spring Boot MyBatis-Plus MySQL要弄清楚这套系统怎么跑先得弄明白“Java Web”这个词在毕设压缩包里的真实含义。现在下载到的源码包里绝大多数并不是过去那种要部署到外部 Tomcat 的 WAR 工程而是 Spring Boot 工程内嵌 Tomcat入口是一个带 main 方法的 Application 类编译后执行一个 jar 包就能启动。也就是说“Java Web”在这里是泛称实际底座是 Spring Boot、MySQL、MyBatis-Plus 这一线。明白这件事后面读代码、改端口、跑接口才会快。2.1 先看主流Spring Boot、MySQL、MyBatis-Plus 各司其职一个宿舍管理系统的功能在外行眼里很简单学生登记、分配宿舍、退宿、换寝、查看水电费细分一些还有楼栋管理和公告。但当这些需求落到工程上就需要把 HTTP 请求、SQL 和页面渲染拆开。Spring Boot 在整套代码里负责接收浏览器请求、返回页面或 JSONMyBatis-Plus 把 Java 实体类与 MySQL 表字段的转换关系从黑匣子变成继承好的 BaseMapperMySQL 存最终的业务数据。三者配合的结果是你不必手写太多 SQL就能完成一套完整的数据库增删改查。对比一下更传统的组合。有些毕设包会选 JSP Servlet JDBC在 Servlet 里直接写 Connection 和 PreparedStatement代码上手容易但业务逻辑和数据库访问揉在一个类里后续维护很费劲。另一种组合是 Spring Boot Spring Data JPA实体关系清晰时 JPA 能省不少样板代码但引入延迟加载的副作用教学演示时容易踩错。如果目标是“逻辑清楚、能对着答辩讲”我更倾向用 MyBatis-Plus因为它的条件构造器写统计查询像在摆积木CRUD 逻辑一眼就能对上业务字段。技术栈组合上手门槛业务表达的清晰度实际建议JSP Servlet JDBC最低但原始低SQL 散落在 Servlet 里除非老师指定否则不推荐Spring Boot Spring Data JPA中中延迟加载、N1 查询容易讲不清可以用但要额外补 ORM 原理Spring Boot MyBatis-Plus中偏低高BaseMapper 直接提供 CRUD条件构造器可读适合大多数毕设选题这个取舍里有一个容易被忽略的坑Maven 依赖版本匹配。同一个 zip 包里spring-boot-starter-parent 的版本和 mysql-connector-java 的版本经常隔着老远版本闸门没对上启动时最常见的报错是 ClassNotFoundException 或 NoSuchMethodError。所以拿到包之后的第一件事不是找 Main 类而是先看 pom.xml。2.2 数据库设计学生表、宿舍表、入住记录表的职责边界宿舍管理系统的数据模型不大但有明显的业务约束。第一张表是学生基础信息表记录学号、姓名、性别、班级和联系方式。它不该直接存宿舍编号——如果同一个学生换过一次寝直接改学生表字段等于没有留下任何转寝历史。正确做法是单独建一张入住记录表把“当前住哪”和“曾经住过哪”都保留下来。-- 学生基础信息表 CREATE TABLE student ( id BIGINT AUTO_INCREMENT PRIMARY KEY, student_no VARCHAR(20) NOT NULL COMMENT 学号, name VARCHAR(50) NOT NULL COMMENT 姓名, gender TINYINT NOT NULL COMMENT 0-男 1-女, class_name VARCHAR(50) NULL COMMENT 班级, phone VARCHAR(20) NULL COMMENT 联系电话, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_student_no (student_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_general_ci COMMENT学生表; -- 宿舍表楼栋号加房间号唯一床位数单独标出来 CREATE TABLE dormitory ( id BIGINT AUTO_INCREMENT PRIMARY KEY, building_no VARCHAR(20) NOT NULL COMMENT 楼栋编号, room_no VARCHAR(20) NOT NULL COMMENT 房间号, max_bed INT NOT NULL DEFAULT 4 COMMENT 床位数, gender_flag TINYINT NOT NULL DEFAULT 0 COMMENT 0-男寝 1-女寝, UNIQUE KEY uk_building_room (building_no, room_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT宿舍表;student_no 设置唯一键是为了防止同一学号被重复录入dormitory 表的 uk_building_room 则保证同一栋楼不会冒出两个 101 房间。这两张表之间暂不设外键因为学生与宿舍的关系是一段段的时间记录用第三张 stay_record 表来承载。-- 入住记录表学生与宿舍的每一次关联都落在这一张表 CREATE TABLE stay_record ( id BIGINT AUTO_INCREMENT PRIMARY KEY, student_id BIGINT NOT NULL COMMENT 学生ID, dormitory_id BIGINT NOT NULL COMMENT 宿舍ID, is_current TINYINT NOT NULL DEFAULT 1 COMMENT 1-当前在住 0-历史记录, check_in_time DATETIME NULL COMMENT 本次入住时间, check_out_time DATETIME NULL COMMENT 退宿时间, change_count INT NOT NULL DEFAULT 0 COMMENT 换寝次数, KEY idx_student (student_id), KEY idx_dormitory (dormitory_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT入住记录表;is_current 这个字段是整个设计的题眼值为 1 表示该学生当前在住值为 0 表示已经退宿或换寝后的历史记录。查询“某个学生住在哪”时只需要找 is_current 1 的那一条查“这个宿舍曾经住过谁”时把所有记录捞出来。这样设计比在学生表里直接加 dormitory_id 字段要安全得多也是答辩时最能讲出设计感的一处。2.3 入住、退宿、换寝的状态流转状态流转是这套系统的业务核心。入住不等于随便 insert 一条记录就算完需要先确保该学生没有其他在住记录宿舍也没有满员退宿时把当前在住的记录从 1 置为 0不删数据换寝可以拆成“先退宿、再入住”两步这样每次换寝的时间点都有迹可查。-- 入住新增一条 is_current1 的记录 INSERT INTO stay_record (student_id, dormitory_id, is_current, check_in_time, change_count) VALUES (1, 2, 1, NOW(), 0); -- 退宿把在住记录置为历史 UPDATE stay_record SET is_current 0, check_out_time NOW() WHERE student_id 1 AND is_current 1; -- 换寝先执行上面的退宿再插入一条新的入住记录change_count 加 1 INSERT INTO stay_record (student_id, dormitory_id, is_current, check_in_time, change_count) VALUES (1, 5, 1, NOW(), 1);这里有一个很多初学代码包都会犯的错把 dormitory_id 直接塞进 student 表。表面上看查询变快了但退宿、换寝、历史统计全部失真答辩时老师一句“你如何查出这个学生上个月住哪”就能让整个设计露馅。宿舍表里有 gender_flag学生表里有 gender性别校验的最好位置放在 Service 层而不是数据库 CHECK 约束里否则报错信息很难转成页面能展示的提示。后面第四章我会把这段校验一起写进代码。3. 从 zip 到能启动的系统环境准备与最小启动步骤标题里的“源码数据库”已经说明压缩包里自带建表脚本和初始数据但很多人在这一步就卡住。启动这件事本身并不难顺序对了十分钟内就能看到登录页。先说一个总体原则先导数据库再改配置最后启动应用。顺序反了报错信息会混在一起很难判断到底是连接问题还是工程问题。3.1 解压之后先读目录确认主模块和 SQL 文件解压之后不要急着把整个文件夹拖进 IDEA先花五分钟确认三样东西根目录 pom.xml 里 Spring Boot 的版本src/main/resources 下有没有 application.yml 或 application.properties以及根目录或 doc 目录下有没有 .sql 文件。大多数这类包里都会有一个 README.txt作者通常把数据库账号、密码、默认登录账号写在里面先读它比翻代码快得多。pom.xml 的关键任务是看依赖是否会被正确下载。IDEA 初次打开 Maven 工程会自动导入依赖如果出现大量红字先看本机 JDK 版本和 pom.xml 里 java.version 是否一致。宿舍管理系统常用 JDK 8 或 JDK 11装高版本 JDK 时 Maven 编译经常报 “invalid target release” 之类错误。把 java.version 改成和本地一致基本是第一颗后悔药。提示有的代码包把 SQL 文件放在名叫 sql 或 database 的目录下文件名一般是 dormitory.sql 或 student_dormitory.sql。如果压缩包里真的没有 SQL 文件只有实体类也可以让 MyBatis-Plus 根据实体类生成建表 SQL但我不建议拿它当默认路径手工 SQL 的索引和注释往往更完整。3.2 数据库导入命令要敲准尤其注意字符集让数据库导入先行是为了把后面启动时的报错范围缩小。命令主要是三条# 1. 创建数据库字符集和排序规则一次定死 mysql -u root -p -e CREATE DATABASE IF NOT EXISTS dormitory DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 2. 把包里的 SQL 文件导入到 dormitory 库 mysql -u root -p dormitory dormitory.sql # 3. 验证导入结果 mysql -u root -p -e USE dormitory; SHOW TABLES;第一条显式指定 utf8mb4 是必须的。很多老代码包里的 CREATE DATABASE 语句没有字符集参数MySQL 8 默认可能是 utf8mb4但 MySQL 5.7 下某些环境会落到 latin1中文字段导入后直接变乱码。第二条在 Windows 的 cmd 和 Git Bash 里运行正常但如果你用 PowerShell重定向是不支持的会直接报语法错误。遇到这种情况要么换成 cmd 窗口要么用mysql -u root -p dormitory -e source C:/path/dormitory.sql。第三条 SHOW TABLES 是确认表都建出来了如果表数量为 0回到第二条重新看路径。3.3 修改数据源配置并启动 Spring Boot 应用数据库导入成功后接下来改配置。打开 src/main/resources/application.yml核心要改的是 spring.datasource 这一段。server: port: 8080 spring: datasource: # 驱动类名MySQL 8 必须用 cj 版 driver-class-name: com.mysql.cj.jdbc.Driver # 连接地址数据库名、编码、时区都要写清楚 url: jdbc:mysql://localhost:3306/dormitory?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/Shanghai username: root password: 你自己的数据库密码连接 URL 里的参数有两个容易踩坑。第一组是 useUnicodetrue 和 characterEncodingutf8mb4它们决定中文读写是否正常绝不能省。第二组是 serverTimezoneAsia/ShanghaiMySQL 8 默认强制要求时区不写会在启动日志里出现 “The server time zone value” 警告部分版本甚至直接拒绝连接。driver-class-name 也要注意MySQL 8 不再支持旧驱动 com.mysql.jdbc.Driver要用 com.mysql.cj.jdbc.Driver。启动方式有两种开发调试用 Maven 直接跑正式演示时建议打成 jar 再跑。# 方式一直接在项目根目录启动 mvn spring-boot:run # 方式二先打包再执行适合答辩前固定版本 mvn clean package -DskipTests java -jar target/dormitory-0.0.1-SNAPSHOT.jar控制台出现 Started Application 字样就算启动成功浏览器访问 http://localhost:8080/login 应该能看到登录页。如果访问不到先看控制台最后几行异常是端口被占还是数据库连接失败端口问题会跳转到第五章数据库连接失败则回上一步检查用户名密码。4. 核心代码实现登录权限、入住退宿与统计报表怎么落启动跑通只是第一步真正决定这套系统值不值得交的是三段代码登录鉴权、入住退宿、统计报表。这三段几乎能覆盖全部业务 CRUD也是后续扩展水电管理、公告发布的基础。4.1 登录与权限拦截器加 Session 串起整套鉴权宿舍管理系统的角色不多通常就是管理员和操作员用不上完整的 Spring Security。常见做法是 HandlerInterceptor 加 Session登录成功把用户对象放进 Session之后的请求在拦截器里判断 Session 是否为空没登录就重定向回登录页。// 登录拦截器只做一件事检查 Session public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 从 Session 里拿登录时放进去的用户对象 Object loginUser request.getSession().getAttribute(loginUser); if (loginUser null) { // 未登录就回到登录页不放行 response.sendRedirect(request.getContextPath() /login); return false; } return true; } }拦截器写好后需要注册到 Spring MVC并指定哪些路径放行。登录页本身和静态资源必须排除否则会出现“还没登录就被挡在登录页外”的死循环。Configuration public class WebConfig implements WebMvcConfigurer { Autowired private LoginInterceptor loginInterceptor; Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns(/**) // 拦截所有请求 .excludePathPatterns(/login, /css/**, /js/**, /images/**); } }登录接口的逻辑很直接接收用户名密码查数据库比对成功就放进 Session失败返回错误提示。注册拦截器后如果没有生效先检查 WebConfig 类有没有被 Spring Boot 扫描到也就是它所在的包是不是在主启动类所在包之下。这是这类源码包最常见的玄学问题之一其实只是扫描路径没包含。4.2 入住与退宿在 Service 层写清业务约束入住是最能体现逻辑严谨度的功能。表面上是往 stay_record 里 insert 一条记录实际上要先做两步校验重复入住校验和床位数量校验。Service public class StayService { Autowired private StayRecordMapper stayRecordMapper; Autowired private DormitoryMapper dormitoryMapper; Transactional(rollbackFor Exception.class) public boolean checkIn(Long studentId, Long dormitoryId) { // 1. 一个学生同一时段只能有一条在住记录 Long activeId stayRecordMapper.selectActiveId(studentId); if (activeId ! null) { throw new RuntimeException(该学生当前已在住不能重复入住); } // 2. 校验宿舍床位是否已满 Dormitory dormitory dormitoryMapper.selectById(dormitoryId); Long used stayRecordMapper.countCurrentByDorm(dormitoryId); if (used dormitory.getMaxBed()) { throw new RuntimeException(房间床位已满); } // 3. 插入新的在住记录 StayRecord record new StayRecord(); record.setStudentId(studentId); record.setDormitoryId(dormitoryId); record.setIsCurrent(1); record.setChangeCount(0); return stayRecordMapper.insert(record) 0; } }对应的 Mapper 接口稍微关心的人可以这样定义Mapper public interface StayRecordMapper extends BaseMapperStayRecord { Select(SELECT id FROM stay_record WHERE student_id #{studentId} AND is_current 1) Long selectActiveId(Long studentId); Select(SELECT COUNT(id) FROM stay_record WHERE dormitory_id #{dormitoryId} AND is_current 1) Long countCurrentByDorm(Long dormitoryId); }这里两个细节要说明。第一Transactional 让这两个查询和插入操作处于同一事务中中途抛 RuntimeException 会回滚避免出现“查了有余位但插入失败”的数据错乱。第二步骤 2 里的 getMaxBed 来自 dormitory 表若 max_bed 是 4而当前在住人数已经到了 4就应直接拦截。很多初学代码只做了第 3 步没有校验结果房间都睡成上下铺了系统还显示有空位。退宿的逻辑简化来看其实是同一个 stay_record 表的更新操作把 is_current 从 1 改成 0同时写退宿时间。切不可直接把记录 delete 掉否则住宿历史全部蒸发统计月报也会跟着对不上。4.3 统计住宿率用条件构造器少写一半 SQL宿舍管理系统里最高频的一张统计页是“各宿舍住宿率”。如果手写 SQL难免要 join 好几张表再 group by用 MyBatis-Plus 的 LambdaQueryWrapper 可以写得非常直观。// 按宿舍统计当前在住人数返回 ListMap ListMapString, Object roomStats stayRecordMapper.selectMaps( new LambdaQueryWrapperStayRecord() .select(StayRecord::getDormitoryId, count(StayRecord::getId).as(cnt)) .eq(StayRecord::getIsCurrent, 1) .groupBy(StayRecord::getDormitoryId) );这段代码生成的 SQL 大致是SELECT dormitory_id, COUNT(id) AS cnt FROM stay_record WHERE is_current 1 GROUP BY dormitory_id。selectMaps 返回的每个 Map 里包含 dormitoryId 和 cnt 两个键正好够前端展示每个宿舍的人数。住宿率要算百分比时再把 dormitory 表的 max_bed 关联进来做一次除法放在 Service 层组装成 DTO 返回。用条件构造器有个前提实体字段到数据库列名的映射要正确。MyBatis-Plus 开启驼峰映射后dormitoryId 自动对应 dormitory_id 列isCurrent 对应 is_current 列。如果查询结果全是 null先去查 application.yml 里有没有配置 map-underscore-to-camel-case: true。5. 毕业设计最常见的 5 个坑与排查方法这五个问题是我看到这类“源码数据库”包时最常遇到的也是控制台报错最密集的五个位置。每一条按“现象 → 原因 → 解决”来写你按顺序查基本都能定位。5.1 数据库连接报错Access denied / Communications link failure现象启动应用后控制台出现 java.sql.SQLException: Access denied for user rootlocalhost或者运行到某个页面时突然报 Communications link failure。原因前者是 application.yml 里的用户名密码与本地 MySQL 不一致后者通常是 MySQL 服务没启动或者网络连接被断。还有一类隐藏原因是 MySQL 8 的 caching_sha2_password 认证插件导致驱动握手失败。解决先确认 MySQL 服务正常运行再用命令行手动登录验证密码mysql -u root -p能进说明密码没问题不能进就回去把 MySQL 密码改掉。接着核对 yml 里的 username 和 password 一字不差。如果本机 MySQL 是 8且确认本地连接环境可信可以在 URL 后追加 allowPublicKeyRetrievaltrue 和 useSSLfalse 再试一次这两参数能修掉相当一部分加密握手上的怪问题。5.2 页面中文乱码现象登录页正常但学生姓名、班级、宿舍楼名在页面上显示成 ??? 或一组乱码。原因数据库字符集、JDBC URL 参数、页面编码三层里至少有一层不是 UTF-8。毕业设计代码包里最容易漏的是第二层URL 里只写了 serverTimezone没写 characterEncoding。解决先用 SQL 查库的实际字符集SELECT table_schema, default_character_set_name FROM information_schema.schemata WHERE schema_namedormitory。如果不是 utf8mb4就用 ALTER DATABASE 改掉再重新导入数据。然后检查 application.yml 的 JDBC URL确认带 useUnicodetruecharacterEncodingutf8mb4。最后看 IDE 里的 HTML/JSP/Thymeleaf 模板文件编码不要用平台默认编码去覆盖 UTF-8。5.3 Failed to configure a DataSource现象启动还没到业务日志直接抛 Failed to configure a DataSource: url attribute is not specified and no embedded datasource could be configured。原因Spring Boot 没读到 application.yml或者文件里根本没有 spring.datasource.url。常见的情况是文件名写错或者配置放在了 src/main/resources 之外。解决打开 src/main/resources 目录确认文件是 application.yml 而不是 application.yaml 随便起的名字且缩进正确。如果目录下存在 target/classes旧配置可能残留可以先执行 mvn clean package 重新编译再看 target/classes/application.yml 是否生成。只要 yml 存在且包含 url 键这个错基本不会出现。5.4 MyBatis-Plus 的 SQL 执行失败Unknown column现象页面点击查询后后端日志出现 Unknown column xxx in field list或者 SQL 里突然在 limit 附近报语法错误。原因实体类字段和数据库列名对不上也可能是数据库字段用了保留字比如 desc、order生成 SQL 时没有被反引号包住。解决先对照实体类里的 TableField 注解和表结构。MyBatis-Plus 的驼峰映射只负责把 dormitoryId 转成 dormitory_id但不会纠正列名拼写错误。再到 MySQL 里手工执行 SELECT * FROM stay_record LIMIT 1若这条 SQL 也报 Unknown column问题就在表本身。如果字段确实是保留字就在 TableField 里写成 TableField(desc)用反引号包住。5.5 端口被占用Web server failed to start现象启动时提示 Port 8080 was already in use应用闪退或卡住。原因上次启动的 Java 进程没完全关闭或者其他服务占了 8080 端口。很多人在 IDEA 里点了一次停止但实际上 Tomcat 线程还挂在后台。解决最省事的办法改 application.yml 里的 server.port 为 8081立刻避开冲突。更彻底的做法是找到并结束占用进程Windows 下 netstat -ano | findstr 8080 查到 PID再 taskkill /F /PID 加 PIDmacOS/Linux 下 lsof -i :8080 查出进程再 kill。答辩前建议用这个命令先做一遍端口检查省得现场手忙脚乱。6. 答辩前做一个小动作准备一组可复现的演示数据这一步是整篇文章里性价比最高的验收技巧也是我自己的血泪经验。尽量不要拿随手填写的数据去演示。宿舍管理系统在答辩时被追问最多的问题是“住宿率怎么算”“换寝之后历史记录还在不在”“女生会不会被分进男寝”。与其临场点鼠标不如提前准备一组可控数据两个楼栋、四个房间覆盖满员、有空位、性别不对应三种情况再加一个学生的换寝历史记录。演示时只走一条路径先用统计页告诉老师住宿率从哪来再点进一个有空位的房间执行入住然后把这个学生退宿回到统计页看数字变化。整个过程不超过三分钟但把核心业务链路全部串起来了。在此基础上我一般会把数据库清理脚本单独放在 rebuild.sql 里演示前重新执行一次先 DROP 再导入。答辩现场最容易翻车的就是脏数据——演示用的学生只是上次测试留下的房间实际已经满员第二次点入住直接给你弹“房间已满”。你总不能当着老师的面去改数据库提前清理才是正解。最后再说一个能加分的小习惯把统计住宿率那段 LambdaQueryWrapper 在答辩中挑一行出来讲比如 COUNT 分组、is_current 条件如何影响结果。老师们通常更愿意看到你对自己的代码有真实理解而不是背一段 Spring 原理。源码不是黑匣子核心业务路径你都能亲手走通上台就不会被数据库盘问吓住。希望帮到你。本文还有配套的精品资源点击获取