news 2026/10/10 12:22:17

JSP+MySQL宿舍管理系统实训:从跑通到面试避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JSP+MySQL宿舍管理系统实训:从跑通到面试避坑指南

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

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

很多人看到“实训作业基于JSP+MySQL的学生宿舍管理系统源代码+数据库+文档”这个标题,第一反应是:这不就是大学里最典型的那类课程设计吗?但如果你真把它当成一次“交完就删”的作业,那就亏了。这套东西本质上是一个完整的、可运行的 Java Web 单体应用:前端用 JSP 渲染页面,后端用 Servlet 或简单 JavaBean 处理业务,数据落在 MySQL 里,配套还有建表脚本和一份需求文档。它覆盖了学生信息录入、宿舍分配、床位管理、报修登记、访客记录这些真实场景,麻雀虽小,五脏俱全。

对新手来说,它是你从“只会写 Hello World”到“能跑起一个带登录、带增删改查、带权限区分的系统”的最短路径;对熟手来说,它是一个极佳的“反面教材”——你能一眼看出哪些地方用了过时的写法,哪些地方藏着 SQL 注入的风险,哪些地方的事务边界没处理好。我见过不少同学,简历上写“熟悉 Java Web”,面试官一问“你做过什么项目”,支支吾吾说不出细节。这套系统如果真能自己跑通、改通、讲通,足够撑起一场 20 分钟的技术面。下面我就按“先跑起来、再改明白、最后避坑”的顺序,把这条路走一遍。

2. 把项目跑起来:环境、建库与第一个登录页

2.1 环境选型:为什么 JDK 8 + Tomcat 8.5 最稳

这套实训作业的代码年代感通常比较强,很多写法依赖javax.servlet包,而不是 Jakarta EE 的jakarta.servlet。如果你上来就装 JDK 17 + Tomcat 10,大概率会在启动时看到ClassNotFoundException: javax.servlet.http.HttpServlet,然后开始怀疑人生。我的建议是:JDK 8 或 JDK 11,Tomcat 8.5 或 9.0,MySQL 5.7 或 8.0,IDE 用 IntelliJ IDEA 社区版就够。别小看版本匹配这件事,我见过太多人卡在“项目跑不起来”上,最后发现只是 Tomcat 10 把包名换了。

数据库驱动也要注意:MySQL 8.0 用com.mysql.cj.jdbc.Driver,5.7 用com.mysql.jdbc.Driver。连接串里如果没加serverTimezone=Asia/Shanghai,MySQL 8.0 会直接报时区错误。这些细节文档里不一定写,但你不处理,登录页都出不来。

2.2 建库建表:把 SQL 脚本跑对顺序

拿到数据库脚本后,别急着一次性全选执行。常见做法是先建库,再建表,最后插初始数据。顺序错了,外键约束会直接拦你。下面是一段典型的建表语句,我加了注释说明每个字段的用途:

-- 创建数据库,字符集用 utf8mb4,避免中文乱码 CREATE DATABASE dorm_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE dorm_db; -- 学生表:存基础信息,学号做唯一索引 CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, stu_no VARCHAR(20) NOT NULL UNIQUE COMMENT '学号', name VARCHAR(50) NOT NULL COMMENT '姓名', gender TINYINT DEFAULT 1 COMMENT '1男 2女', class_name VARCHAR(50) COMMENT '班级', phone VARCHAR(20) COMMENT '联系电话' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 宿舍表:记录楼号、房间号、床位数 CREATE TABLE dorm ( id INT PRIMARY KEY AUTO_INCREMENT, building VARCHAR(20) NOT NULL COMMENT '楼号', room_no VARCHAR(10) NOT NULL COMMENT '房间号', bed_total INT DEFAULT 4 COMMENT '总床位', bed_used INT DEFAULT 0 COMMENT '已用床位' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 住宿记录表:学生和宿舍的关联,一个学生只能有一条有效记录 CREATE TABLE live_record ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, dorm_id INT NOT NULL, bed_no INT COMMENT '床位号', status TINYINT DEFAULT 1 COMMENT '1在住 0退宿', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (student_id) REFERENCES student(id), FOREIGN KEY (dorm_id) REFERENCES dorm(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

执行完建表后,再插入管理员账号和几条测试数据。注意live_record的外键依赖student和dorm,所以插入顺序不能反。如果你用的是 Navicat 或 DataGrip,建议把脚本拆成三段执行,每段执行完检查一下有没有报错,别一口气跑完看到一片红。

2.3 配置数据源与跑通登录

JSP 项目连接数据库通常有两种方式:一种是在 JSP 页面里直接写 JDBC 代码,另一种是用context.xml配置连接池。实训作业里前者更常见,虽然不规范,但胜在直观。下面是一个典型的数据库工具类:

// DBUtil.java:统一获取连接,避免每个 Servlet 都写一遍驱动加载 public class DBUtil { private static final String URL = "jdbc:mysql://localhost:3306/dorm_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"; private static final String USER = "root"; private static final String PASSWORD = "你的密码"; static { try { // MySQL 8.0 用 cj 驱动,5.7 去掉 cj Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { // 关闭顺序:ResultSet -> PreparedStatement -> Connection try { if (rs != null) rs.close(); } catch (SQLException e) {} try { if (ps != null) ps.close(); } catch (SQLException e) {} try { if (conn != null) conn.close(); } catch (SQLException e) {} } }

这段代码的关键点有三个:驱动类名要和 MySQL 版本匹配;连接串里必须带时区和字符集参数;关闭资源要按顺序来。登录逻辑一般放在LoginServlet里,接收username和password,用PreparedStatement查admin表,查到了就session.setAttribute("user", admin),然后重定向到首页。如果登录后页面一片空白,先看 Tomcat 日志里有没有SQLException,再看web.xml里 Servlet 映射有没有写对。

3. 读懂代码结构:JSP 页面、Servlet 与 DAO 的分工

3.1 典型目录结构与各层职责

一个规范的 JSP 项目目录通常长这样:src下按包名分servlet、dao、bean、util;web或WebContent下放jsp页面、css、js、WEB-INF。WEB-INF里的web.xml是部署描述符,lib下放 MySQL 驱动 jar 包。很多人把 jar 包放在项目根目录而不是WEB-INF/lib,结果 Tomcat 启动时报NoClassDefFoundError,这就是没搞清类加载路径。

DAO 层负责和数据库打交道,一个方法对应一条 SQL;Servlet 层负责接收请求、调 DAO、把结果塞进request或session,再转发给 JSP;JSP 只负责展示,尽量别在里面写业务逻辑。我见过最离谱的写法是在 JSP 里直接new一个数据库连接然后循环查数据,页面加载慢不说,连接泄漏是迟早的事。

3.2 用 Servlet 处理宿舍分配的核心逻辑

宿舍分配是这套系统里业务最重的一块。它要同时更新dorm表的bed_used和插入live_record,这两步必须在一个事务里,否则会出现“床位占了但记录没写”的脏数据。下面是一个简化后的分配方法:

// DormDao.java:分配床位,事务保证一致性 public boolean assignBed(int studentId, int dormId, int bedNo) { Connection conn = null; PreparedStatement ps1 = null, ps2 = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 // 第一步:检查床位是否已满 String checkSql = "SELECT bed_total, bed_used FROM dorm WHERE id = ? FOR UPDATE"; ps1 = conn.prepareStatement(checkSql); ps1.setInt(1, dormId); ResultSet rs = ps1.executeQuery(); if (rs.next() && rs.getInt("bed_used") >= rs.getInt("bed_total")) { conn.rollback(); return false; // 床位已满 } // 第二步:更新已用床位 String updateSql = "UPDATE dorm SET bed_used = bed_used + 1 WHERE id = ?"; ps2 = conn.prepareStatement(updateSql); ps2.setInt(1, dormId); ps2.executeUpdate(); // 第三步:插入住宿记录 String insertSql = "INSERT INTO live_record(student_id, dorm_id, bed_no) VALUES(?,?,?)"; ps2 = conn.prepareStatement(insertSql); ps2.setInt(1, studentId); ps2.setInt(2, dormId); ps2.setInt(3, bedNo); ps2.executeUpdate(); conn.commit(); return true; } catch (SQLException e) { try { if (conn != null) conn.rollback(); } catch (SQLException ex) {} e.printStackTrace(); return false; } finally { try { if (conn != null) conn.setAutoCommit(true); } catch (SQLException e) {} DBUtil.close(conn, ps2, null); } }

这里有几个参数和写法值得注意:FOR UPDATE是行级锁,防止两个管理员同时分配同一张床位;setAutoCommit(false)开启事务后,任何一步失败都要rollback;finally里把autoCommit恢复成true再关闭连接,避免连接池回收后状态污染。如果你用的是连接池,这一步尤其重要,否则下一个拿到这条连接的人会莫名其妙地处于事务中。

3.3 JSP 页面里怎么安全地展示数据

JSP 里展示数据,最怕两件事:一是中文乱码,二是 XSS。中文乱码在页面顶部加<%@ page contentType="text/html;charset=UTF-8" language="java" %>基本能解决;XSS 则需要你在输出用户输入的内容时做转义。比如学生姓名如果直接<%= student.getName() %>,有人把名字改成一段脚本,页面就会执行。常见做法是用 JSTL 的<c:out>标签,它默认会转义 HTML:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <table> <c:forEach items="${studentList}" var="stu"> <tr> <td><c:out value="${stu.stuNo}"/></td> <td><c:out value="${stu.name}"/></td> <td><c:out value="${stu.className}"/></td> </tr> </c:forEach> </table>

用<c:out>而不是<%= %>,是这套老项目里最容易补上、也最值得补的一个安全习惯。另外,forEach的items属性对应 Servlet 里request.setAttribute("studentList", list)的 key,名字必须一致,大小写敏感。如果页面显示空白,先检查 list 是不是 null,再检查 JSTL 的 jar 包有没有放进WEB-INF/lib。

4. 避坑与排查:这套实训代码最容易翻车的 5 个地方

4.1 中文乱码:现象是问号,根因在编码链

现象:登录后学生姓名显示成???或者乱码方块。原因通常有三层:数据库表字符集不是utf8mb4、连接串没加characterEncoding=utf8、JSP 页面没声明charset=UTF-8。解决方法是三层一起查:先用SHOW CREATE TABLE student;看表字符集,再检查 JDBC URL,最后看 JSP 顶部 page 指令。三层都对了,乱码基本消失。如果还有问题,检查 Tomcat 的server.xml里 Connector 有没有URIEncoding="UTF-8"。

4.2 空指针异常:九成是 session 过期或参数没传

现象:点击某个功能,页面报NullPointerException,堆栈指向 Servlet 里某一行。原因通常是session.getAttribute("user")返回了 null,而代码直接拿它调方法。解决方法是先判断 null,再决定跳登录页还是继续。另一个常见来源是request.getParameter("id")拿到 null,然后Integer.parseInt(null)直接炸。养成习惯:所有外部输入先判空,再转换。

4.3 数据库连接泄漏:Tomcat 跑一会儿就卡死

现象:系统刚启动正常,操作几次后越来越慢,最后所有请求都超时。原因是在 DAO 里getConnection()之后,某个分支return了但没关闭连接。解决方法是把关闭逻辑统一放到finally块,或者用 try-with-resources。如果你用的是连接池,可以在池配置里加removeAbandoned="true"和removeAbandonedTimeout="60"做兜底,但这只是后悔药,根本还是要自己关。

4.4 SQL 注入:登录框输入' or '1'='1就能进

现象:在登录页用户名输入' or '1'='1,密码随便填,居然登录成功了。原因是登录 SQL 用了字符串拼接而不是PreparedStatement。解决方法是把所有带参数的 SQL 都改成?占位符,用setString、setInt传值。这套实训代码里如果还有拼接写法,建议你逐个改掉,这不仅是安全问题,也是面试必问的点。

4.5 外键约束导致删除失败

现象:想删除一个学生,报Cannot delete or update a parent row。原因是live_record表里有这个学生的住宿记录,外键拦住了。解决方法有两种:先删子记录再删主记录,或者把外键改成ON DELETE CASCADE。但级联删除要慎用,宿舍管理场景里,退宿记录通常要保留,所以更稳妥的做法是软删除——把status改成 0,而不是物理删除。

5. 从能跑到能讲:把实训作业变成面试素材的三个技巧

5.1 给系统加一个“操作日志”表,讲清楚 AOP 思想

面试官问“你项目里有没有做过日志”,很多人只能答“用了 log4j 打日志”。你可以更进一步:在数据库里建一张oper_log表,记录谁在什么时间做了什么操作。然后在 Servlet 里用一个Filter统一拦截,把请求参数和当前用户写进去。这样你就能讲清楚“过滤器链”“请求生命周期”“横切关注点”这些概念,而不是干巴巴地说“我写过增删改查”。

// OperLogFilter.java:统一记录操作日志 public class OperLogFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpSession session = request.getSession(false); if (session != null && session.getAttribute("user") != null) { String uri = request.getRequestURI(); String method = request.getMethod(); // 异步写库,避免阻塞主请求 LogService.save(uri, method, session.getAttribute("user").toString()); } chain.doFilter(req, resp); // 放行 } }

这段代码的价值不在于多复杂,而在于你能顺着它讲出“Filter 和 Interceptor 的区别”“为什么日志要异步写”“session 什么时候创建”。面试里,细节就是说服力。

5.2 用表格对比“物理删除”和“软删除”的取舍

维度物理删除软删除
数据可恢复不可恢复可恢复
查询复杂度简单需加status=1条件
外键影响容易触发约束不影响
存储占用释放空间持续占用
适用场景临时数据业务记录

这张表你可以直接用在面试回答里。宿舍管理系统中,学生退宿、报修记录这些都应该软删除,因为历史数据有查询价值。而像临时验证码这种,物理删除没问题。能说清楚“什么场景用什么策略”,比背概念强得多。

5.3 把“文档”读成需求,而不是附件

很多人拿到实训作业的文档,直接扔到一边,只看代码。其实文档里往往写了功能清单和业务流程,那是你理解“为什么这么设计”的钥匙。比如文档里写“学生退宿后床位释放”,你就要去想:释放床位是bed_used - 1还是重新计算?如果并发退宿会不会算错?顺着文档追问下去,你就能发现代码里可能存在的并发问题,然后自己动手改成UPDATE dorm SET bed_used = (SELECT COUNT(*) FROM live_record WHERE dorm_id = ? AND status = 1)。这种“从文档到代码再到改进”的闭环,才是实训作业最大的价值。

我自己的习惯是:每拿到一份这类项目,先跑通,再挑一个最不顺眼的地方改掉,最后把改动过程写成笔记。改过三五个项目之后,你会发现面试时能讲的东西,根本不是背出来的,而是改出来的。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 12:22:12

Spring Boot+微信小程序代驾系统:订单状态机与落地避坑指南

简介&#xff1a;围绕微信小程序代驾系统展开的毕业设计论文文档&#xff0c;适合计算机相关专业学生、Java 后端开发者&#xff0c;以及正在完成 Spring Boot 类毕设项目的读者参考。内容以代驾业务为场景&#xff0c;系统阐述从选题背景、需求分析到系统设计、技术选型、模块…

作者头像 李华
网站建设 2026/10/10 12:20:34

一个未达标自媒体项目的完整复盘:从工作分解到风险管理的真实案例

简介&#xff1a;北京邮电大学信息与通信工程学院大二下课程期末论文&#xff0c;以作者真实运营自媒体账号的经历为分析对象&#xff0c;完整梳理了项目管理与经济决策知识的应用过程。正文涵盖项目简介、工作分解结构、成本收益分析、竞争战略、失败原因及风险管理、结语等模…

作者头像 李华
网站建设 2026/10/10 12:19:28

FIDIC银皮书中文版工程化拆解:从PDF到可检索条款库与风险检查清单

简介&#xff1a;FIDIC合同&#xff08;银皮书中文版&#xff09;PDF文档&#xff0c;面向国际工程承包、项目管理及合同管理领域的从业者与学习者&#xff0c;用于查阅EPC交钥匙工程标准合同条款、理解合同签订与执行规范。文档以中文完整呈现银皮书正文&#xff0c;目录结构清…

作者头像 李华
网站建设 2026/10/10 12:19:02

PostgreSQL逻辑复制实战:从WAL到Kafka的实时数据同步

简介&#xff1a;本资源面向数据库开发与数据集成工程师&#xff0c;提供一套基于PostgreSQL逻辑复制功能的实时数据变更捕获与同步系统源码。系统通过解析WAL日志捕获数据变更&#xff0c;将其转换为可执行的SQL语句&#xff0c;并借助Kafka消息队列实现PostgreSQL到异构数据源…

作者头像 李华
网站建设 2026/10/10 12:18:25

《创业之路》-1027-细读商业经典 - 创新是有利的变异,是人为的主动的推动个人、企业、国家、社会持续演进的动力

创新&#xff1a;人为定向的有利变异&#xff0c;文明主动演进的核心动力这个定义精准打通了生物演化与社会演进的底层逻辑&#xff1a;生物的进化靠随机变异 自然选择&#xff0c;是被动的、盲目的、无方向的&#xff1b;而人类社会的演进靠主动创新 市场 / 社会筛选&#x…

作者头像 李华