简介这是一套基于Java Web技术栈开发的学生宿舍管理系统实战项目面向Java初学者、Web开发入门者及高校课程设计学生解决传统宿舍管理中信息分散、操作低效、人工易错等实际问题。资源采用B/S架构整合JSP动态页面、Java Servlet业务逻辑、MySQL关系型数据库与Tomcat应用服务器覆盖从登录验证、宿舍增删改查到学生入住状态管理的完整功能闭环。压缩包共80个文件含26个JSP页面实现交互视图、22个编译后Class文件与11个Java源码含Servlet和JavaBean、10个CSS样式文件、7个XML配置文件如web.xml与数据库连接配置以及1个SQL建表脚本整体3.9MB结构清晰便于逐层理解MVC分层设计。已有2020人学习下载读者可直接部署运行深入掌握JDBC数据库操作、JSP内置对象传参、Session会话管理及Tomcat环境配置等核心技能并参考完整目录组织方式开展二次开发。1. 为什么还在用 JavaJSPMySQL 做学生宿舍管理系统不是过时而是稳得有道理某高校信息中心去年上线的宿舍管理后台没上 Spring Boot没用 Vue 前端也没碰云原生——就是一套 Java Servlet JSP MySQL 搭建的 B/S 系统至今稳定运行 14 个月日均处理 3200 条床位分配、维修申报、晚归登记请求数据库单表最大 87 万行Tomcat 进程内存占用长期压在 420MB 以内。这不是怀旧是典型场景下的理性选择用户角色固定宿管员、辅导员、学生、业务流程清晰入住→调宿→退宿→报修→查寝、并发峰值明确开学周/期末周集中操作、运维能力有限校方 IT 人员仅 2 名无专职 DevOps。Java 的强类型保障逻辑严谨性JSP 的服务端渲染天然适配内网低带宽环境页面平均加载 0.8sMySQL 的事务一致性兜住关键数据如床位状态变更必须同步更新学生档案与房间台账。如果你正面临一个「不需要微服务、不追求高并发、但要求零数据错漏」的校园管理类系统交付这套组合不是备选而是经过十年实操验证的基准方案。2. 从零搭建用最简依赖跑通核心流程2.1 环境准备JDK 8 Tomcat 9 MySQL 5.7 的最小可行栈这是当前教育类项目最稳妥的版本组合。JDK 8 提供长期支持且兼容所有主流 JSP 标签库Tomcat 9 对 Servlet 4.0 的支持足够覆盖 JSP 2.3 特性如 EL 表达式增强MySQL 5.7 的 InnoDB 事务隔离级别REPEATABLE-READ能精准控制床位锁定逻辑。注意不要升级到 JDK 17 或 Tomcat 10——JSP 编译器对新模块系统的兼容性问题会直接导致org.apache.jasper.JasperException: Unable to compile class for JSP这类玄学报错某实验室曾为此多花 3 天排查。# 下载并解压Linux 示例 wget https://download.oracle.com/java/18/latest/jdk-18_linux-x64_bin.tar.gz tar -xzf jdk-18_linux-x64_bin.tar.gz # ❌ 错误示范直接用此 JDK 启动 Tomcat 9 # ✅ 正确做法降级使用 OpenJDK 8 sudo apt install openjdk-8-jdk export JAVA_HOME/usr/lib/jvm/java-8-openjdk-amd64提示Tomcat 的conf/context.xml中必须关闭 session persistence注释掉Manager classNameorg.apache.catalina.session.PersistentManager否则在宿舍系统高频刷新查寝列表时会因序列化 Session 中的ListStudent导致NotSerializableException。2.2 数据库建模三张表撑起主干业务宿舍管理的核心矛盾是「人-房-状态」的强约束关系。我们只建三张物理表非范式理论最优但实操最稳表名关键字段业务约束说明studentid (PK), name, stu_id (UNIQUE), room_id (FK), status (ENUM: in,out,transferring)stu_id强制唯一避免同一学生重复入住status用 ENUM 而非 INT防止非法状态写入dormitoryid (PK), building_no, floor, room_no, capacity, current_occupancycurrent_occupancy由程序维护非触发器避免并发更新丢失见 4.2 节repair_recordid (PK), student_id, room_id, content, status (ENUM: pending,done,rejected), create_time报修记录必须关联student_id和room_id双维度方便按楼栋或学生追溯-- 创建 student 表关键约束已加注 CREATE TABLE student ( id bigint(20) NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL, stu_id char(10) NOT NULL UNIQUE COMMENT 学号全局唯一, room_id bigint(20) DEFAULT NULL COMMENT 当前宿舍ID可为空未分配, status enum(in,out,transferring) NOT NULL DEFAULT out COMMENT 入住状态, PRIMARY KEY (id), KEY idx_stu_id (stu_id), KEY idx_room_id (room_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 初始化一条测试数据宿管员账号 INSERT INTO student (name, stu_id, room_id, status) VALUES (admin, 9999999999, NULL, in);参数说明CHAR(10)存储学号比VARCHAR(10)更高效——学号长度固定且避免 VARCHAR 的变长存储开销utf8mb4是硬性要求否则学生姓名含生僻字如「䶮」「犇」时会存成?。2.3 JSP 页面骨架用标准标签库替代 scriptletJSP 中禁止出现% ... %脚本片段scriptlet全部改用 JSTL EL。某跨平台系统曾因在list.jsp中写while(rs.next())导致模板与业务逻辑强耦合后续增加导出 Excel 功能时被迫重写全部页面。正确姿势% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % % taglib prefixfmt urihttp://java.sun.com/jsp/jstl/fmt % !-- list.jsp 片段展示待处理报修 -- table classtable theadtrth学生/thth房间/thth问题/thth时间/thth操作/th/tr/thead tbody c:forEach items${repairList} varr tr td${r.studentName}/td td${r.buildingNo}-${r.floor}F-${r.roomNo}/td td${r.content}/td tdfmt:formatDate value${r.createTime} patternyyyy-MM-dd HH:mm//td td a hrefrepair?actiondoneid${r.id} classbtn btn-sm btn-success完成/a /td /tr /c:forEach /tbody /table逻辑说明${repairList}由 Servlet 的request.setAttribute(repairList, list)注入JSTL 的c:forEach在服务端完成循环渲染避免前端 JavaScript 拼接 HTML 字符串引发 XSS 风险fmt:formatDate统一日期格式省去 JS 端new Date().toLocaleString()的兼容性坑。3. 业务逻辑落地用 Servlet 分层实现关键用例3.1 入住流程事务边界必须卡在 Service 层学生入住需同时更新student表绑定房间和dormitory表增加入住数且必须保证原子性。错误做法是把 SQL 写在 Servlet 中手动 commit正确做法是封装为 Service 方法并用 JDBC 手动事务控制// DormService.java public boolean checkIn(long studentId, long roomId) { Connection conn null; PreparedStatement ps1 null, ps2 null; try { conn dataSource.getConnection(); conn.setAutoCommit(false); // 关键关闭自动提交 // 更新学生状态 ps1 conn.prepareStatement(UPDATE student SET room_id ?, status in WHERE id ?); ps1.setLong(1, roomId); ps1.setLong(2, studentId); int row1 ps1.executeUpdate(); // 更新宿舍入住数注意此处用 UPDATE ... SET current_occupancy current_occupancy 1 ps2 conn.prepareStatement(UPDATE dormitory SET current_occupancy current_occupancy 1 WHERE id ?); ps2.setLong(1, roomId); int row2 ps2.executeUpdate(); if (row1 1 row2 1) { conn.commit(); // 两步都成功才提交 return true; } else { conn.rollback(); // 任一失败则回滚 return false; } } catch (SQLException e) { try { if (conn ! null) conn.rollback(); } catch (SQLException ignored) {} throw new RuntimeException(入住失败, e); } finally { closeResources(ps1, ps2, conn); } }参数说明current_occupancy current_occupancy 1是核心技巧——避免先SELECT再UPDATE的并发覆盖问题如两个宿管同时给同一房间分人可能都读到current_occupancy3然后都写4实际应为5。3.2 报修处理用 Filter 实现权限拦截宿舍系统中学生只能提交报修宿管才能处理。用WebFilter拦截/repair请求根据 Session 中的角色跳转// RepairFilter.java WebFilter(/repair) public class RepairFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; HttpSession session request.getSession(false); if (session null || session.getAttribute(role) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } String role (String) session.getAttribute(role); String action request.getParameter(action); // 学生只能提交不能处理 if (student.equals(role) (done.equals(action) || reject.equals(action))) { request.setAttribute(error, 权限不足); request.getRequestDispatcher(/error.jsp).forward(request, response); return; } chain.doFilter(req, resp); } }逻辑说明Filter 在 Servlet 执行前介入比在每个 Servlet 中写if(!admin.equals(role))更安全——避免遗漏某个接口session.getAttribute(role)的值应在登录成功后由LoginServlet显式设置而非依赖 Cookie 或 URL 参数。3.3 查寝统计用原生 SQL 实现动态条件查询辅导员需要按楼栋、楼层、日期范围筛选查寝记录。JSP 中用c:if拼接 SQL 会导致 SQL 注入正确做法是用PreparedStatement动态构建 WHERE 条件// DormCheckService.java public ListCheckRecord searchByConditions(String buildingNo, Integer floor, Date startDate, Date endDate) { StringBuilder sql new StringBuilder(SELECT * FROM dorm_check WHERE 11); ListObject params new ArrayList(); if (buildingNo ! null !buildingNo.trim().isEmpty()) { sql.append( AND building_no ?); params.add(buildingNo); } if (floor ! null) { sql.append( AND floor ?); params.add(floor); } if (startDate ! null) { sql.append( AND check_time ?); params.add(startDate); } if (endDate ! null) { sql.append( AND check_time ?); params.add(endDate); } // 执行查询使用 Apache Commons DbUtils 简化 QueryRunner runner new QueryRunner(dataSource); return runner.query(sql.toString(), new BeanListHandler(CheckRecord.class), params.toArray()); }参数说明params.toArray()将动态参数列表转为Object[]传入query()确保占位符?与参数顺序严格对应BeanListHandler自动映射字段名到 JavaBean 属性省去手动rs.getString(xxx)。4. 避坑指南生产环境踩过的 5 个真实坑4.1 现象学生提交报修后页面显示「操作成功」但数据库无记录原因RepairServlet中调用了response.sendRedirect()跳转但未在跳转前return导致后续代码如conn.close()仍执行而连接池已回收该连接INSERT语句实际未提交。解决sendRedirect()后必须立即return或改用request.getRequestDispatcher().forward()服务端跳转不中断请求流。4.2 现象多个宿管同时给 301 房间分配学生current_occupancy最终比实际人数少 1原因虽然用了UPDATE ... SET current_occupancy current_occupancy 1但未加WHERE current_occupancy capacity条件超员分配后current_occupancy仍被错误递增。解决在 UPDATE 语句中加入容量校验UPDATE dormitory SET current_occupancy current_occupancy 1 WHERE id ? AND current_occupancy capacity并检查executeUpdate()返回值是否为 1为 0 表示已满员。4.3 现象JSP 页面中文乱码显示为??原因Tomcat 的conf/server.xml中Connector缺少URIEncodingUTF-8导致 GET 请求参数如?name张三被 ISO-8859-1 解码。解决修改server.xmlConnector port8080 protocolHTTP/1.1 URIEncodingUTF-8 ... /4.4 现象登录后访问其他页面Session 中的user对象为 null原因web.xml中未配置session-configsession-timeout30/session-timeout/session-config且浏览器禁用 Cookie导致 JSESSIONID 无法传递。解决强制启用 URL 重写在web.xml添加session-configtracking-modeURL/tracking-mode/session-config并在所有链接用c:url value/list.jsp/生成带;jsessionidxxx的 URL。4.5 现象MySQL 连接池耗尽Tomcat 日志报Cannot get a connection, pool error Timeout waiting for idle object原因BasicDataSource的maxActive设为 20但某次批量导入 500 条学生数据时每个学生 INSERT 单独开启连接未释放。解决① 将批量操作改为单条INSERT INTO ... VALUES (...),(...),...② 在context.xml中设置removeAbandonedOnBorrowtrue和removeAbandonedTimeout60自动回收超时连接。5. 进阶技巧让老技术应对新需求5.1 用 JSP Fragment 实现模块化页面复用当系统需要为不同角色学生/宿管/辅导员展示差异化首页时避免复制粘贴整页代码。创建fragments/header.jspf注意.jspf后缀%-- fragments/header.jspf --% nav classnavbar navbar-expand-lg navbar-light bg-light a classnavbar-brand href#宿舍管理系统/a div classnavbar-nav a classnav-link hrefindex.jsp首页/a c:if test${role admin} a classnav-link hrefmanage-room.jsp房间管理/a /c:if c:if test${role student} a classnav-link hrefrepair-form.jsp报修/a /c:if /div /nav在index.jsp中引入% include filefragments/header.jspf % div classcontainer mt-4 h2欢迎回来${userName}/h2 !-- 其他内容 -- /div优势.jspf文件不会被 Tomcat 直接编译为 Servlet仅作静态包含c:if标签在服务端渲染时动态控制菜单项比前端 JS 判断更安全避免角色伪造。5.2 用 Log4j2 实现操作审计日志宿舍系统需留存关键操作如调宿、退宿、报修处理的完整轨迹。在DormService的关键方法中添加日志// 使用 Log4j2 的 Logger非 System.out private static final Logger logger LogManager.getLogger(DormService.class); public boolean transferStudent(long studentId, long newRoomId) { // ... 业务逻辑 ... if (success) { logger.info(学生{}调宿成功原房间{}→新房间{}, studentId, oldRoomId, newRoomId); } else { logger.warn(学生{}调宿失败目标房间{}已满, studentId, newRoomId); } return success; }log4j2.xml配置分离审计日志Appenders RollingFile nameAuditLog fileNamelogs/audit.log filePatternlogs/audit-%d{yyyy-MM-dd}-%i.log.gz PatternLayout pattern%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n/ Policies TimeBasedTriggeringPolicy / SizeBasedTriggeringPolicy size10 MB/ /Policies /RollingFile /Appenders Loggers Logger namecom.example.dorm.service.DormService levelinfo additivityfalse AppenderRef refAuditLog/ /Logger /Loggers价值点审计日志独立于应用日志方便运维人员快速定位责任事件%d{HH:mm:ss.SSS}精确到毫秒满足「同一秒内多次操作」的时序追溯。5.3 用 MySQL 事件调度器实现自动清理历史报修记录超过 180 天需归档避免repair_record表无限膨胀。启用 MySQL 事件调度器-- 开启事件调度器 SET GLOBAL event_scheduler ON; -- 创建每月 1 号凌晨 2 点归档旧数据的事件 CREATE EVENT archive_old_repairs ON SCHEDULE EVERY 1 MONTH STARTS 2024-01-01 02:00:00 DO INSERT INTO repair_archive SELECT * FROM repair_record WHERE create_time DATE_SUB(NOW(), INTERVAL 180 DAY); DELETE FROM repair_record WHERE create_time DATE_SUB(NOW(), INTERVAL 180 DAY);血泪经验事件中的DELETE必须加WHERE条件且确保索引有效create_time字段需建索引否则全表扫描会锁表数分钟首次执行前先用SELECT COUNT(*)验证条件范围避免误删。我坚持用这套组合不是因为拒绝新工具而是深知在校园信息化场景里稳定性比炫技重要可维护性比性能数字重要让非专业人员也能看懂日志、改懂 SQL、重启服务不慌才是真正的工程价值。希望帮到你。本文还有配套的精品资源点击获取