简介这套基于Java的学生宿舍管理系统以SpringBoot为后台框架采用Bootstrap构建前端界面搭配MySQL数据库适合高校毕业设计、课程实训以及刚接触后台管理系统的Java开发者。系统按角色划分三个入口管理员可对学生、楼宇、宿舍、入住、宿管和管理员信息进行增删改查并支持Excel表格导入导出宿管负责学生、宿舍与入住相关操作学生端则提供宿舍信息查看功能登录时还包含验证码校验。资源包共661个文件以JS、CSS、HTML等前端资源与Java源码、Class编译文件为主体另有SQL数据库脚本、XML配置文件、项目文档和少量演示视频压缩包整体72.37MB。压缩包内预置了已修改好的MySQL5和MySQL8两套项目方便不同数据库版本环境直接选用。目前已有467人学习下载适合用于毕业设计二次开发或学习SpringBoot后台管理开发技术栈。1. 学生宿舍管理系统不写花架子就解决床位与查寝那点事学生宿舍管理系统单看名字平平无奇但它恰恰是Java毕业设计里最经得起推敲的一类项目——需求明确、表结构清楚、前台后台都有活干。某高校宿管科之前用Excel登记床位学生调宿时信息更新不及时楼管阿姨统计晚归要翻纸质表格管理员被问得最多的一句话就是“哪个床位还空着”。这套基于Java的Web系统把学生信息、床位分配、入住迁出、报修维修、访客登记全部放进同一个后台用Servlet处理请求、JSP渲染页面、MySQL持久化数据跑通一遍基本还原了宿管员的真实工作流。适合正在做毕设的人也适合想找一套结构完整、能把Java Web知识串起来的源码来复现的同学。下面拆解的是最常见的JSPServlet版本整体套路和网上流传的毕业设计源码包保持一致。2. 技术选型与数据库设计先把地基夯实2.1 为什么是JSPServlet而不是Spring Boot很多毕设同学一上来就问为什么不用Spring Boot我的回答是不是不能用是这套源码更适合用来“讲清楚原理”。宿舍管理系统是典型的课程设计/毕业设计题目导师想看到的是你对Java Web基本功的掌握——JDBC连接、请求转发、Session管理、Filter过滤这些在Servlet里都是明明白白写在代码里的。Spring Boot把Tomcat内嵌、自动配置、依赖管理全部封装掉学生拿着它答辩被问到“请求是怎么进到Controller的”往往答不上来。JSPServlet这套方案的意义在于每一层都裸露在外出了错能顺着堆栈找到根因。配合MySQL和JSTL标签库既不用引入重型框架又能把MVC分层、DAO模式这些学校强调的知识点全部体现出来。对想要一份“能讲清楚、能演出来”的源码的人来说它是性价比最高的选择。下面这张表是这套源码的常见环境组合版本不必完全一致但跨度不建议太大。组件推荐版本说明JDK1.8毕业设计和课程设计最通用的版本MySQL5.75.7对UTF-8和事务支持稳定Tomcat8.5对应Servlet 3.1规范够用且不易报错JSTL1.2配合JSP标签库渲染列表数据库驱动mysql-connector-java 5.1.49注意必须和MySQL 5.7匹配2.2 功能模块拆解与六张核心表从我拆过的多套源码来看宿舍管理系统的功能域基本是固定的系统登录、学生管理、宿舍楼栋管理、床位分配、入住迁出、报修管理、访客登记。再讲究一点的还会加楼栋管理员角色和晚归记录但核心就是前六个。数据库设计对应关系如下模块数据表关键字段管理员登录adminadmin_id, username, password学生信息studentstudent_id, name, gender, major, phone宿舍楼栋dormitorydorm_id, building_no, floor, room_no, capacity床位分配bed_assignmentassign_id, student_id, dorm_id, bed_no报修管理repair_orderrepair_id, student_id, dorm_id, status访客登记visitor_loglog_id, visitor_name, student_id, visit_time主外键关系很直观bed_assignment的student_id关联student表dorm_id关联dormitory表repair_order通过student_id找到报修人通过dorm_id定位宿舍。这样设计的好处是查询链路短比如“查出某栋楼还有多少个空床位”一条JOIN就能完成。2.3 表结构初始化脚本源码包里的SQL脚本通常是这样的写法不复杂但字段坑很多CREATE DATABASE IF NOT EXISTS dorm_db DEFAULT CHARSET utf8mb4; USE dorm_db; CREATE TABLE admin ( admin_id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL COMMENT MD5加密存储 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE dormitory ( dorm_id INT PRIMARY KEY AUTO_INCREMENT, building_no VARCHAR(10) NOT NULL, floor INT NOT NULL, room_no VARCHAR(10) NOT NULL, capacity INT DEFAULT 4 COMMENT 标准间床位数, UNIQUE KEY uk_room (building_no, floor, room_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE bed_assignment ( assign_id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, dorm_id INT NOT NULL, bed_no TINYINT NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_dorm_bed (dorm_id, bed_no), CONSTRAINT fk_bed_student FOREIGN KEY (student_id) REFERENCES student(student_id), CONSTRAINT fk_bed_dorm FOREIGN KEY (dorm_id) REFERENCES dormitory(dorm_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这段脚本里最值得注意的就是bed_assignment表的唯一键uk_dorm_bed。它约束了“同一栋房同一床号只能被一个学生占用”这是防止床位重复分配的数据库兜底措施。很多源码里没有这个唯一键只在Java代码里做判断并发一高就会翻车后面避坑章节会专门讲。另外所有表都用了utf8mb4而不是utf8前者能正确存储手机号里可能会出现的特殊字符也兼容表情符号这是个容易被忽略的细节。2.4 MVC三层一次请求从浏览器到数据库的完整链路这套系统的工程结构遵循典型的MVC分层View层JSP页面放在webapp目录下用JSTLEL表达式渲染列表和表单Controller层Servlet接收请求、调用业务方法、转发或重定向视图Model层DAO接口和实现类封装JDBC操作只有数据访问逻辑。对应到一次登录请求链路是LoginServlet接收表单提交 → 调用LoginDAO的queryByUsernameAndPassword方法 → 通过JDBC执行SQL并比对密码 → 成功则将用户信息放入session → 转发到首页失败则重定向回登录页并携带错误提示。WebServlet(/login) public class LoginServlet extends HttpServlet { private LoginDAO loginDAO new LoginDAO(); Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String username request.getParameter(username); String password MD5Util.md5(request.getParameter(password)); Admin admin loginDAO.findByUsernameAndPassword(username, password); if (admin ! null) { request.getSession().setAttribute(admin, admin); response.sendRedirect(request.getContextPath() /index.jsp); } else { request.setAttribute(error, 用户名或密码错误); request.getRequestDispatcher(/login.jsp).forward(request, response); } } }逻辑说明doPost里先强制请求编码为UTF-8中文用户名不会乱码密码用MD5工具类加密后再比对数据库里不存明文这是源码包里比较规范的处理。参数说明request.getContextPath()返回的是项目上下文路径比如/DormMS加上它再拼/index.jsp才能保证在多应用部署时不跳错页。用sendRedirect登录成功会发起新请求避免用户刷新页面时重复提交登录表单。3. 核心功能落地从JDBC连接到宿舍分配的关键代码3.1 JDBC连接池别再用DriverManager硬怼了早期源码里常见的是DriverManager.getConnection(url, user, password)每次请求都新建连接访问稍微一多MySQL就会报“Too many connections”。稍微规范一点的源码会用连接池常见做法是引入Druid或者dbcp。我一般会在拆解源码时把它改成Druid配置也简单。src目录下的db.propertiesdriverClassNamecom.mysql.jdbc.Driver urljdbc:mysql://localhost:3306/dorm_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai usernameroot password123456 initialSize5 maxActive20 maxWait3000参数说明characterEncodingutf8必须写不写的话传中文参数到MySQL会乱码useSSLfalse是为了避免MySQL 5.7连接时输出大段SSL告警serverTimezone指定时区防止时间字段解析报错。连接池的initialSize表示启动时预创建5个连接maxActive是最大连接数宿舍管理系统并发量不大20足够。对应的工具类public class DruidUtil { private static DataSource dataSource; static { try { Properties props new Properties(); props.load(DruidUtil.class.getClassLoader() .getResourceAsStream(db.properties)); dataSource DruidDataSourceFactory.createDataSource(props); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }逻辑说明静态代码块在类第一次加载时执行连接池只初始化一次后面所有DAO都复用同一个DataSource。getResourceAsStream读取的是classpath根目录下的属性文件db.properties放在src根目录即可。如果启动时抛出ExceptionInInitializerError优先检查属性文件名是否拼错其次检查驱动版本是否和MySQL匹配。3.2 登录鉴权策略是写不了的宿舍管理系统虽然小但也要控制访问权限。学生管理、报修删除这些操作必须要求管理员登录后才能访问实现手段就是一个Filter。WebFilter(urlPatterns {/*}) public class LoginFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; String uri request.getRequestURI(); if (uri.endsWith(/login.jsp) || uri.endsWith(/login) || uri.contains(/statics/) || uri.equals(request.getContextPath() /)) { chain.doFilter(req, resp); return; } Object admin request.getSession().getAttribute(admin); if (admin null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); } }逻辑说明白名单里放行了登录页、登录请求、静态资源CSS、JS、图片其余所有请求都要先检查session里有没有admin对象。没有就重定向回登录页。这里有一个细节uri.contains(/statics/)放行静态资源后JSP页面里引用的bootstrap和jquery才能正常加载不然会出现“页面歪了但接口正常”的诡异现象。参数说明WebFilter(urlPatterns {/*})拦截所有路径注意这个通配符写法是Java EE标准不要写成/那只会拦截根路径。放行登录页的判断用了endsWith而不是equals是因为项目上下文路径会变化用endsWith更稳。3.3 宿舍分配逻辑事务里做“先查后改”宿舍分配是整个系统里最核心的一段代码也是最容易出问题的。错误做法是先查询床位是否空闲如果空闲再执行insert。如果两个管理员同时操作或者一个操作被重复提交就会出现同一床位分给两个人的情况。下面是带事务保护的正确写法public boolean assignBed(int studentId, int dormId, int bedNo) { Connection conn null; try { conn DruidUtil.getConnection(); conn.setAutoCommit(false); // 先锁定床位防止并发分配 String lockSql SELECT assign_id FROM bed_assignment WHERE dorm_id ? AND bed_no ? FOR UPDATE; PreparedStatement lockPs conn.prepareStatement(lockSql); lockPs.setInt(1, dormId); lockPs.setInt(2, bedNo); ResultSet rs lockPs.executeQuery(); if (rs.next()) { conn.rollback(); return false; } String insertSql INSERT INTO bed_assignment (student_id, dorm_id, bed_no) VALUES (?, ?, ?); PreparedStatement insertPs conn.prepareStatement(insertSql); insertPs.setInt(1, studentId); insertPs.setInt(2, dormId); insertPs.setInt(3, bedNo); insertPs.executeUpdate(); conn.commit(); return true; } catch (SQLException e) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } return false; } finally { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException e) {} } }逻辑说明核心是SELECT ... FOR UPDATE行级锁。它把指定宿舍和床号对应的记录锁住其他事务要操作同一行必须等待当前事务结束。先查询如果查到记录说明床位已被占用回滚并返回false查不到则insert分配记录并提交。这一套在事务里完成要么成功要么什么都不变。参数说明setAutoCommit(false)后必须手动提交或回滚否则连接关闭时可能自动提交一个不完整的操作。finally里恢复autoCommit再接回连接池防止连接池中的连接状态残留。数据库端要有UNIQUE KEY uk_dorm_bed(dorm_id, bed_no)兜底哪怕Java代码出了纰漏数据库也会拒绝重复插入。4. 常见问题与避坑部署和运行阶段五个翻车现场4.1 启动Tomcat报ClassNotFoundException: com.mysql.jdbc.Driver这个异常在毕设季出现频率极高。现象就是Tomcat启动或者页面请求数据库时报找不到驱动类。原因基本可以锁定为JDBC驱动jar没有放进WEB-INF/lib目录。很多人把mysql-connector-java.jar放在项目的外部Library里IDEA里编译能通过但Tomcat运行时用的是web应用自己的classpath外部jar根本不参与。解决方法是把mysql-connector-java-5.1.49.jar复制到webapp/WEB-INF/lib/下然后在IDEA中右键该jar选择Add as Library重新部署后再启动。如果是Maven项目检查pom.xml里有没有mysql依赖以及scope是否为provided——这个scope会导致Tomcat运行时看不到jar。4.2 插入的中文变成问号编码问题要三处统一数据库查出来的中文正常但前端表单提交到MySQL里全变成??。这是web项目最经典的老坑。原因在于编码没有在三个地方统一客户端页面编码、服务端接收编码、数据库存储编码。解决方法是先保证db.properties的URL里有characterEncodingutf8然后在web.xml里配置spring的CharacterEncodingFilter或者自写Filter统一设置request和response编码最后检查MySQL建表语句是否用了utf8mb4。注意一个容易漏的点如果表已经建好且是utf8可以执行ALTER TABLE student CONVERT TO CHARACTER SET utf8mb4转换不用重建表但学生表里已有的乱码不会自动恢复需要重新录入或者用SQL做数据修复。4.3 宿舍分配出现“一间房两个床号重复”现象是bed_assignment表里dorm_id bed_no出现两条记录页面显示该床位已分配给A同学但B同学的管理页面也提示分配成功。原因我在3.3节里已经点名了Java代码里先查再改但没有开启事务也没有加锁。两个管理员同时提交都查到床位空闲然后都执行insert。解决方法是三层保障第一层在报错时检查是不是部署了带FOR UPDATE的分配代码第二层看数据库表有没有唯一键uk_dorm_bed没有就补上ALTER TABLE bed_assignment ADD UNIQUE KEY uk_dorm_bed (dorm_id, bed_no)第三层给分配接口加防重复提交Token前端点击分配按钮后置灰10秒内不能再次提交。三层都做了这个问题才叫彻底解决。4.4 点击菜单后404绝对路径和相对路径混用Tomcat部署的应用上下文路径是/DormMS页面里写a href/studentList会把请求发到http://localhost:8080/studentList而不是http://localhost:8080/DormMS/studentList自然404。根因是路径写成了绝对路径且没有包含应用上下文。解决方法是所有前端路径都使用${pageContext.request.contextPath}前缀。JSP页面里写${pageContext.request.contextPath}/studentListServlet重定向时写response.sendRedirect(request.getContextPath() /index.jsp)。如果用了Bootstrap或者jQuery静态资源路径同样处理否则会出现HTML能打开但样式全丢的情况。4.5 登录后刷新页面又跳回登录页现象是登录成功进入首页按F5刷新就退出登录状态。原因有两个可能性第一是session生命周期太短Tomcat默认session超时是30分钟但如果某个Filter调用request.getSession()时传入了false可能拿到null第二是Filter白名单没放行首页转发刷新时请求被Filter拦截session里又取不到admin被重定向回login.jsp。排查方法是看浏览器控制台Network请求如果刷新时请求路径是/index.jsp但被302到/login.jsp说明Filter逻辑有问题检查白名单里是否包含了/index.jsp。如果确认session里确实有admin但每次都丢检查代码里有没有在某个Servlet里调用session.invalidate()或者session.removeAttribute(admin)——很多初学者会把退出登录的代码写在公共执行链路上。5. 从零部署到运行验证把源码跑起来有种常避坑的流程5.1 环境安装与版本检查部署之前先把环境理顺。推荐的都是稳定版本组合避免学生机上常见的“JDK 17跑Tomcat 8.5直接报错”这种问题。环境推荐版本安装注意JDK1.8安装完后命令行执行java -version确认MySQL5.7设置root密码并记好后面配置文件要用Tomcat8.5不要解压路径带中文和空格IDEA2020.1以上装Lombok插件如果源码里有注解版本组合的原则是JDK不要高于Tomcat支持的规范。JDK 8配Tomcat 8.5是最稳的组合JDK 11以上跑老项目偶尔会出现IllegalAccessError之类的玄学问题。5.2 IDEA导入源码并配置Tomcat操作步骤按这个顺序来能省去大部分时间IDEA选择Open选中源码目录的pom.xml或用Open as Project导入如果是普通Web工程则选中根目录后添加Web框架支持。等待Maven依赖下载完成后执行mvn clean package -DskipTests一次把编译错误提前暴露出来。打开File - Project Structure - Artifacts确认Web应用的输出类型是Web Application: Exploded。配置Tomcat Server点击Run - Edit Configurations - 添加Tomcat Server - LocalDeployment选项卡里选择dorm_war exploded。修改Application context为/或/DormMS建议设置为/DormMS和代码里的request.getContextPath()保持一致。启动Tomcat如果端口被占用在配置里把HTTP port改为8081。5.3 数据初始化SQL脚本导入MySQL执行初始化脚本的方式有图形化工具和命令行两种。命令行方式适合习惯终端的读者mysql -u root -p sql/init.sql图形化工具例如Navicat、MySQL Workbench则打开查询窗口整段粘贴执行。执行后验证表结构USE dorm_db; SHOW TABLES; SELECT * FROM admin;admin表里一般预置了一条账号用户名admin密码123456的MD5值。可以自己插入一条测试数据也可以用DBUtils写一个简易初始化器但直接执行SQL脚本是最快的。5.4 一条龙验证测试清单源码部署完不要直接说“跑通了”拿下面这张表走一遍测试能把大部分隐藏问题暴露出来测试项操作预期结果登录成功输入admin/123456跳转首页并显示当前管理员登录失败输入错误密码提示“用户名或密码错误”未登录访问直接访问/studentList被Filter重定向到登录页新增学生填写完整表单提交列表出现新记录分配床位选择宿舍和床号提示分配成功且床位状态变为占用分配重复床位分配同一个床位提示床位已占用报修流程提交报修并更新状态状态流转正常中文查询按姓名关键字搜索结果中中文不乱码5.5 答辩现场容易被追问的三个问题很多毕设项目运行没问题答辩却翻车原因是对自己写的代码一知半解。下面这三个问题是导师最爱问的第一个问题登录功能为什么使用Filter而不是Interceptor回答思路是Servlet规范里Filter是标准的Web组件不需要依赖Spring MVC的拦截器机制项目技术栈就是JSPServletFilter更贴合底层、轻量且容易解释原理。第二个问题查询空床位为什么不只查student表还需要关联dormitory回答思路是床位信息依赖宿舍表必须通过bed_assignment关联dormitory才能拿到楼栋、楼层、房间号避免反范式设计里出现的宿舍和床位对不上。第三个问题为什么用事务和唯一约束双重保障回答思路是Java代码层面的事务负责多步骤操作的一致性数据库唯一约束负责并发冲突时的最后兜底两者互为补充体现“数据最终以数据库为准”的设计思想。6. 扩展与优化让源码从“能跑”到“能演示”6.1 用POI把报修记录导出Excel答辩加分或者实际使用时导出Excel是最快见效的优化。Apache POI是Java操作Excel的常用库加一个依赖就能用。核心代码就两步创建Workbook、填充数据行。public void exportRepairExcel(HttpServletResponse response) throws IOException { ListRepairOrder list repairDAO.findAll(); XSSFWorkbook workbook new XSSFWorkbook(); Sheet sheet workbook.createSheet(报修记录); Row header sheet.createRow(0); header.createCell(0).setCellValue(报修单号); header.createCell(1).setCellValue(学生姓名); header.createCell(2).setCellValue(宿舍楼栋); int rowNum 1; for (RepairOrder order : list) { Row row sheet.createRow(rowNum); row.createCell(0).setCellValue(order.getRepairId()); row.createCell(1).setCellValue(order.getStudentName()); row.createCell(2).setCellValue(order.getBuildingNo() - order.getRoomNo()); } response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setHeader(Content-Disposition, attachment;filenamerepair.xlsx); workbook.write(response.getOutputStream()); workbook.close(); }这段代码主要展示了三个参数XSSFWorkbook生成的是xlsx格式sheet.createRow(0)表示第一行是表头response.setHeader(Content-Disposition, attachment;filenamerepair.xlsx)让浏览器弹出下载而不是直接打开。注意导出前一定要调用response.reset()防止之前输出的HTML内容混入Excel文件导致文件损坏。6.2 加一个操作日志表记录关键操作宿舍管理系统的敏感操作不少比如删除学生、调宿、修改密码。没有日志的话出问题查谁都查不到。加一个operation_log表建表语句很轻量CREATE TABLE operation_log ( log_id INT PRIMARY KEY AUTO_INCREMENT, admin_id INT NOT NULL, action VARCHAR(200) NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;在分配床位的Servlet里插入一行日志写入当前登录管理员ID和操作内容。这样演示系统时顺手展示一下“我刚才操作都有记录”比口头解释安全性更有说服力。6.3 演示时把“验证码”加上验证码是很多评委看一眼就会问“有没有做安全防护”的点。常见做法是用Session保存验证码字符串对比时忽略大小写String code request.getParameter(verifyCode); String sessionCode (String) request.getSession().getAttribute(verifyCode); if (!code.equalsIgnoreCase(sessionCode)) { request.setAttribute(error, 验证码错误); request.getRequestDispatcher(/login.jsp).forward(request, response); return; }核心逻辑就是一次会话内生成验证码存入Session提交时再比对用完即清除。不要用全局变量或静态变量存验证码否则两个管理员登录时会互相覆盖这是个很典型的低级错误。从那以后我每次接到宿舍管理或者类似的Java Web项目都会先花20分钟把数据库表结构和权限边界画清楚再动手写代码。床位数、在住人数、账期时段这些数字在开始编码之前就要从业务方那里确认不然等项目做一半才发现字段对不上返工成本比写代码还高。源码下载下来不要急着删先用初始化脚本建库、跑一遍系统、走完一条完整流程再去看核心代码——这个顺序能帮你少走一半弯路。希望这篇拆解能让你在拿到这套学生宿舍管理系统源码时心里更有底。本文还有配套的精品资源点击获取