Spring Boot+MyBatis学生宿舍管理系统:从登录到报修的全链路实现
发布时间:2026/10/17 17:00:16 锦皓数字建站

简介一套基于 Java 的学生宿舍管理系统采用 SpringBoot、MySQL 与 Bootstrap 组合搭建面向毕业设计、Java Web 初学者及有后台管理系统二次开发需求的开发者。系统围绕管理员、宿管、学生三类角色设计覆盖学生信息维护、楼宇与宿舍管理、入住登记、宿管权限配置等核心业务并支持数据的 Excel 导入导出模块划分清晰适合作为毕业设计源码或课程项目二次开发底座。资源包共 661 个文件以 400 个 js、46 个 css、8 个 html 等前端资源为主后端包含 35 个 java 源码与 40 个 class 文件另附 xml 配置、sql 数据库脚本及 mp4 演示视频整体约 72.37MB压缩包内同时提供 mysql5 与 mysql8 两个版本的适配工程降低环境配置成本。已有 467 人学习下载内含可直接运行的工程、数据库初始化脚本、操作演示视频和设计文档便于使用者快速熟悉模块结构并开展二次开发。1. 学生宿舍管理系统从 Excel 登记到多角色在线协作如果你在学校宿管办公室待过大概率见过这样的场景一摞纸质入住登记表、一张写满备注的 Excel 床位表、墙上贴着的手写报修记录。每到新生入学季宿管老师就对着表格来回比对忙中出错免不了让学生来回跑冤枉路。这套基于 Java 的学生宿舍管理系统就是把上面这一摊事搬进浏览器——学生在线提交报修、查看宿舍信息宿管负责分配床位、处理报修、登记来访管理员统管全部数据。它不搞花哨的微服务架构就是个扎实的 Spring Boot 单体应用数据落在 MySQL 里界面用服务端模板渲染。对于正在选毕业设计题目、或者想快速拿下一个完整 JavaWeb 管理系统的开发者来说这个项目的代码结构和业务完整度都非常值得拆开看一看。2. 技术与架构Spring Boot MyBatis MySQL 的分层设计2.1 技术选型与整体分层项目的基础框架是 Spring Boot 2.x持久层用 MyBatis数据库用 MySQL前端页面采用 Thymeleaf 模板配合 Layui 样式库。这套组合是当下 JavaWeb 管理系统最常见的配方我拆过的毕业设计项目里十有八九是这套配置。Spring Boot 负责把业务对象注册成 Bean 并自动配置数据源MyBatis 通过 XML 映射文件把 SQL 和 Java 方法绑定起来Thymeleaf 则在服务端把数据渲染成 HTML 页面。代码结构上包名按职责划分得比较清晰典型的分层结构是这样的src/main/java ├── controller # 控制层接收请求、返回页面 ├── service # 业务层处理核心逻辑 ├── mapper # 数据访问层接口 ├── entity # 实体类对应数据库表 ├── interceptor # 登录拦截器 └── config # 配置类如拦截器注册整个请求链路走的是标准三层浏览器发起 HTTP 请求Controller 接收参数并调用 Service 层方法Service 里组织业务规则、调用 Mapper 接口Mapper 通过 XML 里的 SQL 语句操作 MySQL 数据库查询结果再原路返回最终由 Thymeleaf 渲染成 HTML 响应给浏览器。这套流程看起来朴素但它是理解后面所有模块的基础我在给这个项目加新功能时也是按照这个链路往里面填代码。2.2 数据库表结构设计宿舍管理系统的核心数据关系不复杂我在建库时提炼出了这几张核心表-- 用户表管理员和宿管统一存放在这里 CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) NOT NULL COMMENT 登录名, password VARCHAR(64) NOT NULL COMMENT 密码MD5加密存储, role TINYINT NOT NULL COMMENT 角色1管理员 2宿管, real_name VARCHAR(32) COMMENT 真实姓名 ); -- 学生表 CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(16) NOT NULL COMMENT 学号, name VARCHAR(32) NOT NULL, gender TINYINT COMMENT 1男 2女, phone VARCHAR(16), dorm_id INT COMMENT 当前宿舍ID空表示未分配 ); -- 宿舍表 CREATE TABLE dorm ( id INT PRIMARY KEY AUTO_INCREMENT, building VARCHAR(16) COMMENT 楼栋如3号楼, room_no VARCHAR(16) COMMENT 房间号如301, capacity INT DEFAULT 4 COMMENT 可住人数, occupied INT DEFAULT 0 COMMENT 已住人数 ); -- 报修工单表 CREATE TABLE repair_order ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL COMMENT 报修学生, content VARCHAR(255) COMMENT 报修内容, status TINYINT DEFAULT 1 COMMENT 1待派单 2维修中 3已完成, create_time DATETIME, finish_time DATETIME );student 表里的 dorm_id 和 dorm 表是一对多关系一个宿舍可以住多个学生。这里用冗余字段 occupied 记录已住人数是为了在分配宿舍时快速判断是否住满避免每次分配都去 count 一次学生表。这是个小优化但非常实用后面讲宿舍分配模块时会看到这个字段起的作用。另外来访登记、卫生检查这类业务也可以各自建表字段设计思路类似主键自增、外键逻辑关联、时间字段记录操作节点。2.3 角色权限与菜单设计这个系统里一共有三种角色管理员、宿管、学生登录后看到的功能菜单完全不同。常见做法是在登录时把用户角色写入 Session渲染页面时根据角色判断菜单的显示与隐藏同时在 Controller 层也做一道校验拦截。角色的职责边界划分得很清楚管理员管系统基础数据比如管理楼栋和宿舍房间、查看统计报表、维护后台账号宿管是日常业务的实际执行人负责给学生分配宿舍、处理退宿调宿、接单报修、登记来访人员学生是最末端的用户功能聚焦在查看自己的住宿信息、提交报修工单、查询处理进度。权限校验的力度属于功能级也就是按钮和数据级权限没做那么细。比如学生登录后直接访问 /repair/list 能看到所有报修单这在设计上是不合理的但很多毕设项目就是这么写的。我后面二次开发章节里会专门讲怎么按登录用户的角色去过滤数据这是把它从能跑变成好用的一个关键改动点。3. 核心模块拆解登录、宿舍分配、报修工单的完整链路3.1 登录认证与会话管理登录是这个系统所有请求的入口实现思路是表单提交 Session 会话 拦截器校验。用户输入用户名密码Controller 接收后把密码做 MD5 加密虽然 MD5 现在不算安全但毕业设计项目里最常遇到的就是这种写法再拿用户名和密文去查用户表匹配成功就把用户对象塞进 Session。PostMapping(/login) public String login(String username, String password, HttpSession session) { // 密码加密后再查询数据库里存的也是密文 String md5 DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8)); User user userService.findByUsernameAndPassword(username, md5); if (user null) { return redirect:/login?error1; } // 登录成功把用户信息写入 Session session.setAttribute(loginUser, user); return redirect:/index; }这里有个常见的坑MD5 加密的字节编码。如果不指定 UTF-8在跨平台环境下可能因为默认字符集不同导致加密结果不一致登录永远失败。我在本地 Windows 和服务器 Linux 上跑同一个项目时踩过这个坑后来统一规定所有涉及字符串转字节的地方都显式指定编码。用户信息存在 Session 里之后还需要一个拦截器统一校验不然用户直接输入内页 URL 就能绕过登录。Spring Boot 里实现方式是实现 HandlerInterceptor 接口在 preHandle 方法里检查 Sessionpublic class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); if (session.getAttribute(loginUser) null) { // 未登录重定向到登录页并记录来源路径 response.sendRedirect(/login); return false; } return true; } }拦截器注册在 WebMvcConfigurer 配置类里需要注意放行登录接口和静态资源这个细节放到避坑章节详细说。整套登录机制胜在简单直观理解成本低如果把 Session 换成 JWT 或者 Spring Security逻辑会更复杂但对这种单体管理系统的场景来说当前方案完全够用。3.2 宿舍分配床位与学生的关系管理宿舍分配是这个系统里业务规则最密集的模块核心要处理两件事宿舍满了不能继续分配学生已经有宿舍了不能重复分配。这两条规则如果只在前端校验懂一点 HTTP 的人直接构造请求就能绕过所以必须写在 Service 层。Transactional public String assignStudentToDorm(Long studentId, Long dormId) { Dorm dorm dormMapper.selectById(dormId); if (dorm null) { return 宿舍不存在; } // 规则一宿舍已满则拒绝分配 if (dorm.getOccupied() dorm.getCapacity()) { return 该宿舍已满无法分配; } Student student studentMapper.selectById(studentId); if (student null) { return 学生不存在; } // 规则二学生已有宿舍则拒绝必须先退宿再分配 if (student.getDormId() ! null) { return 该学生已分配宿舍请先办理退宿; } // 分配更新学生的宿舍ID宿舍已住人数1 studentMapper.updateDormId(studentId, dormId); dormMapper.incrementOccupied(dormId); return 分配成功; }方法上加了 Transactional 保证事务性student 表更新和 dorm 表更新要么都成功要么都回滚。这里有个隐蔽的问题第一行查宿舍和后面更新之间如果有另一个请求也操作了同一个宿舍就可能出现超卖。解决方案是给 dorm 表加乐观锁版本号或者直接改造成先更新后判断的写法// 使用 UPDATE ... WHERE occupied capacity 原子判断 int rows dormMapper.assignIfAvailable(dormId); if (rows 0) { return 该宿舍已满无法分配; }对应的 XML 语句是UPDATE dorm SET occupied occupied 1 WHERE id #{dormId} AND occupied capacity。数据库层面的条件更新能天然避免并发问题这个写法在真实项目里很值得推广。退宿逻辑则是反操作清空学生的 dorm_id宿舍 occupied 减一同样要包在事务里。3.3 报修工单从提交到完成的状态流转报修模块是这个系统里最有流程感的功能它模拟了一个简单的工单状态机。学生提交报修后状态为待派单宿管接到单子指派给维修人员后变成维修中维修完成确认后变成已完成。public class RepairService { // 学生提交报修状态初始为待派单 public void submit(RepairOrder order) { order.setStatus(1); // 1待派单 2维修中 3已完成 order.setCreateTime(new Date()); repairOrderMapper.insert(order); } // 宿管派单指派维修人员并更新状态 public void dispatch(Long orderId, String worker) { repairOrderMapper.updateWorkerAndStatus(orderId, worker, 2); } // 确认完成写入完成时间 public void finish(Long orderId) { repairOrderMapper.updateStatusAndTime(orderId, 3, new Date()); } // 列表查询按学号或状态过滤 public ListRepairOrder list(RepairQuery query) { if (query.getStudentId() ! null) { return repairOrderMapper.selectByStudent(query.getStudentId()); } return repairOrderMapper.selectByStatus(query.getStatus()); } }状态流转的每一步都对应一条 update 语句业务规则非常直白。列表查询这里有个值得留意的设计学生角色登录后只能看到自己提交的工单宿管能看到全部。如果原始代码里没做这个数据隔离二次开发时需要把当前登录用户的 ID 传到查询条件里。前端页面则是简单的表格展示加状态标签学生端提交报修时用下拉框选宿舍房号文本框描述故障现象提交成功后在列表里能看到当前状态是待派单还是维修中。这个模块适合作为理解整个系统 CRUD 的样板一张表、三个状态、两个角色的操作入口。理解它的链路后给系统加卫生检查晚归登记这类模块时照着这个套路复制改造就行。4. 本地跑通从建库到启动的完整操作步骤4.1 环境准备在本地跑这个项目需要准备 JDK 8、Maven 3.6 以上、MySQL 5.7 或 8.0以及一个 Java IDE。我在 IDEA 里导入项目后做的第一件事是检查 Maven 仓库能否正常下载依赖因为这项目拉取了 Spring Boot、MyBatis、Thymeleaf 等多个依赖包网络不好时容易卡住。JDK 版本这里要提醒一下Spring Boot 2.x 官方支持到 JDK 8 和 11如果你本机装的是 JDK 17编译时大概率会报一些库不兼容的问题。我一般会单独装一个 JDK 8 并配置到 IDEA 的 Project Structure 里省去来回折腾。4.2 初始化数据库项目里会附带一个 .sql 脚本打开后能看到建库、建表、插入初始数据的完整语句。核心步骤是在 MySQL 里先建库再导入脚本mysql -u root -p -e CREATE DATABASE IF NOT EXISTS dormitory DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; mysql -u root -p dormitory dormitory.sql建库时把字符集指定为 utf8mb4 是个好习惯它能完整存储学生的中文姓名和报修内容里的特殊符号。如果项目脚本里没写建库语句那在 MySQL 客户端手动执行这两条命令也能达到同样效果。导入完成后可以用一条简单的查询验证数据是否就绪SELECT COUNT(*) FROM student; -- 预期返回初始学生数量我一般还会顺手看一眼 user 表里初始管理员账号的密码是什么样的。大多数类似项目的初始密码是admin123而且存的是 MD5 值。知道初始账号是后面登录验证的关键不然经常卡在第一步进不去系统。4.3 修改配置并启动项目数据库就绪后需要把项目的数据库连接配置改成你本机的用户名密码。配置文件在src/main/resources/application.yml核心改动如下spring: datasource: url: jdbc:mysql://localhost:3306/dormitory?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的数据库密码 thymeleaf: cache: falseurl 里三个参数分别解决中文乱码、驱动时区报错和字符集问题。如果你本地 MySQL 是 8.0 版本驱动类需要是com.mysql.cj.jdbc.DriverserverTimezone 参数尤其不能省否则启动时会直接报一个关于时区的 SQLException。配置改好后启动方式有两种。命令行执行mvn spring-boot:run能直接看到日志输出在 IDEA 里则找到启动类也就是名字类似Application.java的类右键 Run 即可。看到日志里出现Started Application in x.xx seconds就说明启动成功浏览器访问http://localhost:8080/login就能看到登录页用初始账号登进去就能操作各个功能了。5. 避坑记录本地跑这类管理系统最常见的五个报错5.1 连接 MySQL 8.0 时报时区错误现象是启动项目时控制台抛出The server time zone value йʱ is unrecognized后面还跟着一串乱码。原因是 MySQL 8.0 的 JDBC 驱动要求连接串里显式指定时区。解决办法是在数据源 URL 末尾追加serverTimezoneAsia/Shanghai这是最省事的处理方式。当然也可以在 MySQL 里执行set global time_zone 8:00但改连接串只需要动一处可移植性更好我后来都统一用前者。5.2 8080 端口被占用导致启动失败现象是启动日志末尾报Port 8080 was already in use常见于本机有其他 Java 进程占用端口。我一般先确认端口占用情况再决定怎么处理# Linux / macOS 查端口占用 lsof -i :8080 # Windows 查端口占用 netstat -ano | findstr 8080找到占用进程后要么结束它要么直接改项目的端口配置在 application.yml 里增加一句server: port: 8081重启即可生效。开发环境改端口是合法的上线部署时才需要考虑统一规范。5.3 页面中文全部显示为问号现象是学生姓名、报修内容在页面上全部变成???但数据库里的值又是正常的。原因多数出在数据库连接串没指定字符集或者建表时默认字符集不是 utf8mb4。解决方法是先确认库表字符集再检查连接串里是否带了useUnicodetruecharacterEncodingutf8。这两个地方同时改对重启后乱码基本能解决。另外导入 SQL 脚本时要注意脚本文件本身的编码格式用 IDE 打开看有没有乱码用 UTF-8 编码重新保存一次最保险。5.4 登录成功但页面 CSS 样式全部丢失现象是输入账号密码能进首页但页面像没穿衣服一样所有布局都是裸的。原因是登录拦截器把所有请求都拦截了包括/static/**下的 CSS 和 JS 文件浏览器拿不到静态资源样式自然全丢。解决方式是在注册拦截器时放行静态资源和登录接口Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns(/**) .excludePathPatterns(/login, /static/**, /css/**, /js/**); }注意/static/**的写法它匹配 static 目录下的所有文件。如果项目里用的是自定义静态资源路径放行路径也要跟着调整。这个问题的隐蔽性在于功能正常、只是样式没了特别容易被误解成前端代码问题实际上根子在后端拦截器配置上。5.5 宿舍分配后人数对不上现象是宿舍管理列表里显示已住 2 人 / 可住 4 人但实际查看只有 1 个学生住在里面。原因是退宿功能或者学生删除操作没有同步更新 dorm 表的 occupied 字段比如退宿时只清空了 student.dorm_id却忘了执行 occupied 减一。解决方法是先写一条对账 SQL 找出脏数据SELECT d.id, d.occupied, COUNT(s.id) AS actual FROM dorm d LEFT JOIN student s ON s.dorm_id d.id GROUP BY d.id, d.occupied HAVING d.occupied ! actual;查出不一致的记录后手动修正。更根本的做法是把分配和退宿都收敛到 Service 层统一处理事务内同时更新学生表和宿舍表避免各处分散写 update 语句导致漏改。这算是个典型的字段冗余带来的维护代价案例理解了这点就知道为什么前面强调一定用事务去包裹分配逻辑了。6. 二次开发给系统加一个新模块的最小完整路径接手这类管理系统后最常见的需求就是加新功能。以我改造过的晚归登记模块为例完整的开发路径是建表 → 写实体类 → 写 Mapper 接口和 XML → 写 Service → 写 Controller → 写页面六个步骤一个都不能少但这个套路一旦走熟了一个模块大概半天就能搞定。CREATE TABLE late_return ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, record_date DATE NOT NULL, reason VARCHAR(100), register_by INT COMMENT 登记人登录用户的ID, create_time DATETIME );RestController RequestMapping(/late) public class LateReturnController { Autowired private LateReturnService lateReturnService; PostMapping(/add) public Result add(RequestBody LateReturn record) { // 从 Session 取当前登录用户作为登记人 User user (User) request.getSession().getAttribute(loginUser); record.setRegisterBy(user.getId()); lateReturnService.save(record); return Result.ok(登记成功); } }在 Service 层可以做一条补充规则同一学生同一天只能登记一次晚归有重复记录直接拒绝。这个规则写在 Mapper 层用唯一索引实现也行但加在 Service 里可以给前端返回更友好的提示信息。除了加新模块另一个值得做的改造是把登录密码从 MD5 替换成 BCryptSpring Security 里自带了 BCryptPasswordEncoder引入后只需改注册和登录两处校验代码数据表里的密码字段长度要扩到 60 位。动手之前建议先把整个项目从头到尾跑一遍把三个角色的功能都点一遍用表格记录每个功能的 URL、参数、预期结果。我吃过一次亏接到一个报修模块的改造需求结果没先摸清原有列表接口的查询参数加完字段才发现和前端页面的筛选条件对不上前后返工了两天。从那以后我每次接手这种全栈项目都强制自己先走一遍登录 → 核心操作 → 登出闭环再动手写第一行业务代码。希望这个习惯和这套改造路径能帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。