
简介这是一套面向JSP与MySQL初学者的学生宿舍管理系统实训作业完整源码包适合课程设计、毕业设计或期末实训参考。系统围绕宿舍分配、住宿费用、报修维修、统计报表等功能展开包含学生信息管理、宿舍信息管理、分配与调整、费用管理、报修与维修、统计报表及用户权限管理等模块。压缩包共67个文件约5.55MB主要包含17个Java源文件与17个编译后class文件、10个JSP页面、8个XML配置、7个JAR依赖、1个SQL数据库脚本及1份docx作业文档各类型文件分工明确便于直接导入IDE运行调试。包内附有数据库脚本、项目配置文件、文档说明及完整的源码目录结构可帮助理解JSPServletJavaBean的开发模式与MySQL表设计思路。已有69人学习下载适合需要快速上手Web项目或参考实训报告写法的人群。1. 实训作业做宿舍管理系统JSPMySQL依然是性价比最高的组合每年实训季都能在问答平台刷到同一类求助宿舍管理系统源代码有了但跑不起来数据库文件导入报错或者能跑但讲不出设计思路。把 JSP 和 MySQL 放在一起并不是因为它新而是因为这套组合把 JavaWeb 最核心的链路完整串了一遍浏览器发起请求、Servlet 处理、JSP 渲染、JDBC 连库、SQL 增删改查、Tomcat 发布。对一个两周到六周的数据库课程设计或软件综合实训来说这个技术栈足够讲清三层结构也足够被提问。这篇文章直接按“数据库 → 源码 → 部署 → 文档”的顺序给出一套可落地的宿舍管理系统方案。会给出建表 SQL、JDBC 工具类、登录和增删改查的代码骨架以及 Tomcat 部署和答辩时最常见的坑。适合正在做实训作业、手里有一份源码但不知道如何修改和讲解的同学也适合需要抽查学生成果的指导教师。多说一句任何一份你拿到的源码都不能直接等于你的作业。把它拆成表结构、DAO、Servlet、JSP 四部分自己能重建一遍答辩时才站得住。2. 数据库先行宿舍管理系统的表结构与MySQL初始化脚本2.1 先定业务边界再画ER图写代码前先把业务画清楚。学生宿舍管理系统的需求通常收敛为三块人学生、宿管、物楼栋、宿舍、床位、事入住、退宿、报修、来访登记。不需要做考勤、水电费这种扩展功能除非你的实训题目明确要求。功能越多答辩时的备问点越多。画 ER 图时注意实体间的关系一个楼栋包含多个宿舍一个宿舍住多个学生一次报修属于某个宿舍或某个学生。这个关系用 E-R 图表达出来评阅老师一眼就能看出你有没有理解外键。我一般会把 ER 图放在报告里同时把表结构关系写清表名关键字段外键作用buildingid, name, address无楼栋基础信息dormitoryid, building_id, room_no, capacity, usedbuilding_id → building.id宿舍房间与容量studentid, stu_no, name, gender, dorm_id, bed_no, phonedorm_id → dormitory.id学生基本信息adminid, username, password无管理员登录repairid, dorm_id, stu_id, content, status, create_timedorm_id → dormitory.id, stu_id → student.id报修记录这张表里没有把宿舍单独拆成一张更细的 room 表是因为实训规模下宿舍数量有限直接在 dormitory 里维护容量和已住人数查询和展示都简单。如果你想把模型做得更规范也可以把 dormitory 拆成 room 表但会多一层联查答辩时要有准备能说清楚多一层表带来的冗余消除和查询成本。2.2 建库脚本字符集、外键、索引一次性写对实训里拿到的数据库脚本最常见的毛病是没有指定字符集导致中文乱码、没有外键导致删数据后出现孤儿记录、没有注释导致别人看不懂字段含义。下面这个建库脚本直接照着用SQL 里已经去掉多余字段控制在答辩能讲清楚的范围内DROP DATABASE IF EXISTS dorm; CREATE DATABASE dorm DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE dorm; CREATE TABLE building ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(32) NOT NULL UNIQUE COMMENT 楼栋名如1号楼, address VARCHAR(128) DEFAULT COMMENT 位置说明 ) ENGINEInnoDB; CREATE TABLE dormitory ( id INT PRIMARY KEY AUTO_INCREMENT, building_id INT NOT NULL, room_no VARCHAR(16) NOT NULL, capacity INT NOT NULL DEFAULT 4 COMMENT 床位数, used INT NOT NULL DEFAULT 0 COMMENT 已住人数, manager VARCHAR(32), UNIQUE KEY uk_building_room (building_id, room_no), CONSTRAINT fk_dorm_building FOREIGN KEY (building_id) REFERENCES building(id) ) ENGINEInnoDB; CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, stu_no VARCHAR(20) NOT NULL UNIQUE COMMENT 学号, name VARCHAR(32) NOT NULL, gender CHAR(1) NOT NULL DEFAULT 男, dorm_id INT NOT NULL, bed_no VARCHAR(8), phone VARCHAR(20), enroll_year YEAR, CONSTRAINT fk_student_dorm FOREIGN KEY (dorm_id) REFERENCES dormitory(id) ) ENGINEInnoDB; CREATE TABLE repair ( id INT PRIMARY KEY AUTO_INCREMENT, dorm_id INT NOT NULL, stu_id INT, content VARCHAR(255) NOT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待处理 1处理中 2已完成, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_repair_dorm FOREIGN KEY (dorm_id) REFERENCES dormitory(id), CONSTRAINT fk_repair_stu FOREIGN KEY (stu_id) REFERENCES student(id) ) ENGINEInnoDB; CREATE INDEX idx_repair_status ON repair(status);脚本里有几个值得注意的地方。字符集用 utf8mb4 而不是 utf8因为 MySQL 8 中 utf8mb4 才是完整四字节字符集学生信息里出现生僻字或特殊符号不会变成问号。所有表都用 InnoDB保证外键约束和事务能力实训阶段不需要考虑 MyISAM。used字段表示宿舍已住人数由业务代码在入住和退宿时维护这里不建触发器是因为触发器在答辩时很容易被追问执行时机和一致性用代码更新反而好讲。CREATE INDEX单独写在建表语句后面而不是写在列定义里是为了让索引可以单独在报告里说明。repair 表的状态字段会频繁出现在查询条件中比如统计待处理报修单所以单独建了 idx_repair_status 索引。宿舍和学生的唯一键用 UNIQUE KEY 而不是 PRIMARY KEY就是为了保证“同一栋楼的房间号不重复”这个业务规则。2.2.1 mysql安装配置教程里最容易漏的两步很多同学在自己电脑上脚本跑不通问题往往不在脚本本身而在安装环节。mysql安装配置教程里最容易漏的是安装时没注意 root 密码策略导致后续 JDBC 连不上另一个是安装后字符集还是默认的 latin1建库时没有显式指定 utf8mb4 就直接导入。Windows 上装完 MySQL 后先用命令行验证服务是否启动net start | findstr -i mysql mysql -uroot -p能进 mysql 提示符后再执行SHOW VARIABLES LIKE character_set_database;看数据库默认字符集。如果是 latin1在 my.ini 的 [mysqld] 段落加character-set-serverutf8mb4重启服务。这一步做完上面的建库脚本导入就不会乱码。脚本导入之后顺手插入几条测试数据后面做登录和列表页都要用INSERT INTO building(id, name) VALUES (1, 1号楼); INSERT INTO dormitory(id, building_id, room_no, capacity, used) VALUES (1, 1, 101, 4, 3); INSERT INTO student(stu_no, name, gender, dorm_id, bed_no, phone, enroll_year) VALUES (20230001, 张三, 男, 1, 1号床, 13800000000, 2023);插入后再查一次学生表确认中文正常显示再进入源码阶段。这里顺便验证一下外键是否生效往 student 表插入一个不存在的 dorm_id如果报外键约束错误说明脚本执行完整。2.3 统计查询为什么要建索引用EXPLAIN说服答辩老师宿舍管理系统数据量小索引收益在界面上感觉不到。但答辩老师一定会问“数据量大了怎么办”你必须能说出“在频繁查询的字段上建索引”。与其背概念不如现场给一条 EXPLAIN 语句EXPLAIN SELECT s.stu_no, s.name, d.room_no FROM student s JOIN dormitory d ON s.dorm_id d.id WHERE s.dorm_id 5;执行结果里可以看到 type 为 ref、key 为 fk_student_dorm说明这次查询走了索引而不是全表扫描。学生表里 dorm_id 本来就有外键索引所以这里不用再额外建。如果查询条件是WHERE status 0就会用到前面建的 idx_repair_status。知道怎么读 EXPLAIN比把索引概念背十遍更有说服力。注意一个常见误用不要在性别、状态这种低区分度字段上单独建索引选择性太低MySQL 可能也不会用。宿舍管理系统里真正值得建索引的只有外键、学号、状态这些高频查询字段。答辩时如果能主动说出“外键本身会建索引不需要重复建”会明显加分。3. JSPServlet分层把源码从“能跑”改成“能讲”3.1 实训源码的最短可行结构拿到宿舍管理系统源代码后第一件事不是打开每个 JSP 看代码而是先看目录结构。很多老项目把所有代码都写在 JSP 里表面上省事但答辩时连“三层架构”都指不出来容易被判为不完整。我一般建议按下面这个结构重排src/main/java/com/dorm entity/ Student.java Dormitory.java Repair.java Admin.java dao/ StudentDao.java DormitoryDao.java RepairDao.java AdminDao.java service/ StudentService.java DormService.java RepairService.java controller/ LoginServlet.java StudentServlet.java RepairServlet.java LogoutServlet.java util/ DBUtil.java StringUtil.java src/main/webapp admin/ index.jsp student_list.jsp dorm_list.jsp repair_list.jsp css/ 样式文件 WEB-INF/web.xml 或注解这个结构对应的是严格的三层Servlet 只收参数和跳转Service 只处理业务规则DAO 只拼 SQL。Entity 类与表字段一一对应。如果时间紧张至少把 DAO 和 JSP 分开不要在 JSP 里出现% Connection conn DBUtil.getConnection(); %这类代码。答辩评分标准里“代码分层是否合理”通常是独立一项。3.2 DBUtilJDBC连接参数决定你能否连上MySQL所有数据库操作的入口是 DBUtil。这个类必须单独写不要在十个 Servlet 里各写一遍 DriverManager.getConnection。用 MySQL 8 时驱动类和连接串参数与 MySQL 5 不同最常见的报错就是ClassNotFoundException: com.mysql.jdbc.Driver。package com.dorm.util; import java.sql.*; public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/dorm?useUnicodetruecharacterEncodingutf8 useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement st, ResultSet rs) { if (rs ! null) { try { rs.close(); } catch (SQLException ignored) {} } if (st ! null) { try { st.close(); } catch (SQLException ignored) {} } if (conn ! null) { try { conn.close(); } catch (SQLException ignored) {} } } }URL 里四个参数必须知道含义答辩很可能逐个问。useUnicodetruecharacterEncodingutf8保证中文不乱码这要和数据库端 utf8mb4 一起生效。serverTimezoneAsia/Shanghai解决 MySQL 8 的时区报错不加会看到 The server time zone value 提示。useSSLfalse关闭 SSL 警告。allowPublicKeyRetrievaltrue解决 MySQL 8 连接时 Public Key Retrieval 异常。PASSWORD 的明文写在代码里实训可以接受但报告里最好提一句生产环境会用配置中心和加密方式管理。3.3 登录功能从表单到session的一整套代码登录是宿舍管理系统的门面也最容易暴露问题。登录功能要讲清楚四个点请求编码、参数校验、与数据库比对、Session 记录状态。先给出 LoginServlet 骨架WebServlet(/login) public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String username req.getParameter(username); String password req.getParameter(password); if (username null || username.isEmpty() || password null || password.isEmpty()) { resp.sendRedirect(req.getContextPath() /login.jsp?errorempty); return; } AdminDao dao new AdminDao(); Admin admin dao.findByUsernameAndPassword(username, password); if (admin null) { resp.sendRedirect(req.getContextPath() /login.jsp?errorbad); return; } HttpSession session req.getSession(); session.setAttribute(admin, admin); resp.sendRedirect(req.getContextPath() /admin/index.jsp); } }doPost 方法里处理登录逻辑doGet 直接回落避免 GET 请求也能修改登录状态。setCharacterEncoding必须在读取任何参数之前调用否则中文用户名会乱码。密码比对由 AdminDao 完成用SELECT * FROM admin WHERE username? AND password?这里用 PreparedStatement 而不是字符串拼接防止 SQL 注入。Session 设置完成后重定向到管理首页用getContextPath()拼接路径项目改名后不受影响。如果题目里要求 jsp个人信息展示页面写法也是一样先查 student 表再把结果 setAttribute 到 request在 JSP 里用${student.name}展示不要在 JSP 里直接查库。3.4 JSP编译class文件保存在哪里改完页面不生效先清work目录实训做久了会遇到一个经典问题改了 JSP刷新浏览器还是旧页面。这时候要知道 JSP 不是原样运行的Tomcat 会把 JSP 翻译成 Java 再编译成 class。在 web项目配置tomcat后查看jsp编译后的java类路径永远在 Tomcat 的 work 目录下apache-tomcat-9.0.xx/work/Catalina/localhost/项目名/org/apache/jsp/目录下能看到 login_jsp.java 和 login_jsp.class 这种文件。修改 JSP 后 Tomcat 理论上会自动检测文件变化重新编译但偶尔会出现缓存不及时或编译失败此时删除整个 work 目录再重启 Tomcat 是最稳妥的做法。Windows 上先停服务再删Linux 上执行rm -rf /usr/local/tomcat/work/*; cd /usr/local/tomcat/bin ./startup.sh还有一个调试坑在 JSP 对应的 Java 文件里打断点显示“当前不会命中断点”。这是因为 IDE 里的断点打在源码 JSP 上而实际运行的类在 work 目录里两者不是同一个文件。解决办法是在 Servlet 的 doPost 或 doGet 第一行打断点用 Debug 模式启动 Tomcat走一遍请求就能命中。3.5 屏蔽jsp离开页面提示与增删改查的通用模式有的 JSP 模板会自带window.onbeforeunload离开提示是模板作者防止表单误关加的。实训演示时这个弹窗很影响观感屏蔽jsp离开页面提示最简单的写法是在目标页面里把事件覆盖掉window.onbeforeunload null;如果页面里的提示是动态绑定的可以先window.onbeforeunload undefined再重新赋值。如果弹窗来自公共 header 文件就在公共文件中删除注册 onbeforeunload 的那一行。这个方法只对当前页面生效别写在所有页面共享的全局 JS 里否则需要监听的表单页面也会失效。增删改查是宿舍管理系统的核心代码模式统一为“controller 收参 → service 处理 → dao 执行”。以新增学生为例DAO 里是这个套路public boolean addStudent(Student s) { String sql INSERT INTO student(stu_no, name, gender, dorm_id, bed_no, phone, enroll_year) VALUES(?,?,?,?,?,?,?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, s.getStuNo()); ps.setString(2, s.getName()); ps.setString(3, s.getGender()); ps.setInt(4, s.getDormId()); ps.setString(5, s.getBedNo()); ps.setString(6, s.getPhone()); ps.setInt(7, s.getEnrollYear()); return ps.executeUpdate() 0; } catch (SQLException e) { e.printStackTrace(); return false; } }所有 JDBC 的 query 方法都用 try-with-resources 关闭连接PreparedStatement 的?占位符按顺序绑定避免拼字符串的转义问题。注意一个细节新增学生成功后还要同步更新 dormitory 表的 used 值。这个业务规则不要放在 DAO 里正确做法是放到 Service 层在一个方法里先 addStudent 再 updateUsedNum必要时用事务包住。答辩时能说出这一层说明你真的理解分层不能只停留在目录上。4. 部署到Tomcat从mysql-connector到JDBC URL的实战参数4.1 依赖给Maven还是直接把jar放进WEB-INF/lib实训环境里先确认项目用什么打包。如果项目有 pom.xml直接用 Maven 引入 JDBC 驱动比较省事dependency groupIdmysql/groupId artifactIdmysql-connector-j/artifactId version8.0.33/version /dependency如果是从老师那拷贝的 Eclipse Dynamic Web Project没有 Maven就把驱动 jar 复制到WEB-INF/lib下然后 Eclipse 里右键项目 → Build Path → Add Jar。注意两种方式不要混用否则 WEB-INF/lib 和 Maven 依赖里各有一份 jar部署时可能引起 ClassCastException表现为接口实现类加载冲突的异常堆栈。判断依赖是否加载成功看 Tomcat 启动日志。如果启动时没有异常但第一个数据库操作报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver优先检查 jar 是否真的在目标部署目录里。Eclipse 有时发布时没有自动同步要手动执行 Redepoly。4.2 部署检查清单日志、防火墙、数据库权限部署失败时不要来回刷新页面。按顺序检查这几件事在真机上访问http://localhost:8080/项目名/确认 Tomcat 进程存在查看logs/catalina.out或logs/localhost.yyyy-MM-dd.log中是否有 Context 启动失败用mysql -uroot -p在命令行里能连上库排除 MySQL 服务停止检查数据库用户权限是否允许来自应用服务器的访问第 4 点最容易被忽略。实训机器上 MySQL 和应用在同一台root 一般能连。但如果你把项目拷到另一台电脑或用了局域网 IP 访问MySQL 用户表的 host 可能是localhost导致 Access denied。临时解决办法是让专用账号支持所有来源CREATE USER dorm_user% IDENTIFIED BY 123456; GRANT ALL PRIVILEGES ON dorm.* TO dorm_user%; FLUSH PRIVILEGES;这样只授权给 dorm 库不要直接开放 root 的远程权限。MySQL 8 的用户管理语法和 MySQL 5 有差异用 GRANT 时需要确认账号已先创建。数据库同步工具在这种场景下也有用但实训单机环境把权限配好就够了。4.3 用mysqldump配合Workbench交数据库作业实训要求“数据库”这一项时往往要同时交 .sql 文件和表结构截图。可以用 MySQL Workbench 做图形化查看也可以直接命令行操作。做两件事一是修正表结构二是导出可用于交付的 SQL 文件。命令行导出最可靠mysqldump -uroot -p --default-character-setutf8mb4 dorm dorm.sql还原时不要直接在图形界面里双击导入在 mysql 命令行里执行mysql -uroot -p dorm dorm.sql执行前确认 dorm 库已存在mysqldump 默认不会建库。如果想达到“一键导入”的效果导出的 sql 文件开头补上CREATE DATABASE IF NOT EXISTS dorm和USE dorm这样别人拿到作业时不会踩库名错误。MySQL workbench使用教程里常说的 EER 图导出也值得学一下它能直接从数据库反向生成模型图放进报告里比截图更专业。4.4 运行期SQL异常三连时区、驱动、Access denied把运行期间最常见的三个报错放在一起对照方便抽查报错特征原因修复ClassNotFoundException: com.mysql.cj.jdbc.Driver驱动 jar 缺失或驱动类名写错检查 WEB-INF/libMySQL 8 用 com.mysql.cj.jdbc.DriverMySQL 5 用 com.mysql.jdbc.DriverThe server time zone value ...或Unknown database dorm连接串缺少 serverTimezone库不存在或库名写错URL 加serverTimezoneAsia/ShanghaiSHOW DATABASES确认库名Access denied for user rootlocalhost密码错误或用户 host 不匹配核对密码确认连接串 USER/PASSWORD用 ALTER USER 修改密码后重启服务这三条解决了JDBC URL 这段基本就过关。MySQL 8 的连接串参数区分大小写allowPublicKeyRetrievaltrue中 key 和 retrieval 不能写错否则驱动不认参数。排查顺序永远是先驱动、再连接串、最后权限不要一上来就重装 MySQL。5. 文档与答辩用ER图、测试记录和演示脚本让实训报告过审5.1 实训报告的五件套实训作业的评分点通常在文档上源码反而不是第一个被翻的。见过不少同学把全部代码贴进文档结果老师根本找不到表结构设计。宿舍管理系统的文档按五件套来组织最稳需求说明、ER图、数据库表设计、关键代码、测试记录。这五样对应数据库课程设计最常见的检查项。测试记录不要只写“功能正常”每一条要写清输入、预期结果、实际输出。比如“学生列表按宿舍号筛选输入101返回该宿舍3名学生页面显示3行”这种记录在答辩时可以直接当演示脚本。截图要带清晰的控制台或地址栏才看得出是真实运行而不是拼图。5.2 演示脚本按角色和流程走不被问住答辩演示不要点哪算哪按业务流走一遍评阅老师跟着节奏提问节奏在自己手里。推荐顺序管理员登录进入首页添加一栋楼添加一个宿舍说明容量和已住人数添加学生绑定宿舍返回列表看到宿舍已住人数从0变成1查询该宿舍学生列表演示筛选条件新增一条报修状态设置为待处理处理报修状态改为已完成数据库里查 status 字段变化拖到前在后面介绍。5.3 答辩前自己提四个问题模仿老师习惯答辩前自己问自己四个问题宿舍满了还能不能加人学生换宿舍怎么处理删掉一个宿舍会发生什么密码存的是明文吗这四个问题每个都有标准回应容量校验放在 Service 层宿舍满了直接给提示换宿舍要写入住历史并更新两个宿舍的 used 值删除宿舍因外键约束会先报错应该先处理学生明文密码只能用于实训生产要加盐哈希。回答时如果能带一条 SQL 说明比如SHOW CREATE TABLE student;来展示外键定义比空讲概念更可信。把这条命令在答辩现场执行顺便把输出贴进报告文档的真实性也顺带证明了。按上面的路径把数据库脚本、DBUtil、登录逻辑、增删改查和一个演示脚本过一遍这份实训作业基本就能交得从容。本文还有配套的精品资源点击获取