
简介JavaWeb宿舍管理系统是一套基于JavaWeb技术的宿舍管理项目源码面向高校、培训机构及个人开发者可高效管理宿舍分配、床位信息、住宿查询、报修工单等日常事务同时适合课程设计、毕业设计或JavaWeb入门进阶练习。压缩包共238个文件约4.1MB其中java/jsp/class文件构成后端业务逻辑js/css/html构建前端交互界面sql脚本用于初始化数据库配合png、jpg等图片素材和xml/properties等配置文件结构清晰可直接导入Eclipse并结合Tomcat部署运行。该系统覆盖Servlet、JSP、JDBC、Spring框架、MVC分层等关键知识点并包含用户登录、角色权限管理等安全机制源码目录完整、注释明确便于快速理解各模块调用关系。已有1560人学习下载尤其适合希望以真实项目提升开发能力的初学者参考借鉴。1. 项目概述为什么“宿舍管理系统”成了JavaWeb课设的经典之选如果你正在搜“JavaWeb宿舍管理系统”这类项目大概率是两种情况一是学校课程设计给出了这个题目二是你在找一个能完整串起JSP、Servlet、MySQL的练手项目。这个题目确实经典因为宿舍管理系统的业务边界足够清晰——无非是宿舍楼栋、房间、学生、入住退宿这几张表的事但又能把增删改查、登录鉴权、多条件查询、分页这些Web开发的常见操作全涵盖进去作为个人练手或毕设非常合适。我最初拿到这个题目时第一反应是“这会不会太简单了”。真做下来才发现一个五脏俱全的管理系统并不好写。它不像商城项目那样有商品、购物车、订单这些复杂的业务联动但正因为简单反而对基本功要求更高登录状态怎么保持、权限怎么区分、数据库连接怎么写才不容易泄漏、分页怎么在JSP里优雅实现每一处都是面试官爱问、考试爱考的点。本文会完整复盘一个基于JavaWeb的宿舍管理系统的设计与实现思路覆盖技术选型、数据库设计、核心功能编码、前后端联调以及我踩过的典型坑。无论你是刚学完Java基础想找个项目练手还是正在赶课设的DDL这篇文章都能提供一份可以直接参考的完整方案。2. 功能规划与核心技术选型2.1 需求拆解一个宿舍管理系统到底要做哪些事在动笔写代码之前先花时间把需求拆清楚这是整个项目最值得投入的环节。宿舍管理系统的典型用户角色有两种管理员和宿管员有的学校还会加一个学生角色但核心是前两类。先列功能清单用户登录与注册管理员和宿管员使用账号密码登录密码加密存储登录后Session保持状态。宿舍楼管理对宿舍楼栋进行增删改查包括楼名、楼号、楼层数、每层房间数等基础信息。宿舍房间管理房间的入住人数、床位数量、当前居住学生列表判断房间是否已满。学生入住管理录入学生信息分配宿舍退宿时释放床位。学生信息查询按学号、姓名、楼栋、房间号等多条件组合查询和分页展示。来访登记可选加分项对外来人员进行登记和注销。管理员面板统计总楼栋数、总房间数、入住率、空余床位等核心指标展示在首页看板。这些功能看起来多但本质上可以归纳为“两张主表加若干关联表”的CRUD宿舍楼表、宿舍房间表、学生表、入住记录表以及用户表。业务不难难的是如何把CRUD写得干净、健壮、好维护。这也是这个项目真正有价值的地方——它逼着你像个正规项目一样去组织代码而不是在一个Servlet里堆几百行。2.2 选型思路为什么是JSP Servlet MySQL而不是Spring Boot拿到这个题目很多同学第一反应是“现在谁还不用Spring Boot”。我的观点是如果你想练手Spring Boot直接去写一个更复杂的项目更有价值。但宿舍管理系统如果用了Spring Boot MyBatis-Plus Thymeleaf反而会掩盖很多底层逻辑比如Servlet的生命周期、HttpServletRequest和HttpServletResponse的工作方式、数据库连接怎么手动管理这些都是校招笔试和面试的基础考点。我的技术栈选择如下组件选型理由前端页面JSP JSTL EL课设标配能直观地理解服务端渲染的过程后端控制层Servlet 3.0最基础的JavaWeb请求处理机制便于理解HTTP协议业务层Service DAO两层比Controller直接写JDBC更好维护也符合分层思想数据库MySQL 5.7最常见的教学和面试版本8.0也兼容JDBC工具手写DBUtil 连接池DruidDruid可以监控SQL执行情况方便调试前端样式Bootstrap adminlte模板可用静态模板不需要手写CSS界面美观且响应式这个组合在2025年的今天看起来有点“复古”但恰恰因为“复古”你能完全掌控每个环节。当我后来去面试时面试官对这个项目的兴趣远超那些千篇一律的Spring Boot商城因为他可以顺着问你“Session实现原理”“SQL注入如何避免”“连接池参数怎么调”而这些都是这个项目里真实遇到的。2.3 开发环境准备用VSCode还是IDEA这里插一句工具选择。很多人搜索“vscode创建javaweb项目”说明有一部分同学没有装IDEA或者电脑配置有限。VSCode确实能开发JavaWeb项目只要你装好以下插件Extension Pack for Java内含语言服务、调试器、Maven支持Tomcat for Java支持在VSCode中直接启动Tomcat创建项目的推荐方式是用Maven骨架在VSCode中按CtrlShiftP输入“Java: Create Java Project”选“Maven Archetype”然后选maven-archetype-webapp这样会生成标准的webapp目录结构src/main/java、src/main/webapp、WEB-INF/web.xml。不过我还是建议只要电脑能带得动尽量用IDEA Community版免费或Eclipse。因为VSCode对JavaWeb的调试体验相对弱尤其是断点调试时不太方便。我自己在VSCode里踩过半天Tomcat部署路径的坑后面换到IDEA后顺手多了。3. 数据库设计与核心表结构3.1 表结构设计先从“入住”这个业务核心出发数据库设计是整个系统的基础设计得好不好直接决定后面写业务逻辑的复杂度。当时我的寝室长跟我说了一句至今受用的话“你先把数据的流转路径画出来再开表。”我最后设计的是5张核心表t_user用户表字段包括id、username、passwordMD5加密、role1管理员、2宿管员、create_time。t_building宿舍楼表字段包括id、building_name楼名如“梅苑”、building_no楼号、floor_count层数、room_count每层房间数、remark。t_room宿舍房间表字段包括id、building_id外键关联楼栋、room_no房间号如“501”、bed_count床位数、current_people当前人数。t_student学生表字段包括id、student_no学号、name、gender、college学院、class_name班级、phone、create_time。t_dorm_record入住记录表字段包括id、student_id外键、room_id外键、check_in_time、check_out_time、status1入住中、0已退宿。这里有一个设计细节值得展开为什么不直接把room_id放到学生表里面因为宿舍分配是会产生历史记录的。一个学生入住A栋101后来调宿到B栋202如果只在学生表里存一个room_id那第一次的入住记录就丢了。用单独的入住记录表可以让“学生”与“房间”形成多对多关系而这正是业务现实的正确建模。这个设计在和面试官聊的时候是很好的加分项。3.2 建表SQL及注意事项直接贴出我建表的SQL片段做了必要的注释-- 用户表 CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录名, password VARCHAR(64) NOT NULL COMMENT 密码存MD5值, role TINYINT DEFAULT 2 COMMENT 1管理员,2宿管员, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 宿舍楼表 CREATE TABLE t_building ( id INT PRIMARY KEY AUTO_INCREMENT, building_name VARCHAR(50) NOT NULL, building_no VARCHAR(20) NOT NULL, floor_count INT DEFAULT 6, remark VARCHAR(200) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 房间表 CREATE TABLE t_room ( id INT PRIMARY KEY AUTO_INCREMENT, building_id INT NOT NULL, room_no VARCHAR(10) NOT NULL, bed_count INT DEFAULT 4, current_people INT DEFAULT 0, CONSTRAINT fk_room_building FOREIGN KEY (building_id) REFERENCES t_building(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 学生表 CREATE TABLE t_student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL UNIQUE COMMENT 学号, name VARCHAR(50) NOT NULL, gender TINYINT DEFAULT 1 COMMENT 1男,2女, college VARCHAR(100), class_name VARCHAR(100), phone VARCHAR(20), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 入住记录表 CREATE TABLE t_dorm_record ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, room_id INT NOT NULL, check_in_time DATETIME DEFAULT CURRENT_TIMESTAMP, check_out_time DATETIME, status TINYINT DEFAULT 1 COMMENT 1入住中,0已退宿, CONSTRAINT fk_record_student FOREIGN KEY (student_id) REFERENCES t_student(id), CONSTRAINT fk_record_room FOREIGN KEY (room_id) REFERENCES t_room(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;几个关键决策字符集统一用utf8mb4因为utf8在MySQL里存储emoji和特殊汉字时可能报错。我在第一版项目里用了utf8结果录入学生姓名时遇到生僻字直接插入失败后来通宵把所有表改成utf8mb4才解决。外键约束一定要加。虽然很多生产项目为了性能会去掉外键但课设项目加上外键既能保证数据一致性也能在答辩时展示你对数据库规范化的理解。唯一索引建在t_student.student_no上防止同一学号重复录入。3.3 补充基础数据与密文密码初始化管理员账号时不要直接在SQL里写明文密码。我用一个工具类生成MD5public static String md5(String source) { try { MessageDigest md MessageDigest.getInstance(MD5); byte[] bytes md.digest(source.getBytes(StandardCharsets.UTF_8)); StringBuilder sb new StringBuilder(); for (byte b : bytes) { String hex Integer.toHexString(b 0xff); if (hex.length() 1) sb.append(0); sb.append(hex); } return sb.toString(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } }登录对比时对用户输入的密码做同样的MD5后与数据库比对。虽然MD5不算安全的加密算法彩虹表可破解但作为课设完全够用如果要提升安全性可以加盐。4. 后端核心代码实战从工具类到业务闭环4.1 数据库连接管理手写连接池还是用Druid数据库连接这块有几个选择DriverManager原始连接、C3P0、DBCP、Druid。我的建议是直接用Druid理由很实际配置简单、自带监控页面、性能稳定。在src/main/resources下放druid.propertiesdriverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/dorm_system?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai usernameroot password你自己的密码 initialSize5 maxActive20 maxWait3000再用一个DBUtil封装public class DBUtil { private static DataSource dataSource; static { try (InputStream is DBUtil.class.getClassLoader().getResourceAsStream(druid.properties)) { Properties props new Properties(); props.load(is); dataSource DruidDataSourceFactory.createDataSource(props); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }然后是BaseDAO封装JdbcTemplate风格的常用方法写一个queryForList和一个update就够用了。在这个项目里我没有引入第三方ORM而是用Apache Commons DButils它是一个非常轻量的封装避免写大量重复的try-catch-finally。如果你不想引入额外依赖手写PreparedStatement也不难。4.2 基于三层架构的功能分层实现代码组织我采用标准三层架构servlet包接收请求解析参数调用service跳转或重定向。service包业务逻辑比如“入住”这个方法里需要同时更新房间人数和插入入住记录。dao包只做SQL交互。这个分层的价值在“入住登记”这个方法上体现得最清楚public boolean checkIn(Student student, int roomId) throws Exception { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 // 1. 插入学生 studentDao.insert(conn, student); // 2. 更新房间人数1 int updated roomDao.increasePeople(conn, roomId); if (updated ! 1) throw new RuntimeException(房间更新失败); // 3. 插入入住记录 recordDao.insert(conn, student.getId(), roomId); conn.commit(); // 全部成功才提交 return true; } catch (Exception e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(true); conn.close(); } }这个过程是面试时的高频提问点为什么要用事务因为插入学生和更新房间是两个独立的SQL操作任何一个失败都会导致数据不一致比如学生录入了但没住进房间。用事务把它们捆绑成一个“原子操作”要么全部成功要么全部回滚。4.3 登录鉴权Session和过滤器是标配登录逻辑不算复杂从请求中取username和password调用userService校验成功后把用户对象放进Session重定向到首页。关键是退出登录时一定要调用session.invalidate()或者session.removeAttribute(user)。但“登录后能访问哪些页面”这个问题就需要过滤器来解决。一个傻瓜式的写法是在每个Servlet里判断Session是否有用户但这样既重复又容易漏。标准做法是写一个AuthFilterWebFilter(/*) public class AuthFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; String uri request.getRequestURI(); // 放行登录相关的页面和静态资源 if (uri.endsWith(/login.jsp) || uri.contains(/user/login) || uri.endsWith(.css) || uri.endsWith(.js) || uri.endsWith(.png)) { chain.doFilter(req, resp); return; } // 未登录用户跳回登录页 HttpSession session request.getSession(false); if (session null || session.getAttribute(user) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); } }这里有一个非常值得注意的点getSession()和getSession(false)的区别。前者如果当前请求没有Session会创建一个新的后者不会。在过滤器里用getSession(false)是标准的习惯否则每次未登录请求都会被创建出无用的Session浪费服务器内存。如果系统支持管理员和宿管员两种角色可以在过滤器里增加权限判断——比如“添加楼栋”的操作只允许管理员执行。但课设阶段做到角色区分即可更细粒度可以用一个注解拦截器实现属于加分项不强制。4.4 核心业务实现学生宿舍分配与信息展示宿舍分配的逻辑是用户选择一栋楼 → 展示该楼所有房间的空余情况 → 选中某个有空位的房间 → 录入学生信息并提交。这里最容易出错的点是并发场景下两个管理员同时给同一个房间分配学生会导致房间人数超员。解决思路是执行更新语句时带上条件用乐观锁的思路UPDATE t_room SET current_people current_people 1 WHERE id ? AND current_people bed_count这条SQL保证了“当前人数小于床位数时才能加1”。如果返回值是0说明房间已满或不存在业务层直接提示“房间已满”。这种写法比先查再更新更合理因为两条SQL之间存在时间窗口并发下会出现都查到了人数是3床位数4、都去分配的情况。学生信息展示页面我会设计成表格 多条件筛选 分页查询条件学号精确模糊都支持、姓名、楼栋下拉框、房间号。分页参数pageNum、pageSizeSQL用LIMIT ?, ?。页面上显示“共xx条记录第x/xx页”。分页的SQL只需注意偏移量计算(pageNum - 1) * pageSize。JSP页面上做一个简单的分页导航条即可不必引入PageHelper这类框架手动实现反而能加深理解。4.5 JSP页面的核心写法与细节JSP里的核心逻辑一定要用JSTL标签不要用% %脚本段否则页面一团糟。在web.xml或项目依赖中引入JSTL后在JSP顶部写% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % % taglib prefixfmt urihttp://java.sun.com/jsp/jstl/fmt %遍历房间列表展示时一个典型片段c:forEach items${roomList} varroom tr td${room.roomNo}/td td${room.bedCount}/td td${room.currentPeople}/td td c:choose c:when test${room.currentPeople room.bedCount} span classtag tag-full已满/span /c:when c:otherwise span classtag tag-available有空位/span /c:otherwise /c:choose /td /tr /c:forEach注意EL表达式中访问的是JavaBean的属性名也就是getRoomNo()方法对应的roomNo首字母大小写一定要对否则渲染出来是空的。5. 常见问题与排查技巧实录5.1 中文乱码问题乱码是JavaWeb新手最大的敌人。我总结出三个层面JSP页面乱码在JSP第一行写% page contentTypetext/html;charsetUTF-8 languagejava %。请求参数乱码POST请求在Servlet里调用request.setCharacterEncoding(UTF-8)必须在读取任何参数之前GET请求需要在Tomcat的server.xml里配置URIEncoding或者在代码里手动new String(param.getBytes(ISO-8859-1), UTF-8)。数据库乱码连接URL加characterEncodingutf8并且确认表结构是utf8mb4。排查乱码有一个笨但有效的方法在前端页面填写“测试中文”在Servlet里打印System.out.println(param)比较控制台编码和页面显示就能判断是哪一层出了问题。5.2 Tomcat启动失败或者访问404404的第一反应先看项目部署名访问路径是http://localhost:8080/项目名/...而不是http://localhost:8080/...。如果确认路径没问题但依然404检查Servlet注解或web.xml中的映射URL前缀是否正确。如果启动时端口被占用用netstat -ano | findstr 8080Windows或lsof -i:8080macOS/Linux找到进程ID杀掉即可。5.3 数据库连接失败的常见原因驱动类名版本问题MySQL 5.x用com.mysql.jdbc.DriverMySQL 8.x用com.mysql.cj.jdbc.Driver。时区报错连接URL里必须带serverTimezoneAsia/Shanghai。密码错误检查druid.properties是否被正确加载到classpath中有时候改了配置没重启Tomcat使用的是旧配置。5.4 JS和CSS加载不出来这个问题比较隐蔽。如果你的页面使用了过滤器且放行的URL写得过于严格比如只放行了*.css但页面里引用CSS的路径是相对路径导致过滤器没有拦截匹配到就会表现为“页面没有样式”。排查方法打开浏览器开发者工具F12看Network里CSS的请求状态是404还是403如果是404说明路径拼接有问题需要前端加${pageContext.request.contextPath}作为上下文路径前缀。5.5 登录后刷新页面提示重复提交或Session失效表单重复提交是一个高频问题。解决思路有两种使用PRG模式Post/Redirect/Get用户在登录成功后Servlet用response.sendRedirect()而不是request.getRequestDispatcher().forward()跳转到首页这样浏览器地址栏变成GET请求刷新不会重复提交表单。表单加隐藏token登录时生成一个随机token存在Session中表单提交时带上tokenServlet校验后删除。这种方法能防止表单重复提交也能抵御CSRF攻击面试时可以提一提。6. 写在最后踩过这些坑你的JavaWeb才算入门做这个项目最大的收获不是学会了JSP标签怎么用、SQL怎么写而是真正理解了“一个请求从浏览器到服务器再到数据库最后回到浏览器”的完整链路。这种全链路感知是看再多的教学视频也替代不了的。最后分享一个小技巧项目做完后手动删除Tomcat的work目录下的缓存文件再重新启动项目。很多时候你改了Java代码但页面还显示旧内容就是因为Tomcat的JSP缓存没有更新。这个坑我遇到不下三次每次都以为是代码没保存。另外一个建议是给项目加上一个“操作日志”功能记录谁在什么时间执行了什么操作。这个功能不难但能显著提升项目的完整度和答辩分数。如果在做课设这个加分会很实在。如果你正在做JavaWeb的第一个完整项目我建议不要急着追求项目数量这个宿舍管理系统够你折腾半个月了。把它吃透把每个“为什么”都问一遍你的JavaWeb基本功就能超过大多数人。本文还有配套的精品资源点击获取