☰
JSP+MySQL宿舍管理系统实训包:环境搭建、代码拆解与避坑指南
2026/10/10 12:22:15 网站建设 项目流程

简介:这份实训作业资源面向计算机相关专业学生与Java Web初学者,提供一套基于JSP与MySQL的学生宿舍管理系统完整实现,可用于课程设计、毕业实训或自学练手。系统围绕学生信息、宿舍登记、住宿分配与调整、费用管理、在线报修、统计报表及用户权限等模块展开,帮助理解动态网页与关系型数据库的整合思路。压缩包共67个文件,约5.55MB,包含17个java源文件与17个class编译文件、10个jsp页面、8个xml配置、7个jar依赖,以及sql建库脚本、properties配置和docx设计文档,覆盖从源码、数据库到说明文档的完整链路。已有69人学习下载。读者可据此掌握项目分层结构、数据库表设计与部署配置方法,并借助文档快速跑通系统、对照源码梳理各功能模块的实现逻辑,适合作为Web开发入门到进阶的实践参考。

1. 从一份实训包说起:JSP+MySQL 宿舍管理系统到底能跑出什么

如果你手头正压着一份 JavaWeb 实训作业,要求做一套「学生宿舍管理系统」,还要交源代码、数据库脚本和文档,那这份java-web-student-housion-manager-master压缩包大概率就是你要找的东西。它不是那种只放几张截图糊弄人的演示稿,而是把src、sql、doc、pom.xml都塞齐了的完整工程:JSP 负责页面渲染,Servlet 接请求,JavaBean 承载数据,MySQL 存学生、宿舍、费用、报修这些核心表。拿到手第一件事不是急着点运行,而是先判断它能不能在你的环境里跑起来、数据库脚本能不能一次导入成功、文档里的表结构跟代码里的实体对不对得上。这套东西适合两类人:一类是赶实训截止日期、需要一份能改能交的底稿;另一类是刚学完 JSP 想找个真实项目练手,看看 MVC 在没上框架的年代是怎么落地的。下面我按「先跑通、再拆解、后避坑」的顺序,把这份资源从头到尾过一遍。

2. 环境搭建与数据库导入:把 war 包跑起来的第一公里

2.1 先确认 JDK、Tomcat 和 MySQL 的版本咬合

这份工程带pom.xml,说明它原本是按 Maven 项目组织的,但目录里又出现了.iml和out/artifacts,意味着作者可能用 IDEA 直接打过 war 包。常见做法是:JDK 用 1.8(JSP 老项目对高版本 JDK 兼容性玄学,别一上来就 JDK 17),Tomcat 用 8.5 或 9.0,MySQL 用 5.7 或 8.0。注意 MySQL 8.0 的驱动类名是com.mysql.cj.jdbc.Driver,而 5.x 是com.mysql.jdbc.Driver,如果pom.xml里锁的是老驱动,连 8.0 库会报Unknown system variable 'query_cache_size'之类的错。先看依赖:

<!-- pom.xml 里重点看这三段 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> <!-- 如果是 5.x,连 8.0 库要换 8.0.28 --> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> <!-- 别打成 war 里,Tomcat 自带 --> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> <!-- JSP 页面用 c:forEach 就靠它 --> </dependency>

逻辑说明:provided作用域表示编译时需要、打包时不带入,因为 Tomcat 的lib目录已经有 servlet-api,重复放进去会冲突。参数上,如果你本地 MySQL 是 8.0,把 connector 版本改成8.0.28,同时把连接 URL 里的useSSL和serverTimezone补上,否则启动就报时区错误。

2.2 导入studenthousionmanager.sql并核对表结构

数据库脚本在sql目录下,名字就是studenthousionmanager.sql。我一般先在 MySQL 里建一个空库,字符集选utf8mb4,再执行脚本:

# 登录 MySQL 后执行 CREATE DATABASE studenthousionmanager DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE studenthousionmanager; SOURCE /你的路径/sql/studenthousionmanager.sql;

执行完别急着关,先SHOW TABLES;看一眼。这类系统通常有student、dormitory、room、fee、repair、admin这几张核心表。重点核对三处:一是student表里有没有room_id外键或冗余字段,这决定分配逻辑是查关联还是直接改字段;二是fee表有没有status字段区分已缴未缴;三是repair表有没有progress字段跟踪维修进度。如果脚本里用了ENGINE=InnoDB但没写外键约束,那代码里的删除操作就得自己保证一致性,这是后面容易翻车的地方。

2.3 改数据库连接配置并部署到 Tomcat

连接配置一般藏在src下的db.properties或某个DBUtil.java里。常见写法是:

# db.properties jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/studenthousionmanager?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false jdbc.username=root jdbc.password=你的密码

参数说明:useUnicode和characterEncoding保证中文不乱码,serverTimezone是 MySQL 8.0 必填项,useSSL=false避免本地连接时的证书警告。改完后用 Maven 打包mvn clean package,把target下生成的 war 丢进 Tomcat 的webapps,启动bin/startup.bat或startup.sh。访问http://localhost:8080/项目名/,如果看到登录页,第一公里就算跑通了。如果 404,先看 Tomcat 日志里 war 有没有解压成功;如果 500 且报ClassNotFoundException,多半是 MySQL 驱动没打进WEB-INF/lib。

3. 代码结构拆解:JSP、Servlet、JavaBean 各管哪一摊

3.1 从src目录看 MVC 的分层习惯

打开src,典型包结构是com.xxx.dao、com.xxx.service、com.xxx.servlet、com.xxx.bean。这套分层不是摆设:bean里是跟数据库表一一对应的实体类,字段名通常和列名驼峰对应;dao里是 JDBC 增删改查,一个方法对应一条 SQL;service做业务组合,比如「分配宿舍」要同时改student表和room表的入住人数;servlet只负责收参数、调 service、转发 JSP。判断代码质量的一个土办法:看servlet里有没有直接写 SQL。如果写了,说明分层没做干净,改起来会牵一发动全身。

// 典型 Servlet 写法:只做参数接收和跳转 @WebServlet("/student/add") public class StudentAddServlet extends HttpServlet { private StudentService studentService = new StudentService(); protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); // 不加这行,中文姓名入库变问号 Student s = new Student(); s.setName(req.getParameter("name")); s.setStudentNo(req.getParameter("studentNo")); s.setClassName(req.getParameter("className")); boolean ok = studentService.addStudent(s); if (ok) { resp.sendRedirect(req.getContextPath() + "/student/list"); } else { req.setAttribute("msg", "学号重复或数据库异常"); req.getRequestDispatcher("/student/add.jsp").forward(req, resp); } } }

逻辑说明:req.setCharacterEncoding("UTF-8")必须放在getParameter之前,否则 POST 中文乱码。sendRedirect和forward的区别在这里很关键——添加成功后用重定向,避免刷新页面重复提交;失败时用转发,保留请求域里的错误提示。

3.2 数据库表与实体字段的对应关系

把studenthousionmanager.sql里的建表语句和bean包里的类对照着看,能快速摸清业务边界。下面这张表是我从常见结构里整理出来的对应关系,你拿到手可以按实际脚本微调:

数据库表实体类关键字段业务含义
studentStudentid, student_no, name, class_name, room_id学生基本信息与所住宿舍
dormitoryDormitoryid, building_no, floor, manager宿舍楼栋信息
roomRoomid, room_no, capacity, occupied, dormitory_id房间容量与已住人数
feeFeeid, student_id, amount, status, pay_date住宿费缴纳记录
repairRepairid, room_id, content, progress, create_time报修内容与处理进度
adminAdminid, username, password, role管理员账号与权限

注意room表的occupied字段:分配和退宿时都要同步更新它,如果代码里只改了student.room_id而忘了改occupied,住的人数就会对不上,这是这类系统最常见的逻辑漏洞。

3.3 页面跳转与权限过滤的落点

JSP 页面放在web或WebContent下,WEB-INF里通常有web.xml。老项目可能用web.xml配 servlet 映射,新一点的用@WebServlet注解。权限过滤一般靠一个LoginFilter实现,检查session里有没有admin对象,没有就踢回登录页。如果你发现未登录也能直接访问/student/list,就去web.xml或filter包下找过滤器,看它的url-pattern是不是写成了/*。这里有个细节:过滤器里放行登录页和静态资源(css、js、图片),否则登录页自己都被拦了,会陷入死循环。

4. 核心功能实现细节:分配、费用与报修怎么串起来

4.1 宿舍分配:先查空床位再改两张表

分配逻辑的入口通常是「学生列表」页上的一个「分配」按钮,点进去选楼栋、选房间。后端要做三件事:查该房间occupied < capacity、更新student.room_id、把room.occupied加一。这三步必须在一个事务里,否则中间失败就会出现「学生有宿舍但房间人数没加」的脏数据。

// Service 层分配逻辑,用事务包住 public boolean assignRoom(int studentId, int roomId) { Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 // 1. 查房间是否还有空位,加行锁防止并发超分 String checkSql = "SELECT capacity, occupied FROM room WHERE id = ? FOR UPDATE"; PreparedStatement ps1 = conn.prepareStatement(checkSql); ps1.setInt(1, roomId); ResultSet rs = ps1.executeQuery(); if (!rs.next() || rs.getInt("occupied") >= rs.getInt("capacity")) { conn.rollback(); return false; } // 2. 更新学生宿舍 PreparedStatement ps2 = conn.prepareStatement("UPDATE student SET room_id = ? WHERE id = ?"); ps2.setInt(1, roomId); ps2.setInt(2, studentId); ps2.executeUpdate(); // 3. 房间已住人数加一 PreparedStatement ps3 = conn.prepareStatement("UPDATE room SET occupied = occupied + 1 WHERE id = ?"); ps3.setInt(1, roomId); ps3.executeUpdate(); conn.commit(); return true; } catch (Exception e) { try { if (conn != null) conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); return false; } finally { DBUtil.close(conn); } }

参数说明:FOR UPDATE是行级锁,防止两个管理员同时给同一间房分人导致超员。setAutoCommit(false)到commit()之间的任何异常都要rollback(),否则连接归还池子时事务还挂着。如果你的 MySQL 引擎是 MyISAM,FOR UPDATE不生效,得换成表锁或应用层加同步,这也是导入脚本时要确认ENGINE=InnoDB的原因。

4.2 费用管理:状态字段比金额计算更容易出错

费用模块一般分「生成费用单」和「缴费登记」两步。生成时按学生和学期插入一条status=未缴的记录,缴费时把status改成已缴并写pay_date。这里容易踩的坑是:金额用double存,累加几次就出现0.30000000000000004。正确做法是数据库用DECIMAL(10,2),Java 里用BigDecimal。如果你在代码里看到float或double算钱,建议直接改掉,不然后面统计报表对不上账,查起来就是黑匣子。

-- 统计某学生欠费总额,用 DECIMAL 避免精度丢失 SELECT s.name, SUM(f.amount) AS total_debt FROM student s JOIN fee f ON s.id = f.student_id WHERE f.status = '未缴' GROUP BY s.id, s.name HAVING total_debt > 0;

这条 SQL 可以直接放进dao里做欠费查询。HAVING后面用别名在 MySQL 里可行,但标准写法是重复聚合表达式,移植到其他库时注意。

4.3 报修流程:从提交到完工的状态流转

报修表通常有progress字段,取值可能是「待处理」「维修中」「已完成」。学生提交时插入一条待处理,管理员接单改成维修中,完工改成已完成。前端 JSP 用 JSTL 的c:if根据状态显示不同按钮:

<c:forEach items="${repairList}" var="r"> <tr> <td>${r.roomNo}</td> <td>${r.content}</td> <td> <c:choose> <c:when test="${r.progress == '待处理'}">待处理</c:when> <c:when test="${r.progress == '维修中'}">维修中</c:when> <c:otherwise>已完成</c:otherwise> </c:choose> </td> <td> <c:if test="${r.progress != '已完成'}"> <a href="repair/update? id=${r.id}&progress=已完成">标记完成</a> </c:if> </td> </tr> </c:forEach>

注意c:choose比连续c:if更适合状态互斥的场景,可读性也更好。链接里的&在 JSP 中要写成&amp;才符合 XML 规范,虽然浏览器容错,但严格解析时会报错。

5. 避坑与排查:这份实训包里最容易翻车的五个地方

5.1 中文乱码:现象是姓名变问号,原因是编码链没统一

现象:添加学生后,列表里姓名显示???或乱码。原因:JSP 页面pageEncoding、Servlet 的setCharacterEncoding、数据库连接 URL 的characterEncoding、MySQL 表的字符集,这四处只要有一处不是 UTF-8 就会出问题。解决:JSP 头部写<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>,Servlet 里req.setCharacterEncoding("UTF-8")和resp.setContentType("text/html;charset=UTF-8")都加上,连接 URL 带characterEncoding=utf8,建库时用utf8mb4。改完重启 Tomcat,别只刷新页面。

5.2 驱动版本不匹配:现象是启动报时区或 SSL 错误

现象:Tomcat 启动后访问登录页 500,日志里出现The server time zone value '?D1ú±ê×?ê±??' is unrecognized或Establishing SSL connection without server's identity verification is not recommended。原因:MySQL 8.0 驱动连 5.7 库,或连接 URL 没配时区。解决:URL 后面加?serverTimezone=Asia/Shanghai&useSSL=false,驱动版本和数据库版本对齐。如果pom.xml里是 5.1.x 驱动,换成8.0.28并同步改jdbc.driver类名。

5.3 外键约束导致删除失败:现象是删学生报 1451

现象:在管理页面删除一个学生,后台抛Cannot delete or update a parent row: a foreign key constraint fails。原因:fee或repair表里有该学生的关联记录,外键挡住了删除。解决:要么先删子表记录再删主表,要么在dao里做级联删除,要么把外键改成ON DELETE CASCADE。我一般倾向在业务层显式删,因为级联删太隐蔽,容易误删费用记录。

5.4 分页查询的 SQL 拼接:现象是第二页数据重复

现象:学生列表第一页正常,点第二页出现和第一页一样的数据。原因:LIMIT的偏移量算错,或者ORDER BY的字段有重复值导致排序不稳定。解决:确认LIMIT (pageNum-1)*pageSize, pageSize的公式,并且ORDER BY id ASC加一个唯一字段兜底。如果代码里用字符串拼 SQL,还要防注入,把pageNum用PreparedStatement的setInt传。

5.5 文档与代码不一致:现象是按文档配了却跑不通

现象:doc里的作业.docx写的数据库名是dorm_db,但sql脚本里建的是studenthousionmanager。原因:文档和代码不是同一轮改出来的,实训包常见。解决:以sql脚本和db.properties为准,文档只用来理解业务模块划分。遇到字段对不上,直接看bean类和建表语句,那才是真正跑起来的依据。

6. 二次开发与验证:把这份底稿改成能交差的版本

拿到这份代码,直接交上去大概率会跟别人撞车,所以最后一步是改出差异。我的习惯是先跑通登录和列表,然后挑一个模块做增强。比如给宿舍分配加一个「按班级批量分配」:在service里循环调用assignRoom,但要注意每次调用都开事务会慢,可以改成一个事务里批量处理。再比如给费用模块加一个「导出 CSV」的 Servlet,用resp.setContentType("text/csv")和PrintWriter直接写流,不依赖任何额外 jar。

验证改动是否生效,别只看页面。我一般会开两个窗口:一个操作页面,一个在 MySQL 命令行里SELECT对应的表。分配一个学生后,立刻查student.room_id和room.occupied是否同步变了;缴费后查fee.status和pay_date是否落库。这样能抓到「页面提示成功但数据库没动」的假成功。

还有一个容易被忽略的点:out/artifacts目录里可能有作者之前编译好的 war 或 exploded 目录,别直接拿那个部署,因为里面的db.properties可能还连着作者本地的库。以src为准重新编译,改完配置再打包。最后,如果你要写实训报告,把doc里的架构图用自己的话重画一遍,数据库表用SHOW CREATE TABLE导出真实结构贴进去,比抄文档里的描述更靠谱。从那以后我每次拿到这种实训包,都强制自己先跑一遍「建库→导脚本→改配置→部署→点一遍所有菜单」,确认没有暗坑再动手改,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询