ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java Servlet+JSP+MySQL宿舍管理系统实战:从建表到事务与避坑

Java Servlet+JSP+MySQL宿舍管理系统实战:从建表到事务与避坑 简介这是一套面向Java Web初学者的ServletJSPMySQL宿舍管理系统实战源码适合刚接触Web开发、需要完整小项目练手的学生与自学者。系统实现用户注册登录及宿舍信息的增删改查覆盖Servlet处理请求、JSP渲染页面、JDBC连接MySQL等核心环节帮助理解前后端交互与数据库操作的基本流程。压缩包共63个文件约10.01MB包含18个java源文件与18个class编译文件、5个jsp页面、3个jar依赖包以及sql建表脚本、配置说明docx、功能描述txt和演示mp4视频另附png界面截图便于对照运行效果。目前已有1160人学习下载。资源提供可直接导入的工程结构与数据库脚本配合视频与文档能快速跑通项目并从中学习session保持登录、MVC分层组织代码及异常处理等实用思路是入门Web开发、积累课程设计经验的参考案例。1. 从零手写一套宿舍管理系统为什么 ServletJSP 原生界面至今还值得练如果你正在搜「java 基于 servletjspmysql 简单宿舍管理系统 jsp 原生界面开发」大概率不是想做一个多惊艳的产品而是想找一个能跑通、能改、能写进简历或课程设计的完整闭环。宿舍管理系统这个题目本身不新鲜但它的业务边界特别清晰学生、宿舍楼、房间、床位、入住退宿、管理员权限几张表就能把关系型数据库的增删改查、外键约束、事务处理全练一遍。用 Servlet 做控制器、JSP 做视图、MySQL 做持久化这套组合虽然老却是理解 Java Web 请求生命周期最直白的路径——没有 Spring 的自动装配帮你藏细节request、response、session、forward、redirect 全得自己写。适合谁适合刚学完 Java 基础、想搞懂「浏览器输入网址到页面出来中间发生了什么」的人也适合被框架惯坏、想回头补底层的人。下面我按自己带人做课程设计的实际路径把选型、建表、编码、排错、进阶一次讲透。2. 环境与选型ServletJSPMySQL 这套组合到底怎么搭才不翻车2.1 为什么不用 Spring Boot偏要原生 Servlet很多人第一反应是「都 2025 年了还写 Servlet」。但你要练的是 Web 底层不是框架 API。原生 Servlet 的请求处理链路是显式的Tomcat 收到 HTTP 请求 → 根据 web.xml 或 WebServlet 映射到某个 Servlet → Servlet 调 Service → Service 调 DAO → DAO 用 JDBC 查 MySQL → 结果塞进 request 域 → forward 到 JSP → JSP 渲染 HTML 回浏览器。这条链路里每一环你都能打断点看到。用 Spring Boot 的话DispatcherServlet 帮你把映射、参数绑定、视图解析全做了出问题你只能看日志猜。所以我的建议是第一版一定用原生 Servlet 写透第二版再考虑用框架重构对比着看才知道框架到底帮你省了什么。选型上还有几个具体决定。JDK 用 8 或 11 都行Tomcat 用 8.5 或 9MySQL 用 5.7 或 8.0。注意 MySQL 8.0 的驱动类名是com.mysql.cj.jdbc.DriverURL 要带时区和 SSL 参数否则会报mysql ssl连接错误或者时区异常这是新手最常卡的地方。JSP 页面里不要写 Java 代码块% %写业务逻辑只用来做展示和循环业务全放 Servlet 和 Service否则后期改一个字段要翻十个页面。2.2 数据库设计五张表撑起整个宿舍管理宿舍管理系统的核心实体就五个管理员、学生、宿舍楼、房间、入住记录。表设计直接决定后面代码好不好写。我一般这样建-- 管理员表 CREATE TABLE admin ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 学生表 CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, sno VARCHAR(20) NOT NULL UNIQUE COMMENT 学号, name VARCHAR(50) NOT NULL, gender TINYINT DEFAULT 1 COMMENT 1男 2女, phone VARCHAR(20), status TINYINT DEFAULT 0 COMMENT 0未入住 1已入住 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 宿舍楼表 CREATE TABLE building ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, gender TINYINT DEFAULT 1 COMMENT 楼栋性别限制 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 房间表 CREATE TABLE room ( id INT PRIMARY KEY AUTO_INCREMENT, building_id INT NOT NULL, room_no VARCHAR(20) NOT NULL, capacity INT DEFAULT 4 COMMENT 床位容量, occupied INT DEFAULT 0 COMMENT 已住人数, 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_time DATETIME DEFAULT CURRENT_TIMESTAMP, checkout_time DATETIME DEFAULT NULL, FOREIGN KEY (student_id) REFERENCES student(id), FOREIGN KEY (room_id) REFERENCES room(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;逻辑说明student.status和room.occupied是冗余字段目的是避免每次查「房间还有没有空位」都去 count 入住记录这是典型的读多写少场景下的空间换时间。checkin表用checkout_time是否为 NULL 表示当前是否在住退宿时更新这个字段而不是删记录保留历史。参数上capacity默认 4 是常见本科宿舍标准改成 6 或 8 直接改默认值即可。字符集统一utf8mb4否则学生姓名里的生僻字会变问号。注意外键约束在批量导入测试数据时容易报错可以先SET FOREIGN_KEY_CHECKS0导完再打开。生产环境是否用外键有争议但课程设计阶段建议保留能帮你发现脏数据。2.3 项目骨架与 web.xml 最小配置目录结构按 Maven 标准来src/main/java放 Servlet、Service、DAO、实体类src/main/webapp放 JSP、CSS、JSWEB-INF/lib放 mysql-connector-java 和 jstl 的 jar。如果用 Mavenpom 里加 servlet-apiscope 用 provided、jsp-api、jstl、mysql-connector-java 四个依赖就够。web.xml 里其实只需要配欢迎页和编码过滤器Servlet 用注解更清爽web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee version3.1 welcome-file-list welcome-filelogin.jsp/welcome-file /welcome-file-list filter filter-nameencodingFilter/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class /filter /web-app不过上面这个过滤器类来自 Spring原生项目里自己写一个更合适WebFilter(/*) public class EncodingFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(UTF-8); resp.setContentType(text/html;charsetUTF-8); chain.doFilter(req, resp); } }逻辑说明这个过滤器拦截所有请求统一把请求体和响应编码设成 UTF-8解决中文乱码。参数上/*表示全部路径如果只想拦 JSP 可以改成*.jsp但建议全拦因为 POST 表单提交到 Servlet 也会乱码。注意WebFilter需要 web.xml 的metadata-completefalse或者不写 version 冲突的配置Tomcat 8 以上默认支持注解扫描。3. 核心功能落地登录、宿舍分配、退宿的 Servlet 与 JSP 怎么写3.1 登录鉴权Session 存什么、JSP 怎么防直接访问登录是最基础也最容易写出安全漏洞的地方。流程是login.jsp 提交表单到 LoginServletServlet 查 admin 表比对用户名密码成功则session.setAttribute(admin, admin)并重定向到列表页失败则转发回登录页带错误信息。WebServlet(/login) public class LoginServlet extends HttpServlet { private AdminDao adminDao new AdminDao(); protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username req.getParameter(username); String password req.getParameter(password); Admin admin adminDao.findByUsername(username); if (admin ! null admin.getPassword().equals(password)) { req.getSession().setAttribute(admin, admin); resp.sendRedirect(req.getContextPath() /student/list); } else { req.setAttribute(msg, 用户名或密码错误); req.getRequestDispatcher(/login.jsp).forward(req, resp); } } }逻辑说明sendRedirect和forward的区别这里必须搞清楚——登录成功用重定向防止用户刷新页面重复提交表单失败用转发因为要把错误信息带回 JSP 显示。参数上req.getContextPath()拿到项目上下文路径避免部署路径变化导致跳转 404。密码这里直接明文比对课程设计够用但真实项目必须加盐哈希这是底线。JSP 端防直接访问的做法是在每个需要登录的页面顶部加判断% if (session.getAttribute(admin) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } %更优雅的做法是写一个 LoginFilter拦截/student/*、/room/*等路径统一校验 session。这样 JSP 里就不用重复写判断也避免漏写。3.2 宿舍分配一次入住要同时改三张表事务不能省宿舍分配是整个系统里唯一需要事务的操作往 checkin 插一条记录、把 student.status 改成 1、把 room.occupied 加 1。三步必须全成功或全回滚否则会出现「学生显示已入住但房间没占位」的脏数据。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 insSql INSERT INTO checkin(student_id, room_id) VALUES(?,?); PreparedStatement ps2 conn.prepareStatement(insSql); ps2.setInt(1, studentId); ps2.setInt(2, roomId); ps2.executeUpdate(); // 3. 更新学生状态和房间占用数 conn.prepareStatement(UPDATE student SET status1 WHERE id studentId).executeUpdate(); conn.prepareStatement(UPDATE room SET occupiedoccupied1 WHERE id roomId).executeUpdate(); conn.commit(); return true; } catch (Exception e) { try { if (conn ! null) conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); return false; } finally { DBUtil.close(conn); } }逻辑说明FOR UPDATE是行级锁防止两个管理员同时给同一个房间的最后一张床位分配学生导致超员。setAutoCommit(false)开启事务任何一步异常都 rollback。参数上occupied1用数据库自增而不是先查再写避免并发覆盖。注意这里为了演示简洁用了字符串拼接实际应该全部用 PreparedStatement 防 SQL 注入第 4 章会专门讲这个坑。3.3 JSP 原生界面用 JSTL 循环渲染房间列表JSP 里最忌讳写% for(...) { %这种脚本片段可读性差还容易出错。用 JSTL 的c:forEach干净得多% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % table border1 trth房间号/thth楼栋/thth容量/thth已住/thth操作/th/tr c:forEach items${roomList} varroom tr td${room.roomNo}/td td${room.buildingName}/td td${room.capacity}/td td${room.occupied}/td td c:if test${room.occupied room.capacity} a hrefcheckin?roomId${room.id}分配/a /c:if c:if test${room.occupied room.capacity} span stylecolor:red已满/span /c:if /td /tr /c:forEach /table逻辑说明${roomList}是 Servlet 里req.setAttribute(roomList, list)传过来的。c:if根据占用情况动态显示「分配」链接还是「已满」。参数上room.occupied room.capacity这个判断在 JSP 和数据库里各做一次JSP 是为了界面友好数据库是为了并发安全两者不冲突。注意 JSTL 的 jar 包要放对位置否则会报The absolute uri: http://java.sun.com/jsp/jstl/core cannot be resolved这是新手高频错误。4. 避坑与排查这套老技术栈最容易翻车的五个地方4.1 中文乱码POST 和 GET 要分开治现象表单提交后数据库里存的是????或者页面显示问号。原因Tomcat 默认用 ISO-8859-1 解码请求体JSP 页面默认编码也可能不是 UTF-8。解决POST 乱码用req.setCharacterEncoding(UTF-8)在读取参数前设置GET 乱码URL 传参在 Tomcat 8 以上默认已经是 UTF-8如果还乱检查 server.xml 的 Connector 有没有URIEncodingUTF-8。数据库连接 URL 也要加useUnicodetruecharacterEncodingutf8。三处都对了才不会乱。4.2 MySQL 8.0 连接报 SSL 和时区错误现象启动就报javax.net.ssl.SSLHandshakeException或者The server time zone value ?D1ú±ê×?ê±?? is unrecognized。原因MySQL 8.0 默认要求 SSL 连接且时区参数没配。解决JDBC URL 写成jdbc:mysql://localhost:3306/dorm?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue。useSSLfalse关掉 SSL本地开发够用serverTimezone指定时区allowPublicKeyRetrievaltrue解决 caching_sha2_password 认证问题。驱动版本用 8.0.x 对应 MySQL 8.0用 5.1.x 的驱动连 8.0 会报各种奇怪错误。4.3 JSP 页面加载完后自动刷新一次现象有人搜「jsp页面让加载完后刷新一次」通常是想在提交后刷新列表。原因直接response.sendRedirect到列表页时如果浏览器缓存了旧页面可能看到旧数据。解决在 JSP 的 head 里加meta http-equivrefresh content0;urllist可以强制刷新但更好的做法是 Servlet 里设置响应头resp.setHeader(Cache-Control, no-cache, no-store, must-revalidate)让浏览器不缓存动态页面。用 meta refresh 要注意 content 的秒数设 0 是立即跳转设 5 是 5 秒后跳别写错。4.4 SQL 注入字符串拼接查询是血泪教训现象登录框输入 or 11就能绕过密码。原因用SELECT * FROM admin WHERE username username 拼接 SQL。解决全部改用 PreparedStatement 占位符。上面 3.2 的代码里我为了演示事务用了拼接实际项目里UPDATE student SET status1 WHERE id?必须用ps.setInt(1, studentId)。这个坑不踩一次不长记性但踩了可能整个系统数据被删所以宁可一开始就写规范。4.5 连接池没关导致 Tomcat 启动越来越慢现象开发时反复重启 Tomcat跑几十次后报Too many connections。原因每次DBUtil.getConnection()都新建连接用完没 closeMySQL 连接数被占满。解决finally 块里必须 close或者用 Druid、HikariCP 连接池。课程设计阶段至少保证Connection、PreparedStatement、ResultSet三个都在 finally 里关。我一般写个DBUtil.close(conn, ps, rs)静态方法统一处理避免漏关。5. 进阶技巧把原生 JSP 项目改造成能写进简历的样子5.1 用过滤器做统一登录校验和权限控制前面提过 LoginFilter这里给个完整实现。它拦截所有/admin/*路径检查 session 里有没有 admin 对象没有就踢回登录页。这样你新增任何管理页面都不用重复写校验代码。WebFilter(/admin/*) public class LoginFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; Object admin request.getSession().getAttribute(admin); if (admin null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); } }逻辑说明WebFilter(/admin/*)只拦管理路径登录页和静态资源不受影响。参数上request.getContextPath()保证部署到不同 context 都能正确跳转。这个过滤器加上后你的项目就有了「权限控制」这个简历关键词比单纯增删改查高一个档次。5.2 分页查询别一次性把几千条学生数据全查出来学生表数据一多SELECT * FROM student直接卡死页面。分页是必须的。MySQL 用LIMIT offset, sizeSELECT * FROM student ORDER BY id DESC LIMIT ?, ?Servlet 里接收page参数算出 offset (page-1)*sizesize 固定 10 或 20。同时要查一次SELECT COUNT(*) FROM student算总页数传给 JSP 渲染页码。参数上 size 别设太大10 到 20 之间体验最好。这个改造做完你的系统就从「玩具」变成「能演示」了。5.3 验证方法怎么确认你的系统真的没大问题写完别急着交按这个清单过一遍第一用两个浏览器分别登录两个管理员账号同时给同一个房间的最后一个床位分配学生看会不会超员验证事务和行锁。第二在登录框输入 or 11看能不能绕过验证 SQL 注入。第三把学生姓名改成生僻字如「龘」看数据库和页面显示是否正常验证字符集。第四连续刷新列表页 50 次看 Tomcat 日志有没有连接泄漏警告验证连接关闭。第五退宿一个学生后检查 student.status、room.occupied、checkin.checkout_time 三处是否同步更新验证业务一致性。这五条过了这套宿舍管理系统就站得住。我自己的习惯是每加一个功能就先想「这个操作如果失败一半会怎样」然后决定要不要加事务。原生 ServletJSP 这套东西看着老但把请求链路、事务边界、编码问题、注入防护这些基本功练扎实了后面学任何框架都是降维打击。希望帮到你。本文还有配套的精品资源点击获取
返回列表