简介这是一份javaweb学生宿舍管理系统的完整设计与实现源码包配有数据库脚本面向需要完成毕业设计、期末大作业或课程设计的Java学习者。项目经本地编译可正常运行评审分达98分难度适中适合作为入门级Web项目参考。资源共133个文件压缩包约3.3MB核心内容包括JSP页面、Java后端代码、编译后的Class文件、SQL数据库文件以及CSS样式、JS脚本、XML配置和项目工程文件便于还原开发环境并理解前后端交互流程。功能上覆盖学生信息、宿舍楼栋、宿管人员、水电费管理等常见模块并提供登录、查询、新增、修改、删除等基础操作可直接部署运行或二次扩展。目前已有145人学习下载对于需要快速搭建宿舍管理场景、掌握JavaWeb增删改查模式的读者来说是一份经过校验的高分参考方案。1. 学生宿舍管理系统这个 JavaWeb 项目为什么值得动手做如果你正在找课程设计或毕业设计的题目又不想选那些烂大街的图书管理、网上商城那「学生宿舍管理系统」这个方向值得认真考虑。它表面看是一套标准的 JavaWeb 增删改查但内部包含了多角色权限、宿舍分配冲突处理、报修流程状态流转这些真实业务场景正好踩中评委和老师最看重的两个点数据库设计是否合理、业务逻辑是否有闭环。我见过太多人把这类系统做成单表 CRUD 的玩具最后答辩被问一句「两个学生同时选同一张空床位怎么办」就卡住了——这不是代码问题是设计问题。本篇就按我实际带项目的方式从数据库建模讲到 IDEA 里跑通再讲到答辩演示的加分细节整个过程用的都是最常见的 JavaWeb MySQL Tomcat 技术栈不引入 Spring Boot 这类框架符合大部分高校课程设计的选题要求。2. 先立架构再写代码角色权限与数据库表设计2.1 三种角色划分管理员、宿管员、学生的权限边界动手写代码之前第一件事不是建表而是把系统的角色和权限边界画清楚。学生宿舍管理系统常见的角色有三种系统管理员、宿管员、学生。很多初学者把权限做成一个字段硬编码进页面里用 if 判断显示不同菜单这种做法在课程设计里能跑通但答辩时容易被追问「权限控制放在页面层绕过页面直接访问 Servlet 怎么办」所以我在项目里会加一层基于 Filter 的 URL 拦截。管理员维护宿舍楼、宿舍房间、学生信息分配床位查看全部报修记录。宿管员管理本楼栋的入住记录、处理报修、登记来访。学生查看自己的入住信息、提交报修、修改个人密码。权限落地的常见做法是用户表里存 role 字段登录后写入 Session再写一个 LoginFilter 拦截除 login 和静态资源外的所有 Servlet 路径。这样做的好处是权限校验和业务代码解耦后续加角色不用改动每个 Servlet。我在带课程设计时反复强调一个原则先画角色用例图再画数据库 ER 图最后才写建表语句顺序反了大概率会在做报修功能时发现缺字段。2.2 数据库五张核心表学生、宿舍、分配记录、报修、用户宿舍管理系统的表结构设计是整个项目的评分命脉。最少需要五张表用户表sys_user、学生表student、宿舍表dormitory、分配记录表assign_record、报修表repair_record。扩展表可以加楼栋表building但五张表已经能覆盖核心业务闭环。学生表和用户表为什么分开这是我在答辩指导时最常解释的问题。学生表存学号、姓名、专业、班级这类静态信息用户表存登录账号、密码、角色。分开的好处是如果未来系统对接学校统一认证只需要替换用户表的数据来源学生表完全不动。宿舍表的核心字段是楼栋号、房间号、床位数量、已住人数其中已住人数这个字段是典型的空间换时间设计——查询空床位时不用 count 寝室的分配记录直接比对已住人数和床位数量列表加载速度快一个数量级。分配记录表是宿舍管理的核心业务表字段至少包含 id、student_id、dormitory_id、bed_no、assign_time、status。status 字段用来标记当前分配是否有效学生调宿时不是删掉旧记录而是把旧记录 status 置为 0再插入一条新记录。这样保留完整历史轨迹答辩时能讲出「数据可回溯」的设计思想。报修表字段包含 id、student_id、dormitory_id、content、status、create_time、handle_timestatus 用整数枚举0 待处理、1 处理中、2 已完成。2.3 建库脚本书写规范字符集、外键与初始化数据建库脚本我习惯用 UTF-8 编码和 InnoDB 引擎。以下是核心建表语句直接复制到 Navicat 或 MySQL 命令行执行即可CREATE DATABASE IF NOT EXISTS dormitory_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE dormitory_db; CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, role TINYINT NOT NULL COMMENT 1-管理员 2-宿管员 3-学生, student_id INT DEFAULT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE dormitory ( id INT PRIMARY KEY AUTO_INCREMENT, building_no VARCHAR(20) NOT NULL, room_no VARCHAR(20) NOT NULL, bed_count INT NOT NULL DEFAULT 4, used_count INT NOT NULL DEFAULT 0, UNIQUE KEY uk_building_room (building_no, room_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;建表时有三处容易翻车第一表名和字段名不要用 MySQL 的保留字比如order、group、desc这类单词我见过有人把报修表命名为repair_order直接导致 SQL 执行报错第二字符集必须统一指定 utf8mb4否则插入 emoji 表情或特殊符号时数据库连接层会报「Incorrect string value」错误第三外键要不要加我的建议是逻辑外键优于物理外键——表之间不建 FOREIGN KEY 约束而是在业务代码里维护关联关系。理由很简单课程设计的数据量很小物理外键带来的数据一致性收益体现不出来反而会在删除宿舍时被外键约束卡住给演示环节增加不必要的风险。初始化数据建议写成独立的data.sql文件包含两个管理员账号、一个宿管员、十名左右学生和四五间宿舍。为什么要写初始化数据因为答辩演示时不可能现场注册十个学生再分配宿舍预置数据能让系统打开就有内容可看。注意学生的初始密码统一设为默认值并在文档中说明强制修改密码的逻辑。3. 在 IDEA 中导入源码从环境配置到跑通的最小步骤3.1 导入 JavaWeb 源码的两种工程结构说明拿到一份 JavaWeb 宿舍管理系统源码最常见的有两种工程结构一种是老派的普通 Web 项目目录结构是 src 源码目录 WebContent 或 webapp 静态资源目录用 Eclipse 或 MyEclipse 导出另一种是基于 Maven 的 Web 项目根目录有 pom.xml。用 IDEA 打开时处理方式不同我分别说明。普通 Web 项目的导入路径是IDEA 欢迎页选择 Open选中项目根目录IDEA 检测到.classpath和.project文件后会提示是否作为 Eclipse 项目导入选择「Create project from existing sources」或直接信任 Eclipse 格式。导入后需要手动配置 Artifacts这一步很容易被忽略菜单 File → Project Structure → Artifacts点加号选择 Web Application Exploded把 webapp 目录关联进去。不配置这一步Tomcat 运行时会出现 404 报错因为 IDEA 不知道该把哪个目录发布到 Tomcat。Maven 项目的导入更简单Open 时选中 pom.xml 文件IDEA 会自动解析依赖并下载。宿舍管理系统这种课程设计用的依赖不多常见就三四个mysql-connector-java、javax.servlet-api、jstl、commons-fileupload。如果下载慢在 pom.xml 里追加阿里云镜像仓库即可这段配置我每次都直接复制repositories repository idaliyun/id urlhttps://maven.aliyun.com/repository/public/url /repository /repositories3.2 配置 Tomcat 与部署避免 404 和端口冲突Tomcat 的配置是第二个高频踩坑点。我一般用 Tomcat 8.5 或 9.0 搭配 JDK 1.8版本匹配上注意Tomcat 10 及以上的 javax.servlet 包名变成了 jakarta.servlet旧代码直接部署会报 ClassNotFoundException这是血的教训。IDEA 中配置 Tomcat 的路径Run → Edit Configurations → 加号 → Tomcat Server Local。注意不是 Tomcat Server Remote。然后在 Deployment 标签页点加号选 Artifact把项目以 war exploded 形式部署。这里解释一下为什么选 exploded 而不是 warexploded 模式支持 JSP 和静态资源的热部署修改后刷新页面就能看到效果war 包模式每次都要重新打包演示时改代码特别耽误时间。热部署的代价是偶尔会触发「OutOfMemoryError: PermGen space」解决方法是把 IDEA 的 VM 参数里的-XX:MaxPermSize调大到 256m。端口冲突是课堂上出现频率最高的报错之一启动 Tomcat 时提示Port 8080 is already in use。原因一般是后台还挂着一个之前没关掉的 Tomcat 实例或者本机的其他服务占用了 8080。我一般用命令行快速定位netstat -ano | findstr 8080 taskkill /PID 进程号 /F也可以用 IDEA 右侧的 Services 面板手动停掉旧实例。如果改端口要同时改 Tomcat 配置里的 HTTP port 和项目里所有硬编码的访问地址否则页面跳转时会跑到旧端口上这个坑我踩过不止一次。3.3 数据库连接配置统一管理连接参数宿舍管理系统的数据库连接配置理论上应该写在db.properties或jdbc.properties文件里然后用一个工具类读取。但很多课程设计源码用的是硬编码方式直接在 JDBC 工具类里写死 URL、用户名、密码。硬编码最大的问题是换环境部署时要去代码里逐个找容易漏改所以我接手源码的第一步就是把这些参数集中到一个配置文件里。jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/dormitory_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password123456这里的 URL 参数有讲究。useUnicodetruecharacterEncodingutf8解决中文乱码useSSLfalse避免 MySQL 8.0 连接时弹出 SSL 警告serverTimezoneAsia/Shanghai是必须的MySQL 8.0 及以上版本如果不在连接串里指定时区直接报Server returns invalid timezone错误。驱动类名也要注意版本差异MySQL 5.x 用com.mysql.jdbc.DriverMySQL 8.x 用com.mysql.cj.jdbc.Driver。如果本机装的是 MySQL 8.0 而源码里用的旧驱动连接时大概率报ClassNotFoundException。数据库连接的工具类写法上我建议用 ThreadLocal 包装 Connection 实现同一线程内复用这在事务处理时是刚需。宿舍分配这个操作要在一个事务里完成两步更新宿舍的 used_count 加一、插入分配记录。如果不用事务第一步成功第二步失败的话数据就脏了。下面的代码是我在项目里实际使用的工具类核心方法public class DBUtil { private static String driver; private static String url; private static String username; private static String password; static { try { InputStream in DBUtil.class.getClassLoader().getResourceAsStream(db.properties); Properties props new Properties(); props.load(in); driver props.getProperty(jdbc.driver); url props.getProperty(jdbc.url); username props.getProperty(jdbc.username); password props.getProperty(jdbc.password); Class.forName(driver); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, username, password); } public static void close(Connection conn, Statement stmt, ResultSet rs) { try { if (rs ! null) rs.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (stmt ! null) stmt.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn ! null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }这段代码的逻辑说明静态代码块在类加载时执行一次读取配置文件并注册驱动避免每次获取连接都重复加载驱动。关闭资源的顺序有讲究——ResultSet 先关Statement 其次Connection 最后关顺序反了会报「Connection is closed」或资源泄漏。很多源码偷懒只关 Connection短时间运行看不出问题但答辩演示时频繁刷新页面MySQL 连接数会耗尽报Too many connections到时候临时加 max_connections 参数只能救急。4. 核心功能实现登录鉴权、宿舍分配与报修流程4.1 登录鉴权与验证码Servlet Session Filter 的组合登录功能是每个评委必看的部分也是最容易拉开差距的地方。基础版是用户名密码比对进阶版要加验证码和 Filter 拦截。验证码不是加分项而是必备项因为课程设计答辩时评委一定会问「怎么防止暴力破解」没有验证码的项目在这道题上直接扣分。我使用的是简单的数字字母验证码用 Java 的 BufferedImage 生成不额外引入第三方库。登录 Servlet 的核心逻辑分几步接收表单参数 → 调用 DAO 查询用户 → 比对密码 → 写入 Session → 重定向到对应角色的首页。这里有两个细节容易疏漏第一密码比对要用 equals而不是字符串对象比较用永远返回 false这种低级错误在初学者代码里特别常见第二登录成功后要session.setAttribute(loginUser, user)并把用户角色也存进去后续 Filter 校验角色时直接读 Session。Filter 的拦截规则我给出一份可以直接用的配置思路拦截路径写/*但放行/login、/captcha、静态资源/static/*。放行静态资源很重要否则 CSS 和 JS 文件全被拦截页面样式全丢。Filter 内部逻辑public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req (HttpServletRequest) request; HttpServletResponse resp (HttpServletResponse) response; HttpSession session req.getSession(false); String uri req.getRequestURI(); if (uri.endsWith(/login) || uri.endsWith(/captcha) || uri.contains(/static/)) { chain.doFilter(request, response); return; } if (session null || session.getAttribute(loginUser) null) { resp.sendRedirect(req.getContextPath() /login); return; } chain.doFilter(request, response); }这段拦截逻辑的说明req.getSession(false)表示如果当前没有 Session 就返回 null而不是创建一个新 Session。如果写成getSession(true)未登录用户每次访问都会创建一个空 Session白白浪费内存也起不到拦截作用。放行前缀判断用endsWith而不是equals因为登录请求可能带参数或路径前缀不同留一点容错空间。重定向时使用req.getContextPath()拼路径这样项目部署在任意上下文路径下都不会写死路径导致跳转 404。4.2 宿舍分配与调宿事务保证数据一致性宿舍分配是整个系统业务复杂度最高的功能也是我判断一个项目是不是「高分项目」的分水岭。功能要求是学生选择有空位的宿舍和床位系统校验床位是否被占分配成功后宿舍的已住人数加一。如果学生调宿旧分配记录标记为失效新分配记录插入新宿舍已住人数加一、旧宿舍减一。这里最难的点是并发问题两个学生同时提交选同一个床位如果代码是「先查询是否为空 → 再插入分配记录」在高并发下会出现同一床位被分配两次。课程设计虽然没有真实并发压力但答辩时老师会问这个问题所以代码里必须用事务加行锁来处理。我给出的方案是查询空床位时使用SELECT ... FOR UPDATE锁住宿舍记录再执行插入提交事务后释放锁。Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 锁定宿舍行防止并发分配 String lockSql SELECT used_count, bed_count FROM dormitory WHERE id ? FOR UPDATE; PreparedStatement ps conn.prepareStatement(lockSql); ps.setInt(1, dormitoryId); ResultSet rs ps.executeQuery(); if (!rs.next()) { throw new RuntimeException(宿舍不存在); } int used rs.getInt(used_count); int bedCount rs.getInt(bed_count); if (used bedCount) { throw new RuntimeException(该宿舍已满员); } // 插入分配记录 String insertSql INSERT INTO assign_record (student_id, dormitory_id, bed_no, status) VALUES (?, ?, ?, 1); PreparedStatement ps2 conn.prepareStatement(insertSql); ps2.setInt(1, studentId); ps2.setInt(2, dormitoryId); ps2.setString(3, generateBedNo(used 1, bedCount)); ps2.executeUpdate(); // 更新已住人数 String updateSql UPDATE dormitory SET used_count used_count 1 WHERE id ?; PreparedStatement ps3 conn.prepareStatement(updateSql); ps3.setInt(1, dormitoryId); ps3.executeUpdate(); conn.commit(); } catch (Exception e) { if (conn ! null) conn.rollback(); throw new RuntimeException(分配失败原因 e.getMessage()); } finally { if (conn ! null) conn.setAutoCommit(true); DBUtil.close(conn, null, null); }这段代码有三个点要重点理解。第一FOR UPDATE是行级排他锁事务提交或回滚后锁自动释放这是保证床位不超卖的关键。第二generateBedNo(used 1, bedCount)是一个自定义方法用于生成床位的逻辑编号比如宿舍 4 人间已有 2 人入住第三个床位编号为 3这个编号只代表床位序号不跟物理床架绑定简单实用。第三事务中任何一步抛出异常都会回滚保证used_count和分配记录不会出现不一致。我在实际项目中还加了参数校验学生是否已在校、是否已经分配过宿舍防止同一个学生重复入住。4.3 报修流程闭环从提交到完成的五步状态机报修功能看起来是简单的增删改查但「流程闭环」是加分点。很多初学者的报修只有提交和查看没有状态流转评委一问「报修之后宿管怎么知道、修完怎么反馈」就答不上来。我的设计是把报修拆成五步学生提交 → 宿管员接单 → 处理中 → 完成 → 学生确认评价。实际代码中我用一个 int 字段 status 表示状态0 待接单、1 处理中、2 已完成、3 已评价。状态流转的核心是权限控制学生只能提交和查看自己的报修宿管员能查看本楼栋所有报修并修改状态。这里要注意一个常见设计缺陷——学生提交报修时报修单的 dormitory_id 不能让学生从前端随意传而是从当前登录用户的宿舍信息里取防止学生手动篡改表单把报修挂到别的宿舍。学生提交报修的 Servlet 关键代码HttpSession session req.getSession(); Student loginStudent (Student) session.getAttribute(loginStudent); // 从 session 取学生信息而不是信任前端传入的 studentId int studentId loginStudent.getId(); int dormitoryId loginStudent.getDormitoryId(); String content req.getParameter(content); if (content null || content.trim().isEmpty()) { resp.sendRedirect(repair_add.jsp?error1); return; } RepairDAO dao new RepairDAO(); boolean result dao.insert(studentId, dormitoryId, content.trim()); if (result) { resp.sendRedirect(repair_list.jsp?msgsuccess); } else { resp.sendRedirect(repair_add.jsp?error2); }这里最值得借鉴的是「从 Session 取当前登录用户信息」这个思路而不是从前端表单里获取用户标识。前端的 studentId 字段可以被伪造一旦学生把请求里的 studentId 改成别人的学号再配合后端不加校验就能以他人名义提交报修这是典型的越权漏洞。我在项目的 BaseServlet 设计里还会统一校验登录状态和角色权限每个 Servlet 只关心业务逻辑。报修列表的分页查询也是加分点。我用的写法是计算总页数后用LIMIT offset, pageSize分页查询前先统计总数SELECT COUNT(*) FROM repair_record WHERE dormitory_id ? AND status ? SELECT * FROM repair_record WHERE dormitory_id ? AND status ? ORDER BY create_time DESC LIMIT ?, ?注意分页查询里ORDER BY create_time DESC一定要加否则数据库默认按主键排序翻页后数据顺序可能错乱这在演示时翻到第二页会被看出逻辑不严谨。报修列表页的状态筛选建议用下拉框传 status 参数配合 MySQL 的索引使用效率更高。5. 避坑指南数据库连接、乱码和部署的五个常见问题5.1 数据库连接报错驱动类名与时区参数不匹配现象启动项目后访问任何需要查询数据库的页面浏览器直接 500Tomcat 日志里报ClassNotFoundException: com.mysql.jdbc.Driver或SQLException: The server time zone value。原因MySQL 8.x 的驱动类名从com.mysql.jdbc.Driver改成了com.mysql.cj.jdbc.Driver且要求 URL 中显式声明时区如果源码沿用了 MySQL 5.x 时代的配置新旧驱动不匹配就会出现以上报错。另一个可能性是 mysql-connector-java 的 jar 包没有打包进 WEB-INF/lib 目录IDEA 中依赖标红但项目仍能启动运行时才暴露问题。解决先确认本机 MySQL 版本mysql --version看一眼。然后按版本修改 db.properties 里的驱动类和 URL最后检查依赖的 jar 是否在 Artifacts 的 Output Layout 里。IDEA 的坑在于 Maven 依赖默认不会自动拷贝到 WEB-INF/lib需要右键项目 → Open Module Settings → Artifacts → 选中输出目录 → 右键 Put into Output Root。这个操作很隐蔽但必须做否则启动不报错、一查数据库就崩。5.2 中文乱码请求参数、页面显示和数据库三处不一致现象表单提交的中文姓名在页面显示为「??」或者数据库中存入的是乱码尤其是学生提交报修内容时问题最明显。原因乱码的本质是编码链路上某一环断掉了。完整链路包括浏览器页面编码 → POST 请求编码 → Servlet 读取参数编码 → 数据库连接编码 → 数据库存储编码 → 响应输出编码。任何一个环节不是 UTF-8中文就会变成问号。解决按三处排查。第一处JSP 页面头部加% page languagejava contentTypetext/html; charsetUTF-8 pageEncodingUTF-8%。第二处Servlet 中读取参数前强制设置编码request.setCharacterEncoding(UTF-8); response.setContentType(text/html; charsetUTF-8);注意setCharacterEncoding必须在第一次读取参数之前调用放后面就没效果了。第三处db.properties 的 URL 中带上characterEncodingutf8。如果数据库表已经是乱码数据可以用ALTER TABLE student CONVERT TO CHARACTER SET utf8mb4;转换。有一个容易忽视的点IDEA 的 Run Configuration 里 VM 参数加-Dfile.encodingUTF-8否则控制台日志中的中文也可能乱码虽然不影响功能但答辩演示时观感差。5.3 Tomcat 热部署失效改了 JSP 不生效现象修改 JSP 或 Servlet 后点击刷新浏览器页面还是旧内容有时要重启 Tomcat 才能看到更新。原因IDEA 中 Tomcat 的配置默认开启了热部署但只对 JSP 和静态资源有效Java 代码的修改需要重新编译IDEA 的 Build 操作没有自动执行。此外如果 Deployment 部署的不是 exploded 而是 war 包热部署完全不生效。解决运维层面养成改完代码后按CtrlF9手动 Build 的习惯。比热部署更稳的是启用 IDEA 的「Build project automatically」选项配合 Tomcat 配置里的On frame deactivation选择 Update resources。我在实际项目中的配置是On frame deactivation选 Update classes and resources这样切出 IDEA 窗口时自动编译更新浏览器刷新即可看到效果整个演示过程流畅很多。注意这个选项偶尔会触发类加载器问题导致内存泄漏如果发现页面报PermGen space或类冲突还是手动更新更稳妥。5.4 端口被占用Tomcat 启动即报错现象点击运行按钮控制台几秒后报Error running Tomcat: Address localhost:8080 is already in useTomcat 没有正常启动。原因上一次运行没有完全关闭IDEA 的 Tomcat 进程还在后台或者本机其他软件占用了 8080 端口常见的有被某个 Java 进程或 Nginx 占用。解决查询并结束占用进程。Windows 下用命令netstat -ano | findstr 8080拿到 PID 后去任务管理器结束对应进程或者用taskkill /PID 进程号 /F强制结束。建议顺手检查一下 8005、8009 这两个 Tomcat 管理端口是否被占用它们不冲突但一旦有残留进程同样会导致启动失败。改端口的做法能规避冲突但宿舍管理系统这种课程设计避免改了端口后所有页面链接都要跟着改所以一般不建议换端口。5.5 页面 404 或 ClassNotFoundExceptionArtifacts 配置缺失现象Tomcat 启动成功但访问项目名时返回 404 错误页面或者页面能打开但提交表单后报ClassNotFoundException: com.mysql.jdbc.Driver。原因IDEA 的 Artifacts 没有正确关联 webapp 目录导致发布到 Tomcat 的目录里缺少 JSP 文件依赖 jar 没有输出到 WEB-INF/lib导致运行时找不到类。解决打开 File → Project Structure → Artifacts确认 Tomcat 部署的 Artifact 是 Web Application Exploded且 Output Layout 里存在「Web Resource Directory 指向 webapp」的行。依赖 jar 的处理点击右侧的 Available Elements把 mysql-connector-java 等 jar 双击加入 Output Root 下的 WEB-INF/lib。检查方法是启动后到 Tomcat 的部署路径看一眼把实际发布的目录和项目文件结构做对比少什么补什么。这个坑我接手过至少五个学生的项目几乎每个都有此问题知道了就不慌。6. 答辩演示脚本把系统从「能跑」提升到「高分」的验证思路项目做完只是第一步课程设计的评分维度里演示和答辩占的比重往往接近四成。我建议你准备一份十五分钟左右的演示脚本按角色切换走完整条业务链路每一步都对应数据库的变化验证。具体做法是演示前把 MySQL 命令行打开运行USE dormitory_db; SELECT * FROM dormitory;先记录初始状态然后到页面上执行分配宿舍操作切回命令行再次查询这条记录让评委直观看到used_count从 3 变成 4分配记录表多了一行。这种「页面操作 → 数据变化」的对照演示比单纯点页面有说服力得多因为它证明你不是停留在前端适配而是真正打通了数据链路。答辩环节的高频问题集中在三个方向数据库设计理由、并发安全策略、权限控制方案。数据库设计上要能说明为什么用户表和学生表分开为什么用逻辑外键并发安全就往SELECT ... FOR UPDATE和事务回滚上引权限控制重点讲 Filter 拦截和 Session 取值防越权。这套代码结构里这些点都有体现你只需要在演示到对应功能时自然带出一句比如分宿舍时补一句「这里用了行级锁防止并发重复分配」评委的追问深度会明显降低。我个人的血泪经验是答辩前一天把 MySQL 服务重启一次、用初始化脚本把数据库灌一遍、完整走一遍核心流程。听起来简单但每年都有学生因为本地 MySQL 没设开机自启、演示时连不上库而翻车。还有一个小技巧是准备好预置数据用学号2024001登录学生账号直接演示报修流程省去现场注册的尴尬。希望这份笔记能帮你把这个项目从「照抄能跑」做到「讲得清楚」答辩顺利。本文还有配套的精品资源点击获取