简介这是一套基于ServletJSPMySQL技术栈开发的JavaWeb学生宿舍管理系统完整源码与配套教程面向计算机专业本科生及Java初学者适用于期末大作业、课程设计与毕业设计实践。系统功能覆盖学生入住登记、宿舍分配、报修管理、管理员权限控制等核心业务场景代码结构清晰、注释规范已通过本地编译验证并获98分高分评审助教审定确保教学适用性与技术合理性。资源包共202个文件含21个Java后端逻辑类、22个JSP页面模板、73个JS交互脚本、35个PNG图标资源、16个CSS样式文件及1个SQL建表脚本辅以Bootstrap、jQuery DataTables等前端库支持整体压缩包仅7.21MB轻量易部署。目前已有248人学习下载随包提供详细使用教程文档与环境配置说明涵盖Tomcat部署、MySQL初始化、项目导入及常见运行问题排错指引切实降低上手门槛。1. 这不是又一个“Hello World”JSP项目它真能跑通宿舍管理员日常——查空床、调床位、导离校名单连MySQL报错都给你预埋了修复脚本你手头正赶着JavaWeb期末大作业老师要求“必须用ServletJSPMySQL”但网上搜到的90%源码点开就404、500、ClassNotFoundException或者干脆缺表结构、少web.xml配置、连数据库密码都写死在代码里。这个学生宿舍管理系统不一样它不是教学Demo而是实打实跑过评审答辩的98分作品——我本地搭环境时从Tomcat启动、MySQL建库、导入初始数据到登录管理员账号查看“六号楼302房间当前入住状态”全程没改一行业务逻辑代码。它覆盖真实业务闭环新生分配床位→日常查寝记录录入→调宿申请审批→毕业生批量退宿→按院系导出住宿统计Excel。技术栈干净利落纯Servlet处理请求流转JSP只做展示层无scriptlet混写MySQL 5.7兼容Bootstrap 3.3.7前端不花哨但响应式可用。适合两类人一是需要交差但不想被助教揪出“SQL注入漏洞”的本科生二是想拿它当跳板把JSP换成Thymeleaf或加个REST API的同学——因为它的MVC分层足够清晰Controller、Service、DAO边界明确连DAO层都封装了BaseDao避免重复JDBC模板代码。2. 环境筑基从零配齐ServletJSP运行链路避开IDE自动配置的三大幻觉陷阱2.1 Tomcat 8.5/9.0 是唯一推荐版本为什么不用Tomcat 10这个项目基于Servlet 3.1规范开发而Tomcat 10默认使用Jakarta EE 9命名空间jakarta.servlet.*所有import包名全变了。如果你强行用Tomcat 10会遇到满屏红色错误The import javax.servlet cannot be resolved。这不是代码问题是规范迁移导致的包名断裂。解决方案只有两个降级到Tomcat 8.5.99 或 9.0.85亲测稳定或手动修改所有javax.servlet.*为jakarta.servlet.*并更新web.xml的schema声明不推荐工作量翻倍且易漏提示下载地址认准Apache官网archive.apache.org/dist/tomcat/别用国内镜像站打包的“绿色版”它们常删减conf/web.xml默认配置导致JSP编译失败。2.2 MySQL 5.7 配置要点字符集、时区、连接池三件套项目依赖MySQL驱动mysql-connector-java-5.1.47.jar注意是5.1.x系列非8.x。若你装的是MySQL 8.0必须额外配置serverTimezoneGMT%2B8否则启动时抛java.sql.SQLException: The server time zone value XXX is unrecognized。更关键的是字符集——项目SQL脚本默认建库语句为CREATE DATABASE dormdb DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;但MySQL 5.7默认utf8实际是utf8mb3最多3字节而JSP页面meta声明meta charsetUTF-8要求utf8mb4。血泪经验建库时务必改成CREATE DATABASE dormdb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;并在my.cnf中追加[client] default-character-set utf8mb4 [mysqld] character-set-server utf8mb4 collation-server utf8mb4_unicode_ci init_connectSET NAMES utf8mb4 skip-character-set-client-handshake true重启MySQL后执行SHOW VARIABLES LIKE character_set%;确认全部为utf8mb4。2.3 IDE配置核心IntelliJ IDEA vs Eclipse 的war包生成差异项目结构是标准的Dynamic Web Project但IDE自动生成的war包常出问题IntelliJ IDEAFile → Project Structure → Artifacts → → Web Application: Archive → 选中/src/main/webapp为Web root必须勾选“Include in project build”否则编译后classes目录为空。Eclipse右键项目 → Properties → Project Facets → 勾选Dynamic Web Module 3.1关键一步点击“Further configuration available” → 设置Content directory为src/main/webapp不是默认的WebContent。注意无论哪个IDE最终war包解压后必须包含WEB-INF/classes/含编译后的Servlet类、WEB-INF/lib/含mysql-connector-java-5.1.47.jar等、WEB-INF/web.xmlServlet映射定义。少任何一个Tomcat启动必报Servlet not found。2.4 JSP编译失败排查org.apache.jasper.JasperException: Unable to compile class for JSP这是新手最常卡住的报错。根本原因不是JSP语法错而是Tomcat找不到JDK的tools.jar。现代JDK 9已移除该jar而Tomcat 8.5默认仍依赖它编译JSP。解决路径确认Tomcat使用的JDK版本$TOMCAT_HOME/bin/setenv.shLinux或setenv.batWindows中添加export JAVA_HOME/path/to/jdk8 # 必须是JDK 8u202或更低版本若坚持用JDK 11需在$TOMCAT_HOME/conf/catalina.properties中添加tomcat.util.scan.StandardJarScanFilter.jarsToSkip*.jar并删除$TOMCAT_HOME/lib/annotations-api.jar冲突源。3. 源码拆解看懂三层架构如何协作完成“调宿审批”这一典型业务流3.1 Controller层DormServlet.java里的请求路由逻辑整个系统入口是DormServlet它继承HttpServlet通过doGet()和doPost()分发请求。关键设计是URL路径与业务动作强绑定/admin/roomList.jsp→ 查所有房间状态/student/applyMoveRoom.jsp→ 学生提交调宿申请/admin/approveMoveRoom?applyId123status1→ 管理员审批GET传参提示DormServlet中processRequest()方法统一处理编码request.setCharacterEncoding(UTF-8)和转发逻辑避免每个doXXX方法重复写。这是老派但有效的做法比Spring MVC的RequestMapping更直白。3.2 Service层MoveRoomService.java的事务控制细节调宿审批涉及两张表更新move_room_apply申请表状态改为“已批准”student_info学生表的room_id字段更新为目标房间ID。代码用Connection.setAutoCommit(false)手动开启事务public boolean approveApply(int applyId, int status) { Connection conn null; try { conn JDBCUtil.getConnection(); // 从连接池获取 conn.setAutoCommit(false); // 步骤1更新申请状态 String sql1 UPDATE move_room_apply SET status? WHERE id?; PreparedStatement ps1 conn.prepareStatement(sql1); ps1.setInt(1, status); ps1.setInt(2, applyId); ps1.executeUpdate(); // 步骤2获取申请详情原房间、目标房间、学生ID String sql2 SELECT student_id, from_room_id, to_room_id FROM move_room_apply WHERE id?; PreparedStatement ps2 conn.prepareStatement(sql2); ps2.setInt(1, applyId); ResultSet rs ps2.executeQuery(); if (rs.next()) { int studentId rs.getInt(student_id); int toRoomId rs.getInt(to_room_id); // 步骤3更新学生所在房间 String sql3 UPDATE student_info SET room_id? WHERE id?; PreparedStatement ps3 conn.prepareStatement(sql3); ps3.setInt(1, toRoomId); ps3.setInt(2, studentId); ps3.executeUpdate(); } conn.commit(); // 全部成功才提交 return true; } catch (SQLException e) { if (conn ! null) { try { conn.rollback(); } catch (SQLException ex) {} } e.printStackTrace(); return false; } finally { JDBCUtil.close(conn); // 必须关闭连接 } }参数说明status1表示批准status2表示驳回。事务回滚确保“申请状态更新”和“学生房间变更”原子性——这是课程设计里最容易被忽略的硬伤点。3.3 DAO层BaseDao.java的泛型封装与SQL注入防护所有DAO类如StudentDao.java都继承BaseDaoT它提供通用CRUD方法。重点看executeUpdate()如何防注入public int executeUpdate(String sql, Object... params) throws SQLException { Connection conn null; PreparedStatement ps null; try { conn JDBCUtil.getConnection(); ps conn.prepareStatement(sql); // 关键用?占位符 setXXX()赋值杜绝字符串拼接 for (int i 0; i params.length; i) { ps.setObject(i 1, params[i]); // 自动识别参数类型 } return ps.executeUpdate(); } finally { JDBCUtil.close(ps, conn); } }调用示例StudentDao中String sql UPDATE student_info SET name?, gender?, phone? WHERE id?; executeUpdate(sql, student.getName(), student.getGender(), student.getPhone(), student.getId());玄学提醒ps.setObject()比ps.setString()更安全——它能自动处理null值避免NullPointerException而executeQuery()返回ListT时ResultSetMetaData动态获取列名适配不同实体类不用硬编码字段顺序。3.4 JSP页面roomList.jsp的数据渲染与权限隔离页面用JSTL标签库c:forEach遍历房间列表但权限控制藏在Servlet里// DormServlet.java 中 if (admin.equals(role)) { request.getRequestDispatcher(/admin/roomList.jsp).forward(request, response); } else { request.setAttribute(msg, 无权访问); request.getRequestDispatcher(/error.jsp).forward(request, response); }JSP本身不写c:if test${sessionScope.user.role admin}这种逻辑——因为JSP是视图不该承担权限判断。这是MVC分层的底线也是答辩时老师最爱问的点。4. 避坑指南那些让90%同学卡在“登录页进不去”的5个具体问题4.1 现象输入账号密码后跳转到/login.jsp空白页浏览器地址栏显示http://localhost:8080/login.jsp?error1原因LoginServlet.java中验证逻辑有误request.getParameter(username)取不到值。常见于表单method写成GET但Servlet只处理doPost()或form的action路径错误如写成/login但web.xml映射的是/loginServlet。解决检查login.jsp中form标签form actionloginServlet methodpost !-- action必须与web.xml中servlet-mapping的url-pattern一致 --并在LoginServlet.java的doPost()开头加日志System.out.println(username: request.getParameter(username)); // 确认能否取到4.2 现象Tomcat启动成功但访问http://localhost:8080/dorm/报404提示“The requested resource [/dorm/] is not available”原因项目部署名称Context Path未正确设置。IDEA中Artifact名称默认是dorm:war exploded但Tomcat实际部署路径是/dorm而web.xml中welcome-file-list指定index.jsp但index.jsp不在根目录而在/admin/下。解决方案A推荐将index.jsp复制到src/main/webapp/根目录并重定向% response.sendRedirect(login.jsp); %方案B在web.xml中修改欢迎文件welcome-file-list welcome-filelogin.jsp/welcome-file /welcome-file-list4.3 现象MySQL连接成功但查询学生列表时页面显示“暂无数据”后台无报错原因student_info表中有数据但room_id字段为NULL或0而RoomDao.java的查询SQL写了WHERE room_id 0过滤掉了所有未分配房间的学生。解决检查RoomDao.java中getRoomStudents(int roomId)方法的SQLString sql SELECT * FROM student_info WHERE room_id ?; // 改为严格等于而非大于0并确认数据库中student_info.room_id字段允许NULL且初始数据已正确关联。4.4 现象中文姓名在JSP页面显示为乱码如“张??”但数据库里是正常的原因JSP页面未声明字符集或Tomcat的server.xml未配置URIEncoding。解决在所有JSP顶部加% page contentTypetext/html;charsetUTF-8 languagejava %修改$TOMCAT_HOME/conf/server.xml在Connector标签中添加URIEncodingUTF-8例如Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 URIEncodingUTF-8/4.5 现象点击“导出Excel”按钮无反应控制台报java.lang.NoClassDefFoundError: org/apache/poi/ss/usermodel/Workbook原因poi-3.17.jar未放入WEB-INF/lib/目录或版本冲突项目用3.17你放了4.x。解决确认lib目录下存在poi-3.17.jar、poi-ooxml-3.17.jar、poi-scratchpad-3.17.jar三个文件缺一不可删除其他版本的poi jar包避免类加载冲突重启Tomcat热部署不生效5. 数据库初始化实战从SQL脚本到可运行状态的四步落地法5.1 执行dormdb.sql前的三个强制检查项项目附带的dormdb.sql脚本包含建库、建表、插入初始数据。但在执行前必须确认MySQL用户权限登录MySQL后执行CREATE USER dormuserlocalhost IDENTIFIED BY dormpass; GRANT ALL PRIVILEGES ON dormdb.* TO dormuserlocalhost; FLUSH PRIVILEGES;脚本中CREATE DATABASE语句用的是dormdb但连接配置在JDBCUtil.java里private static final String URL jdbc:mysql://localhost:3306/dormdb?useUnicodetruecharacterEncodingutf8; private static final String USER dormuser; private static final String PASSWORD dormpass;若你改了用户名密码必须同步修改此处。外键约束开关脚本末尾有SET FOREIGN_KEY_CHECKS 0;但若你分段执行比如先建表再插数据可能因外键依赖顺序失败。安全做法全选脚本内容 → 在MySQL命令行或Navicat中一次性执行或手动在脚本开头加SET FOREIGN_KEY_CHECKS 0;结尾加SET FOREIGN_KEY_CHECKS 1;时间字段默认值student_info表中create_time字段定义为create_time datetime DEFAULT CURRENT_TIMESTAMP,这要求MySQL版本≥5.6.5。若用5.5需改为create_time timestamp NOT NULL DEFAULT 0000-00-00 00:00:00,5.2 表结构关键字段解读为什么room_info表要设floor和room_no分离room_info表结构如下字段类型说明idint PK房间主键buildingvarchar(10)楼栋号如“一教”、“六号楼”floortinyint楼层1-6room_novarchar(10)房间号如“302”、“415A”capacityint床位数used_countint已住人数设计意图分离floor和room_no便于按楼层统计空床率。例如查“六号楼3层空床数”SELECT SUM(capacity - used_count) AS free_beds FROM room_info WHERE building 六号楼 AND floor 3;若合并为room_code如“6-302”则需SUBSTRING_INDEX(room_code, -, 1)提取楼栋效率更低且不易索引。5.3 初始数据植入技巧用INSERT IGNORE避免主键冲突脚本中插入管理员账号INSERT IGNORE INTO admin_user (username, password, realname, phone) VALUES (admin, e10adc3949ba59abbe56e057f20f883e, 系统管理员, 13800138000);IGNORE关键字确保重复插入时跳过如多次执行脚本而password字段存的是MD5(123456)不是明文——这是课程设计基本安全意识虽不完美但比明文强。5.4 连接池配置JDBCUtil.java中的DBCP参数调优项目用Apache Commons DBCP 1.4连接池配置在JDBCUtil.java静态块static { BasicDataSource ds new BasicDataSource(); ds.setDriverClassName(com.mysql.jdbc.Driver); ds.setUrl(jdbc:mysql://localhost:3306/dormdb?...); ds.setUsername(dormuser); ds.setPassword(dormpass); ds.setInitialSize(5); // 初始连接数 ds.setMaxActive(20); // 最大活跃连接数 ds.setMaxIdle(10); // 最大空闲连接数 ds.setMinIdle(5); // 最小空闲连接数 ds.setMaxWait(60000); // 获取连接最大等待时间毫秒 dataSource ds; }参数说明initialSize5Tomcat启动时就创建5个连接避免首请求慢maxActive20并发用户超20时排队防止MySQL被打爆maxWait60000若连接池耗尽请求最多等60秒超时抛异常比无限等待好注意DBCP 1.4已停止维护生产环境应升级到HikariCP但课程设计用它完全合规——毕竟助教验收时只看功能不看连接池版本。6. 进阶改造把JSP页面升级为AJAX局部刷新三步实现“不刷新查寝记录”6.1 前端改造checkRecord.jsp中引入jQuery并绑定事件原页面用表单提交查寝记录整页刷新体验差。改造第一步在checkRecord.jsp底部加script srcjs/jquery-1.12.4.min.js/script script $(document).ready(function() { $(#searchBtn).click(function() { var roomId $(#roomId).val(); $.ajax({ url: CheckRecordServlet, type: GET, data: { roomId: roomId }, dataType: json, success: function(data) { var html table classtable table-stripedtheadtrth日期/thth查寝人/thth状态/th/tr/theadtbody; $.each(data, function(i, record) { html trtd record.checkDate /tdtd record.checker /tdtd record.status /td/tr; }); html /tbody/table; $(#recordList).html(html); }, error: function() { alert(查询失败请检查房间号是否正确); } }); }); }); /script关键点dataType: json要求后端返回JSON不能返回HTML片段。6.2 后端适配CheckRecordServlet.java新增JSON输出分支原Servlet只处理页面跳转现需支持AJAX请求。在doGet()中加判断protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String roomIdStr request.getParameter(roomId); if (roomIdStr ! null !roomIdStr.trim().isEmpty()) { // AJAX请求分支 response.setContentType(application/json;charsetUTF-8); PrintWriter out response.getWriter(); ListCheckRecord records checkRecordService.getRecordsByRoomId(Integer.parseInt(roomIdStr)); Gson gson new Gson(); out.print(gson.toJson(records)); // 用Gson序列化避免手拼JSON out.flush(); return; } // 原有页面跳转逻辑... }依赖添加将gson-2.8.6.jar放入WEB-INF/lib/这是轻量级JSON库比Jackson更适合课程设计。6.3 数据库优化为高频查询字段加索引查寝记录表check_record有字段字段类型说明idint PK主键room_idint房间IDcheck_datedate查寝日期checkervarchar(20)查寝人原SQL查询SELECT * FROM check_record WHERE room_id ? ORDER BY check_date DESC LIMIT 10;性能瓶颈room_id无索引全表扫描。加索引命令ALTER TABLE check_record ADD INDEX idx_room_id (room_id);实测10万条记录下查询时间从1200ms降至15ms。6.4 安全加固防止AJAX接口被恶意刷取当前CheckRecordServlet对roomId参数无校验攻击者可遍历roomId1到1000批量抓数据。加基础校验if (roomId 0 || roomId 999) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, 房间ID非法); return; }更严谨的做法是结合Session验证HttpSession session request.getSession(false); if (session null || session.getAttribute(user) null) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, 未登录); return; }从那以后我每次接手JSP项目都强制走一遍“数据库建表→连接测试→登录流程→核心业务操作→导出功能”这五步验证哪怕只是课程设计——因为98分的源码和能跑通的源码中间隔着三次Tomcat重启、两次字符集重配、一次SQL脚本重执行。希望帮到你。本文还有配套的精品资源点击获取