ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Java+JSP+MySQL学生宿舍管理系统:从Excel到Web的实战指南

Java+JSP+MySQL学生宿舍管理系统:从Excel到Web的实战指南 简介这份资源是面向Java Web初学者与课程设计者的学生宿舍管理系统完整项目源码基于B/S架构将Java后端、JSP页面、MySQL数据库与Tomcat服务器整合为一个可运行的管理平台帮助解决宿舍信息录入、查询与维护效率低的问题。压缩包共80个文件约3.9MB其中26个jsp负责视图展示与表单交互11个java与22个class承载Servlet业务逻辑和数据模型10个css完成界面样式7个xml与2个jar支撑工程配置和依赖另附1个sql脚本用于建表与初始化数据。目前已有2020人学习下载。项目涵盖管理员登录验证、宿舍信息增删改查、宿舍分配与学生入住查询等模块目录结构清晰适合作为Web开发课程设计、毕业设计或JSP与Servlet分层实践的参考案例便于读者理解JDBC数据访问、请求转发与数据库设计思路。1. 从一张 Excel 宿舍表说起JavaJSPMySQL 的 Web 学生宿舍管理系统到底解决什么问题很多学校的宿舍管理还停留在「一张 Excel 表 楼管阿姨的纸质登记本」阶段学生换宿舍要跑三趟签字辅导员查空床位靠打电话后勤统计水电费得手动汇总。这套 JavaJSPMySQL 的 Web 学生宿舍管理系统本质就是把「床位、学生、楼栋、入住退宿记录」这几张核心表搬到浏览器里让宿管、辅导员、学生三类角色各看各的页面。它适合课程设计、毕业设计也适合中小院校做内部工具——技术栈足够老反而意味着资料多、坑少、部署门槛低。下面我按「先跑通再优化」的顺序把选型、建表、JSP 页面、避坑和进阶技巧讲透。2. 技术选型与数据库设计为什么这套老组合反而最稳2.1 为什么是 JSP 而不是前后端分离先说结论如果你的目标是「两周内交出一个能演示、能答辩、能真跑的系统」JSP 比 VueSpring Boot 更省事。原因有三点。第一JSP 天然是服务端渲染页面里直接写% %就能取到 request 里的数据不需要额外配 Nginx、跨域代理、Token 刷新那一套。第二JSP 配合 Servlet 的 MVC 模式一个StudentServlet处理增删改查转发到student_list.jsp展示逻辑链路短调试时看 Tomcat 控制台就够了。第三热词里频繁出现的「jsp 个人信息展示页面」「jsp 页面让加载完后刷新一次」说明大量同类项目都在用 JSP 做信息展示你遇到问题搜得到答案。当然代价也要说清楚JSP 把 Java 代码和 HTML 混在一起后期维护会痛苦高并发下 JSP 编译和 Session 占用内存比前后端分离高。但对宿舍管理这种「几十个管理员、几百个学生、并发个位数」的场景这些都不是问题。我一般会建议核心业务用 ServletJSP复杂交互比如床位图拖拽再局部引入 AJAX。2.2 数据库表设计五张表撑起整个系统宿舍管理的数据模型不复杂但字段设计错了后期改表很痛苦。下面是我实际用过的建表 SQLMySQL 5.7 和 8.0 都能跑。-- 楼栋表一栋楼一条记录 CREATE TABLE building ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 楼栋名如1号楼, floors INT NOT NULL DEFAULT 6 COMMENT 楼层数, gender TINYINT NOT NULL DEFAULT 1 COMMENT 1男 2女 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 宿舍表一个房间一条记录 CREATE TABLE dorm ( id INT PRIMARY KEY AUTO_INCREMENT, building_id INT NOT NULL, room_no VARCHAR(20) NOT NULL COMMENT 房间号如301, capacity INT NOT NULL DEFAULT 4 COMMENT 床位上限, occupied INT NOT NULL DEFAULT 0 COMMENT 已住人数, UNIQUE KEY uk_room (building_id, room_no), FOREIGN KEY (building_id) REFERENCES building(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 学生表和用户表分离避免权限字段污染业务字段 CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, sno VARCHAR(20) NOT NULL UNIQUE COMMENT 学号, name VARCHAR(30) NOT NULL, gender TINYINT NOT NULL, phone VARCHAR(20), dorm_id INT DEFAULT NULL COMMENT 为空表示未分配, bed_no TINYINT DEFAULT NULL COMMENT 床位号1-4, FOREIGN KEY (dorm_id) REFERENCES dorm(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 用户表登录用role区分角色 CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(30) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL COMMENT 存MD5别存明文, role TINYINT NOT NULL DEFAULT 3 COMMENT 1管理员 2宿管 3学生, student_id INT DEFAULT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 入住记录表退宿不删学生只改状态留痕 CREATE TABLE checkin_log ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, dorm_id INT NOT NULL, bed_no TINYINT NOT NULL, in_time DATETIME NOT NULL, out_time DATETIME DEFAULT NULL, operator VARCHAR(30) COMMENT 操作人 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;逻辑说明dorm表里的occupied是冗余字段每次分配/退宿都要同步更新好处是查空床位时不用COUNT子查询坏处是要保证事务一致。checkin_log是关键——很多同学做课程设计时直接删学生记录结果查历史入住查不到答辩时被问「退宿后数据去哪了」就答不上来。参数上utf8mb4必须显式指定否则学生姓名里的生僻字会变问号password字段留 64 位是为了以后换 SHA-256 不用改表。提示MySQL 8.0 默认认证插件是caching_sha2_password老版本 JDBC 驱动连不上会报Unable to load authentication plugin。要么升级mysql-connector-java到 8.x要么建用户时指定IDENTIFIED WITH mysql_native_password。2.3 项目分层与目录结构我一般按这个结构组织答辩时老师一看就懂src/ com.dorm.entity/ // Student, Dorm, Building com.dorm.dao/ // StudentDao, DormDao纯JDBC com.dorm.servlet/ // StudentServlet, LoginServlet com.dorm.util/ // DBUtil 连接池, MD5Util WebContent/ login.jsp student/list.jsp dorm/assign.jsp WEB-INF/web.xmlDBUtil用 Druid 或 C3P0 都行别每次DriverManager.getConnection连接泄漏跑一天就崩。下面是最小可用的 DBUtilpublic class DBUtil { private static DruidDataSource ds new DruidDataSource(); static { ds.setUrl(jdbc:mysql://localhost:3306/dorm?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/Shanghai); ds.setUsername(root); ds.setPassword(your_pwd); ds.setInitialSize(5); ds.setMaxActive(20); ds.setValidationQuery(SELECT 1); // 防止连接被MySQL主动断开 } public static Connection getConn() throws SQLException { return ds.getConnection(); } }serverTimezone不写会报时区错误这是 MySQL 8 的经典坑validationQuery是为了应对 MySQL 默认 8 小时空闲断连不配的话第二天早上第一个请求必挂。3. 从登录到床位分配ServletJSP 核心功能落地3.1 登录与角色拦截一个 Filter 搞定权限登录本身不难难的是「学生不能访问管理员页面」。用 Filter 统一拦截比在每个 Servlet 里判断优雅得多。WebFilter(/*) public class AuthFilter implements Filter { // 白名单登录页、登录接口、静态资源 private static final String[] WHITE {/login.jsp, /login, /css/, /js/}; public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; String uri request.getRequestURI().substring(request.getContextPath().length()); for (String w : WHITE) { if (uri.startsWith(w)) { chain.doFilter(req, resp); return; } } HttpSession session request.getSession(false); if (session null || session.getAttribute(user) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } // 管理员页面只允许role1 if (uri.startsWith(/admin/) !Integer.valueOf(1).equals(session.getAttribute(role))) { response.sendError(403, 无权限); return; } chain.doFilter(req, resp); } }逻辑说明getSession(false)表示不新建 Session避免未登录用户每访问一次就产生一个空 Session 拖垮内存。role存的是 Integer比较时别用超过 127 的缓存范围会翻车——这是血泪经验我见过有人把 role 设成 200 然后判断永远 false。3.2 床位分配事务和并发是重点分配床位要同时做三件事更新学生dorm_id、dorm.occupied1、插入checkin_log。这三步必须在一个事务里否则中途失败会出现「学生显示已分配但房间人数没变」的脏数据。public boolean assignBed(int studentId, int dormId, byte bedNo, String operator) { Connection conn null; try { conn DBUtil.getConn(); conn.setAutoCommit(false); // 开启事务 // 1. 乐观锁只有床位没满才允许分配 String sql1 UPDATE dorm SET occupied occupied 1 WHERE id ? AND occupied capacity; PreparedStatement ps1 conn.prepareStatement(sql1); ps1.setInt(1, dormId); if (ps1.executeUpdate() 0) { conn.rollback(); return false; // 床位已满回滚 } // 2. 更新学生宿舍信息 String sql2 UPDATE student SET dorm_id ?, bed_no ? WHERE id ? AND dorm_id IS NULL; PreparedStatement ps2 conn.prepareStatement(sql2); ps2.setInt(1, dormId); ps2.setByte(2, bedNo); ps2.setInt(3, studentId); if (ps2.executeUpdate() 0) { conn.rollback(); return false; // 学生已被分配防止重复提交 } // 3. 写入住日志 String sql3 INSERT INTO checkin_log(student_id, dorm_id, bed_no, in_time, operator) VALUES(?,?,?,NOW(),?); PreparedStatement ps3 conn.prepareStatement(sql3); ps3.setInt(1, studentId); ps3.setInt(2, dormId); ps3.setByte(3, bedNo); ps3.setString(4, operator); ps3.executeUpdate(); conn.commit(); return true; } catch (SQLException e) { try { if (conn ! null) conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); return false; } finally { try { if (conn ! null) { conn.setAutoCommit(true); conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } }参数说明occupied capacity这个条件写在 UPDATE 的 WHERE 里是典型的「用数据库行锁保证并发安全」比先 SELECT 再判断再 UPDATE 靠谱得多——两个宿管同时点分配只有一个能成功。dorm_id IS NULL同理防止学生被分配两次。bed_no用byte是因为床位号最多到 8省内存。3.3 JSP 列表页分页和中文乱码列表页最容易出的两个问题数据多了卡死、中文变问号。分页用LIMIT就够了别在 Java 里subList。% page contentTypetext/html;charsetUTF-8 pageEncodingUTF-8 % % page importjava.util.*, com.dorm.entity.Student % % // 分页参数默认第1页每页10条 int pageNo Integer.parseInt(request.getParameter(pageNo) null ? 1 : request.getParameter(pageNo)); int pageSize 10; ListStudent list (ListStudent) request.getAttribute(studentList); int total (Integer) request.getAttribute(total); int totalPage (total pageSize - 1) / pageSize; % table border1 trth学号/thth姓名/thth宿舍/thth床位/th/tr % for (Student s : list) { % tr td% s.getSno() %/td td% s.getName() %/td td% s.getDormName() null ? 未分配 : s.getDormName() %/td td% s.getBedNo() null ? - : s.getBedNo() %/td /tr % } % /table div 第 % pageNo % / % totalPage % 页 % if (pageNo 1) { %a href?pageNo% pageNo-1 %上一页/a% } % % if (pageNo totalPage) { %a href?pageNo% pageNo1 %下一页/a% } % /div逻辑说明pageEncodingUTF-8和contentType里的 charset 都要写只写一个在某些 Tomcat 版本下仍会乱码。分页 SQL 在 DAO 里拼LIMIT ?, ?注意 MySQL 的 LIMIT 是从 0 开始所以偏移量是(pageNo-1)*pageSize。热词里有人问「jsp 页面让加载完后刷新一次」如果是想定时刷新床位状态在head里加meta http-equivrefresh content30即可但别用在表单页否则用户填一半就被刷掉。4. 部署与联调避坑那些让项目跑不起来的细节4.1 环境版本对不上JDK、Tomcat、MySQL 三件套现象项目在自己电脑跑得好好的换台机器就 404 或 500。原因八成是版本组合不对。我踩过的坑JDK 17 配 Tomcat 8 会报Unsupported class file major version因为 Tomcat 8 只认到 Java 8 编译的字节码。解决方式是统一到 JDK 8 Tomcat 8.5 MySQL 5.7或者 JDK 11 Tomcat 9 MySQL 8。别用 JDK 17 配老 Tomcatjavax.servlet包名在 Jakarta EE 9 之后改成了jakarta.servlet一升级全项目 import 报红。4.2 数据库连接报错从 SSL 警告到驱动不匹配现象控制台刷Establishing SSL connection without servers identity verification is not recommended或者直接Communications link failure。原因是 MySQL 8 默认要求 SSL而老驱动没配。解决连接串加useSSLfalseallowPublicKeyRetrievaltrue同时确认mysql-connector-java版本和 MySQL 大版本匹配5.7 用 5.1.x8.0 用 8.0.x。如果报Unknown system variable query_cache_size说明驱动太老连了 MySQL 8换驱动。4.3 JSP 中文乱码三个地方都要设现象表单提交后数据库里是????。原因JSP 页面编码、request 解码、数据库连接编码三处任一没设都会乱。解决JSP 顶部pageEncodingUTF-8Servlet 里request.setCharacterEncoding(UTF-8)必须在第一次getParameter之前调用连接串加characterEncodingutf8。GET 请求还要改 Tomcat 的server.xml在 Connector 上加URIEncodingUTF-8。4.4 连接池耗尽别忘了关连接现象跑一段时间后报wait millis 60000, active 20。原因DAO 里Connection没在 finally 关闭或者 ResultSet、PreparedStatement 没关。解决用 try-with-resources 语法或者严格在 finally 里按 ResultSet→Statement→Connection 顺序关。Druid 的removeAbandonedtrue可以兜底但那是后悔药别当常规手段。4.5 部署到 Linux 后图片和 CSS 404现象本地 Windows 正常传到 Linux 服务器后样式全丢。原因是路径大小写敏感/CSS/style.css和/css/style.css在 Linux 是两个路径。解决统一用小写目录名JSP 里引用资源用%request.getContextPath()%/css/style.css别写死绝对路径。5. 进阶技巧把课程设计做成能写进简历的项目如果你想让这个系统在面试时拿得出手光有增删改查不够。我一般会加三个东西。第一把密码从 MD5 换成 BCryptjBCrypt库一行代码搞定面试官问「MD5 为什么不能用」你能答上「彩虹表」就赢了。第二给床位分配加一个简单的 Redis 锁或者数据库唯一索引兜底讲清楚「为什么乐观锁比 synchronized 更适合分布式场景」。第三把checkin_log做成可导出的报表用 POI 生成 Excel热词里「web 页面 pdf 打印」也可以用window.print()配合打印样式表实现成本极低但演示效果好。验证方法上我习惯用 JMeter 开 50 个线程同时调分配接口看dorm.occupied会不会超过capacity——如果超了说明事务或锁有问题。这个测试做完你对「并发」的理解就不再是背八股了。最后说个习惯每次改完 DAO我一定先写一个main方法直接调不启动 Tomcat。这样排查 SQL 问题快得多不用在浏览器和日志之间来回切。这套系统技术不新但把事务、并发、编码、部署这些基本功走一遍比追新框架收获大。希望帮到你。本文还有配套的精品资源点击获取
返回列表