尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Java宿舍管理系统实战:从环境搭建到部署排错全流程

发布时间:2026/9/28 1:21:51

资讯中心
01
ARTICLE

Java宿舍管理系统实战:从环境搭建到部署排错全流程

Java宿舍管理系统实战:从环境搭建到部署排错全流程
简介这份资源是面向Java Web初学者与课程设计者的学生宿舍管理系统完整项目包基于Java语言实现涵盖住宿信息管理、宿舍与床位分配、日常行为记录等模块适合用作毕业设计、课程大作业或Java Web入门实战参考。压缩包共661个文件约77.19MB以400个js、46个css等前端资源为主辅以35个java源码、40个class编译文件、8个xml与8个html页面另含sql数据库脚本、mp4辅导视频、docx部署文档及多套项目压缩包覆盖从源码到上线的完整链路。目前已有1615人学习下载。读者可获得MVC分层源码、数据库结构设计、Servlet与JSP交互实现、权限控制与异常处理思路以及MySQL5与MySQL8两套可运行项目便于对照部署文档快速搭建环境、理解DAO封装与资源分配算法是提升数据库设计与后端编程能力的实用素材。1. 从一份 Java 课设压缩包说起宿舍管理系统到底在管什么每年毕业季计算机专业的学生都会面对同一个任务做一个「学生宿舍管理系统」。这个标题看起来平平无奇但它背后牵扯的东西比想象中多——Java 基础、面向对象编程、数据库增删改查、JDBC 连接池、前后端交互、部署上线几乎把本科阶段的核心知识点串了一遍。很多人拿到一份「源代码数据库部署文档辅导视频」的压缩包第一反应是解压、导入、跑起来然后发现数据库连不上、Tomcat 版本不对、页面 404折腾一整天连登录页都进不去。这篇笔记不讲空泛的课程设计意义而是把一套典型的 Java 宿舍管理系统从环境搭建、数据库设计、核心功能实现到部署排错按一线开发的思路拆开讲清楚。适合两类人一是正在做课设、需要一份能跑通的参考方案的学生二是想拿这个项目练手 Java Web 全流程的初学者。读完你应该能自己搭出一套能用的系统而不是只会复制粘贴。2. 环境选型与数据库设计别在第一步就翻车2.1 JDK、Tomcat、MySQL 的版本搭配一套 Java Web 项目能不能跑起来七成取决于环境版本是否匹配。我见过太多人 JDK 17 配 Tomcat 8或者 MySQL 8 用着 5.x 的驱动包报错信息看得一头雾水。下面这套组合是我在多个课设项目里验证过最稳的组件推荐版本说明JDK1.8 或 111.8 兼容性最好绝大多数课设源码基于此Tomcat8.5 或 9.0不要用 Tomcat 10包名从 javax 变成 jakarta老代码全挂MySQL5.7 或 8.08.0 需要改驱动类名和连接 URL 参数IDEIntelliJ IDEA 或 EclipseIDEA 对 Maven 项目支持更好构建工具Maven 3.6如果项目是 Maven 结构版本别太低安装 JDK 后必须配环境变量这是新手最容易漏的一步。Windows 下配置JAVA_HOME指向 JDK 安装目录然后在Path里加上%JAVA_HOME%\bin。验证命令java -version javac -version两条命令输出的版本号必须一致。如果java能跑但javac报「不是内部或外部命令」说明只配了 JRE 没配 JDK或者 Path 里指向了错误的目录。MySQL 安装时记住 root 密码字符集选utf8mb4。建库的时候显式指定字符集避免中文乱码CREATE DATABASE dormitory_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci;注意MySQL 8.0 默认认证插件是caching_sha2_password老版本的 JDBC 驱动连不上。要么升级驱动到 8.x要么把用户认证方式改回mysql_native_password。2.2 宿舍管理系统的表结构怎么设计数据库设计是这类系统的核心。宿舍管理说到底就是几张表的关联学生、宿舍楼、房间、入住记录、管理员。下面是我一般会用的最小可用表结构字段不多但够用。-- 学生表 CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, student_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 联系电话, password VARCHAR(64) NOT NULL COMMENT 登录密码, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 宿舍楼表 CREATE TABLE building ( id INT PRIMARY KEY AUTO_INCREMENT, building_no VARCHAR(10) NOT NULL UNIQUE COMMENT 楼号, building_name VARCHAR(50) COMMENT 楼名, floors INT DEFAULT 6 COMMENT 楼层数, manager VARCHAR(50) COMMENT 楼管员 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 房间表 CREATE TABLE room ( id INT PRIMARY KEY AUTO_INCREMENT, building_id INT NOT NULL, room_no VARCHAR(10) NOT NULL COMMENT 房间号, capacity INT DEFAULT 4 COMMENT 可住人数, occupied INT DEFAULT 0 COMMENT 已住人数, status TINYINT DEFAULT 0 COMMENT 0空闲 1满员 2维修, FOREIGN KEY (building_id) REFERENCES building(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 入住记录表 CREATE TABLE checkin ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, room_id INT NOT NULL, checkin_date DATE COMMENT 入住日期, checkout_date DATE COMMENT 退宿日期, status TINYINT DEFAULT 1 COMMENT 1在住 0已退, FOREIGN KEY (student_id) REFERENCES student(id), FOREIGN KEY (room_id) REFERENCES room(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这几张表的关系是一栋楼有多个房间一个房间可以住多个学生入住记录表把学生和房间关联起来。room表里的occupied字段是冗余设计每次有人入住或退宿就更新它这样查空床位不用每次都去 count 入住记录性能更好。提示外键约束在课设里可以加但生产环境很多团队会去掉外键改在应用层保证一致性。原因是外键会让数据迁移和批量操作变麻烦。课设阶段加上没问题能帮你理解表之间的关系。2.3 JDBC 连接池配置别每次操作都新建连接新手写 JDBC 最常见的错误是每次查询都DriverManager.getConnection()一个页面加载下来建了七八个连接数据库直接被你拖垮。正确做法是用连接池。课设里最常用的是 Druid 或 C3P0Druid 配置简单、监控功能强我一般首选它。在pom.xml里加依赖dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.20/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency然后写一个工具类管理连接public class DBUtil { private static DataSource dataSource; static { try { Properties props new Properties(); // 从类路径加载配置文件 props.load(DBUtil.class.getClassLoader() .getResourceAsStream(druid.properties)); dataSource DruidDataSourceFactory.createDataSource(props); } catch (Exception e) { throw new RuntimeException(数据库连接池初始化失败, e); } } // 获取连接 public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } // 释放资源注意关闭顺序ResultSet - Statement - Connection public static void close(Connection conn, Statement stmt, ResultSet rs) { try { if (rs ! null) rs.close(); } catch (SQLException ignored) {} try { if (stmt ! null) stmt.close(); } catch (SQLException ignored) {} try { if (conn ! null) conn.close(); } catch (SQLException ignored) {} } }druid.properties配置文件放在src/main/resources下driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/dormitory_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8 usernameroot password你的密码 initialSize5 maxActive20 maxWait3000参数说明initialSize是启动时创建的连接数maxActive是最大连接数maxWait是获取连接的超时时间毫秒。课设场景下 5 到 20 完全够用。url里的serverTimezone必须设否则 MySQL 8 会报时区错误useSSLfalse关掉 SSL 警告。3. 核心功能实现登录、入住、退宿三条主线3.1 登录模块从明文到加盐哈希课设里很多源码的登录密码是明文存的这在答辩时容易被老师挑刺。改成 MD5 加盐花不了多少时间但能体现你的安全意识。核心逻辑是注册时生成随机盐值把盐和密码拼接后做哈希盐和哈希值都存库登录时用同样的盐做哈希再比对。public class PasswordUtil { // 生成随机盐 public static String generateSalt() { byte[] salt new byte[16]; new SecureRandom().nextBytes(salt); return Base64.getEncoder().encodeToString(salt); } // 加盐哈希 public static String hash(String password, String salt) { try { MessageDigest md MessageDigest.getInstance(SHA-256); md.update(Base64.getDecoder().decode(salt)); byte[] digest md.digest(password.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(digest); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(哈希算法不可用, e); } } // 校验密码 public static boolean verify(String input, String salt, String storedHash) { return hash(input, salt).equals(storedHash); } }登录的 Servlet 里先根据学号查出盐和哈希值再调用verify。这里有个细节如果学号不存在不要直接返回「用户不存在」统一返回「学号或密码错误」避免被人枚举学号。protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String studentNo req.getParameter(studentNo); String password req.getParameter(password); StudentDao dao new StudentDao(); Student stu dao.findByNo(studentNo); if (stu null || !PasswordUtil.verify(password, stu.getSalt(), stu.getPassword())) { req.setAttribute(msg, 学号或密码错误); req.getRequestDispatcher(/login.jsp).forward(req, resp); return; } req.getSession().setAttribute(currentUser, stu); resp.sendRedirect(req.getContextPath() /student/index.jsp); }注意getSession()默认 30 分钟超时课设演示够用。如果要做「记住我」得额外加 Cookie 或 Token别直接把密码塞 Cookie 里。3.2 入住与退宿事务保证数据一致性入住操作要同时做三件事插入一条入住记录、更新房间的已住人数、如果房间满了还要改状态。这三步必须在一个事务里否则中途失败就会出现「记录有了但人数没加」的脏数据。这是答辩时老师最爱问的点。public boolean checkIn(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. 插入入住记录 String insertSql INSERT INTO checkin(student_id, room_id, checkin_date, status) VALUES(?,?,?,1); PreparedStatement ps2 conn.prepareStatement(insertSql); ps2.setInt(1, studentId); ps2.setInt(2, roomId); ps2.setDate(3, new java.sql.Date(System.currentTimeMillis())); ps2.executeUpdate(); // 3. 更新房间人数和状态 String updateSql UPDATE room SET occupied occupied 1, status IF(occupied 1 capacity, 1, 0) WHERE id ?; PreparedStatement ps3 conn.prepareStatement(updateSql); ps3.setInt(1, roomId); ps3.executeUpdate(); conn.commit(); return true; } catch (SQLException e) { try { if (conn ! null) conn.rollback(); } catch (SQLException ignored) {} e.printStackTrace(); return false; } finally { try { if (conn ! null) { conn.setAutoCommit(true); conn.close(); } } catch (SQLException ignored) {} } }关键点在SELECT ... FOR UPDATE它会对这行记录加排他锁防止两个学生同时抢最后一个床位。没有这个锁并发下会出现超住。退宿逻辑反过来更新入住记录状态为已退、房间人数减一、状态改回空闲同样要包在事务里。3.3 分页查询与条件筛选宿舍列表、学生列表都需要分页。课设里最常见的写法是用LIMIT加偏移量SELECT s.*, r.room_no, b.building_no FROM student s LEFT JOIN checkin c ON s.id c.student_id AND c.status 1 LEFT JOIN room r ON c.room_id r.id LEFT JOIN building b ON r.building_id b.id WHERE s.name LIKE CONCAT(%, ?, %) ORDER BY s.id DESC LIMIT ?, ?;LIMIT的第一个参数是偏移量(pageNum - 1) * pageSize第二个是每页条数。这里有个性能坑数据量大了以后LIMIT 100000, 10会扫描前十万行再丢弃非常慢。课设数据量小无所谓但面试被问到分页优化时要能说出「用游标或子查询先定位主键再关联」的思路。Java 层封装一个 PageResult 对象包含当前页数据列表、总记录数、总页数、当前页码前端根据这些渲染分页条。总记录数用单独的COUNT(*)查询别和列表查询混在一起。4. 部署上线从 IDEA 到 Tomcat 的最后一公里4.1 打 WAR 包与 Tomcat 部署项目在 IDEA 里跑通不代表能部署。部署到独立 Tomcat 需要打成 WAR 包。如果用 Maven在pom.xml里把打包方式设为 warpackagingwar/packaging然后执行mvn clean package -DskipTests生成的target/xxx.war复制到 Tomcat 的webapps目录下启动 Tomcat 会自动解压。访问地址是http://localhost:8080/项目名/。如果 8080 被占用改conf/server.xml里的 Connector 端口。注意WAR 包名决定了访问路径。如果打出来是dormitory-1.0-SNAPSHOT.war访问路径就是/dormitory-1.0-SNAPSHOT/很丑。在pom.xml的buildfinalName里指定成dorm访问路径就干净了。4.2 数据库脚本导入与初始数据部署文档里一般会带一个.sql文件。导入方式有两种命令行和图形化工具。命令行更可靠mysql -u root -p dormitory_db dormitory_db.sql导入前先确认数据库已创建且字符集是utf8mb4。导入后检查表是否齐全USE dormitory_db; SHOW TABLES; SELECT COUNT(*) FROM student;如果中文显示成问号说明导入时客户端字符集不对。在导入命令前加--default-character-setutf8mb4mysql -u root -p --default-character-setutf8mb4 dormitory_db dormitory_db.sql初始管理员账号一般在 SQL 文件末尾的 INSERT 语句里密码可能是明文或哈希。如果是哈希用前面写的PasswordUtil生成一个替换进去。4.3 常见部署报错的定位思路部署阶段报错五花八门但定位思路是固定的先看 Tomcat 日志再看应用日志最后看数据库。Tomcat 日志在logs/catalina.out应用报错堆栈都在里面。几个高频错误ClassNotFoundException: com.mysql.cj.jdbc.Driver—— 驱动包没放进WEB-INF/lib。Maven 项目检查依赖 scope 是不是provided是的话改成默认或compile。Access denied for user rootlocalhost—— 密码错了或者 MySQL 没授权远程访问。本地部署一般不会遇到远程问题就是密码不对。404—— 路径错了或者web.xml里 Servlet 映射配错。检查访问 URL 是否带了项目名检查WebServlet注解的 value 和实际请求路径是否一致。500且堆栈指向NullPointerException—— 九成是某个对象没初始化或者数据库查出来是 null 没判空。顺着堆栈行号去看那一行代码。5. 避坑与排查那些让我熬夜的瞬间5.1 中文乱码三个地方都要设对现象页面输入中文存到数据库变成???或者查出来显示乱码。原因乱码可能出现在三个环节——JSP 页面编码、请求编码、数据库编码。任何一个没设对都会乱。解决JSP 页面头部加% page contentTypetext/html;charsetUTF-8 %Servlet 里在取参数前设req.setCharacterEncoding(UTF-8)数据库和表都用utf8mb4JDBC URL 加characterEncodingutf8。四个地方全对才行缺一不可。5.2 连接池耗尽连接忘了关现象系统用一会儿就卡死报wait millis 3000, active 20意思是 20 个连接全被占用新请求等 3 秒拿不到连接就超时。原因某个 DAO 方法里Connection没关或者异常路径下没走到close。连接池的连接是有限的漏几个就满了。解决所有数据库操作必须放在try-finally里finally中关闭连接。更好的做法是用 try-with-resourcestry (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { // 操作 } catch (SQLException e) { e.printStackTrace(); }这样无论是否异常资源都会自动关闭。检查代码里有没有getConnection()之后没有对应close()的地方。5.3 事务不生效自动提交没关现象入住操作中途抛异常但入住记录已经写进去了房间人数却没更新数据不一致。原因conn.setAutoCommit(false)没设或者设了但异常时没rollback或者用了连接池导致conn不是同一个。解决确认事务代码块里用的是同一个Connection对象setAutoCommit(false)在第一条 SQL 之前调用catch块里必须rollbackfinally里恢复setAutoCommit(true)再关闭。用连接池时尤其注意别在事务中间又去DBUtil.getConnection()拿了一个新连接。5.4 Tomcat 启动慢或端口冲突现象启动 Tomcat 卡在Starting Servlet engine很久或者报Address already in use: bind。原因端口被占用或者项目里有死循环的ServletContextListener。解决端口冲突用netstat -ano | findstr 8080找到占用进程要么杀掉要么改 Tomcat 端口。启动慢检查web.xml里有没有load-on-startup的 Servlet 在做耗时操作比如全表扫描。5.5 修改代码后页面没变化现象改了 JSP 或 Java 代码重启 Tomcat 后页面还是旧的。原因浏览器缓存了旧页面或者 Tomcat 的work目录里有编译好的旧 class。解决浏览器强制刷新CtrlF5。Tomcat 的话删掉work/Catalina目录再启动。IDEA 里如果配了热部署有时候热部署失败也会导致旧代码还在跑最稳妥的办法是 clean 后重新 build。6. 从能跑到好用几个让答辩加分的小技巧系统能跑通只是及格线答辩时老师更想看你对细节的思考。这里分享几个我踩过坑之后总结的改进点花不了多少时间但能让项目看起来成熟不少。第一个是操作日志。每次入住、退宿、修改学生信息都往一张operation_log表里记一条谁、什么时间、做了什么操作、操作对象是谁。表结构很简单CREATE TABLE operation_log ( id INT PRIMARY KEY AUTO_INCREMENT, operator VARCHAR(50) COMMENT 操作人, action VARCHAR(100) COMMENT 操作类型, target VARCHAR(100) COMMENT 操作对象, detail VARCHAR(500) COMMENT 详情, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;在 Service 层的关键方法末尾调一下写日志的工具方法就行。答辩时演示「查看操作记录」功能比单纯增删改查有说服力得多。第二个是数据导出。老师经常会问「这些数据能不能导出来」。用 Apache POI 把学生列表导出成 Excel代码量不大public void exportStudents(ListStudent list, OutputStream out) throws IOException { try (XSSFWorkbook wb new XSSFWorkbook()) { XSSFSheet sheet wb.createSheet(学生信息); // 表头 String[] headers {学号, 姓名, 性别, 班级, 电话, 宿舍}; XSSFRow headerRow sheet.createRow(0); for (int i 0; i headers.length; i) { headerRow.createCell(i).setCellValue(headers[i]); } // 数据行 int rowIdx 1; for (Student s : list) { XSSFRow row sheet.createRow(rowIdx); row.createCell(0).setCellValue(s.getStudentNo()); row.createCell(1).setCellValue(s.getName()); row.createCell(2).setCellValue(s.getGender() 1 ? 男 : 女); row.createCell(3).setCellValue(s.getClassName()); row.createCell(4).setCellValue(s.getPhone()); row.createCell(5).setCellValue(s.getRoomNo()); } wb.write(out); } }注意 POI 的XSSFWorkbook用于.xlsx格式HSSFWorkbook用于.xls。数据量超过几万行时 XSSF 内存占用会很高可以换SXSSFWorkbook流式写入。导出接口的响应头要设Content-Disposition: attachment; filename学生信息.xlsx文件名用URLEncoder.encode处理一下中文。第三个是权限控制。课设里经常只有一种管理员角色其实可以拆成「超级管理员」和「楼管员」两级。超级管理员能管所有楼楼管员只能管自己那栋楼的学生。实现方式是在building表加一个manager_id字段关联管理员查询时根据当前登录用户的角色加WHERE条件。这个改动涉及权限拦截器稍微复杂一点但答辩时是明显的加分项。最后一个习惯每次改完代码先本地跑一遍完整流程——登录、查列表、入住、退宿、导出——再提交。我吃过太多次「改了一个小地方结果把别的功能弄挂」的亏后来养成了改完必走一遍主流程的习惯省下了大量回头排查的时间。希望这些经验能帮到你少走几个我当年走过的弯路。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。