ARTICLE DETAIL

资讯详情

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

Java学生宿舍管理系统:JSP+MySQL实现三角色权限与完整业务闭环

Java学生宿舍管理系统:JSP+MySQL实现三角色权限与完整业务闭环 简介基于Java的学生宿舍管理系统设计与实现文档以高校宿舍管理为背景面向计算机专业毕业生或Web开发学习者。系统采用B/S架构配合JSP和MySQL数据库实现用户登录、学生注册、宿舍管理、组合查询等核心功能可有效解决传统人工管理效率低、易出错的问题适用于高校宿舍管理的信息化建设。资源为单个docx文件约1.44MB全文按毕业设计规范组织涵盖引言、关键技术介绍、系统需求分析、业务流程分析、功能模块设计、数据库设计、系统实现与功能测试等章节并附有系统管理员操作流程、学生注册登录流程及数据表逻辑结构等详细内容。文档还对Java、B/S、JSP、MySQL、J2EE、TOMCAT等技术要点做了阐述便于读者在开发时快速查阅。已有204人学习/下载适合作为毕业设计选题参考、系统开发文档模板及答辩准备材料。1. 先看这套Java学生宿舍管理系统它解决的是宿舍管理里最琐碎的那层很多找Java学生宿舍管理系统源码的人都是被一套又一套“能跑就行”的Demo骗过之后才意识到真正缺的不是代码而是一条完整、能讲得清的业务链路。这套系统对应的场景很具体25栋宿舍楼、每栋容纳600到700名学生宿舍管理员要同时处理住宿登记、报修审核和来访登记单靠Excel或纸质表格根本扛不住。它基于B/S结构用JSPMySQL实现核心是三类角色登录后各管一摊系统管理员管人宿舍管理员管楼学生管自己的信息和报修。适合两类人正在做Java Web毕设、需要完整闭环代码的学生以及想快速搭一套宿舍管理后台做二次开发的工程师。2. B/S架构和三角色权限为什么JSPMySQL还够用2.1 为什么选B/S和JSPMySQL便宜、够用、好演示早期同类系统很多基于C/S架构客户端要装专门软件对机器配置和操作人员都有要求。宿舍管理员群体里不少人连Excel都用不利索让他们装客户端、配连接串基本等于劝退。换成B/S之后唯一要求就是浏览器能打开登录页剩下的逻辑全在服务端这层优势在真实宿舍管理场景里是决定性的。这套系统选JSPMySQL不是因为它技术新而是因为它便宜、够用、好演示。JSP本身是Servlet的封装做页面渲染很方便MySQL开源免费5.5版本在当时的机器上跑得动Tomcat解压即用整个环境搭建半小时内能完成。和现在主流的Spring Boot相比这一套确实老但结构简单到可以让评审老师一眼看懂请求怎么走、数据怎么存毕设答辩时这是绝对加分项。开发环境里原文明确写的是Windows 7旗舰版、MySQL 5.5.62、Eclipse 4.5 64位。我复现时没有盲目升级版本MySQL 5.5配对应版本的Connector/JJDK用1.7或1.8这是最稳的组合。如果你手头是JDK 11以上反而要小心驱动和Tomcat版本的兼容问题后面避坑章节会细讲。2.2 三类角色的用例边界谁动数据、谁看数据系统总用例图把权限分得很清楚这也是整个设计最值得抄的地方。系统管理员拥有全部权限可以增删改学生、宿舍管理员、宿舍楼和院系信息宿舍管理员权限收缩到一栋楼内能做缺勤记录、报修审核、来访者登记还能查楼内学生信息学生的权限最小只能看和改自己的基本信息查考勤记录、发报修申请。这个边界的价值在于它把“管理员”这个模糊概念拆成了两个实际岗位。系统管理员对应学生处或后勤中心宿舍管理员对应楼栋值班员两者根本不可能是同一个人。权限不混代码里的角色判断逻辑才不混。我在实际开发时会为每个角色建独立的功能菜单页登录时根据角色类型跳转不同首页而不是一个页面里堆满所有按钮。2.3 登录流程里的角色分流判断登录流程看起来简单但角色分流决定了系统整体骨架。流程是这样的学生在登录页输入账号密码如果没注册过先进注册页提交姓名、性别、学院、班级这些基本信息再回登录页登录系统管理员和宿舍管理员的账号由初始化SQL直接写入数据库不需要注册入口。后台收到登录请求后第一步不是验证密码而是先判断这次登录属于哪个角色。常见做法是查询时带上角色来源字段或者用不同的Servlet入口分流。我最常用的是在登录页放一个下拉框用户选择“学生/宿舍管理员/系统管理员”表单提交后按类型查对应的表谁的表里有记录谁就能进。代码实现上查询SQL分别是t_student、t_dormAdmin、t_admin三张表的等价语句差异只发生在DAO层的表名和返回实体上。3. 数据库设计五张表与关键字段背后的取舍3.1 从E-R图到五张表核心字段与主外键设计数据库是这套系统里信息量最大的部分E-R图设计了学生、宿舍管理员、系统管理员、宿舍楼、来访者五个实体。落到物理表上就是五张表t_admin、t_student、t_dormAdmin、t_dormBuild、t_visitor。所有表的主键都设计成自增整型不搞联合主键不搞业务主键这是毕设项目最不容易翻车的做法。t_student表字段最多包含学生编号、学号、密码、姓名、宿舍楼编号、宿舍楼名称、性别、电话、班级、院系。注意这里同时出现了宿舍楼编号和宿舍楼名称这个设计看起来重复实际是故意冗余。学生信息页面要显示“X号楼”如果每次都要联查t_dormBuild表性能上不划算页面代码也啰嗦。用空间换查询效率在数据量几千条的场景里完全合理。外键关系上t_student.dormBuildId指向t_dormBuild.dormBuildIdt_dormAdmin.dormBuildId也指向同一列这是整个系统里仅有的两处关联。很多人纠结要不要在物理层建FOREIGN KEY约束我一般建议不建。业务逻辑层保证一致性物理表保持独立这样导入数据、批量修改时能少踩很多坑。3.2 建表SQL按这份结构一次跑通拿到源码后第一件事是建库建表。我就按原设计给出可直接执行的版本字符集统一用utf8避免后面中文乱码。CREATE DATABASE IF NOT EXISTS dorm_system DEFAULT CHARACTER SET utf8; USE dorm_system; CREATE TABLE t_admin ( t_adminId INT(10) NOT NULL AUTO_INCREMENT, t_userName VARCHAR(20) NOT NULL, t_password VARCHAR(20) NOT NULL, t_name VARCHAR(20) DEFAULT NULL, t_sex VARCHAR(2) DEFAULT NULL, t_tel VARCHAR(11) DEFAULT NULL, PRIMARY KEY (t_adminId) ) ENGINEInnoDB DEFAULT CHARSETutf8; CREATE TABLE t_dormBuild ( t_dormId INT(10) NOT NULL AUTO_INCREMENT, t_dormBuildId INT(20) NOT NULL, t_dormName VARCHAR(20) NOT NULL, t_dormType VARCHAR(10) DEFAULT NULL, t_dormNumber INT(10) DEFAULT NULL, PRIMARY KEY (t_dormId) ) ENGINEInnoDB DEFAULT CHARSETutf8; CREATE TABLE t_student ( t_studentId INT(20) NOT NULL AUTO_INCREMENT, t_stuNum VARCHAR(20) NOT NULL, t_password VARCHAR(20) NOT NULL, t_name VARCHAR(20) DEFAULT NULL, t_dormBuildId INT(20) DEFAULT NULL, t_dormName VARCHAR(20) DEFAULT NULL, t_sex VARCHAR(2) DEFAULT NULL, t_tel VARCHAR(11) DEFAULT NULL, t_stuClass VARCHAR(20) DEFAULT NULL, t_stuacademy VARCHAR(20) DEFAULT NULL, PRIMARY KEY (t_studentId) ) ENGINEInnoDB DEFAULT CHARSETutf8; CREATE TABLE t_dormAdmin ( t_dormManId INT(10) NOT NULL AUTO_INCREMENT, t_userName VARCHAR(20) NOT NULL, t_password VARCHAR(20) NOT NULL, t_dormBuildId INT(20) DEFAULT NULL, t_name VARCHAR(20) DEFAULT NULL, t_sex VARCHAR(2) DEFAULT NULL, t_tel VARCHAR(11) DEFAULT NULL, PRIMARY KEY (t_dormManId) ) ENGINEInnoDB DEFAULT CHARSETutf8; CREATE TABLE t_visitor ( t_visitorId INT(18) NOT NULL, t_visitorRecord INT(20) DEFAULT NULL, t_visitorName VARCHAR(20) DEFAULT NULL, t_sex VARCHAR(2) DEFAULT NULL, t_time DATETIME DEFAULT NULL, t_tel VARCHAR(11) DEFAULT NULL, PRIMARY KEY (t_visitorId) ) ENGINEInnoDB DEFAULT CHARSETutf8;这段SQL直接把五张表全部建好。注意t_visitor表的主键用的是来访者身份证号字段本身这符合原文的设计但实际业务里如果同一个来访者多次登记这种主键会冲突。我的习惯是给它加一个自增主键把身份证号改成普通字段这在后面的避坑章节里会细说。3.3 设计取舍冗余字段是故意的别乱删t_student表里冗余了宿舍楼名称有人觉得是设计缺陷但在我看这才是写论文时最好展开的点。答辩老师问“你为什么要冗余”你可以回答“减少高频查询的联表次数”老师接着问“数据不一致怎么办”你可以说“宿舍楼信息变更频率极低通过宿舍管理员模块统一维护变更时联表更新学生表”。这个问题答好了比背十页概念都管用。字段类型方面所有密码字段用VARCHAR(20)这个长度不需要长因为系统用的是明文密码存储。真实生产环境里这是大忌但这套系统的定位是演示和教学密码加密属于进阶改造我会在第6章提一版方案。电话字段用VARCHAR(11)而不是BIGINT是防止手机号首位的0被数字类型吞掉这个细节如果在答辩时主动讲出来老师会认为你真有开发经验。4. 登录与组合查询的实现链路从JSP到Servlet再到DAO4.1 登录鉴权一条完整的Servlet调用链登录是整个系统里技术含量最高的一段原文对这段的描述比较详细我按最典型的JSPServlet写法还原。前端JSP页面里有一个表单action指向登录Servletmethod用post。为什么不用get因为get方式会把账号密码拼在URL后面的查询字符串里浏览器历史记录、代理日志全都能看到这在任何系统里都是硬伤。登录Servlet的核心逻辑大概是这样的protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String userName request.getParameter(userName); String password request.getParameter(password); String role request.getParameter(role); if (userName null || password null || .equals(userName) || .equals(password)) { response.sendRedirect(login.jsp?error1); return; } LoginDAO dao new LoginDAO(); Object user dao.login(userName, password, role); if (user null) { response.sendRedirect(login.jsp?error2); return; } HttpSession session request.getSession(); session.setAttribute(user, user); session.setAttribute(role, role); if (student.equals(role)) { response.sendRedirect(studentIndex.jsp); } else if (dormAdmin.equals(role)) { response.sendRedirect(dormAdminIndex.jsp); } else { response.sendRedirect(adminIndex.jsp); } }这段代码有几个地方值得注意。第一request.getParameter拿的是表单里name属性对应的值这是这次请求带过来的数据不是Session里的旧数据我见过有人把这两者搞混后面避坑会讲。第二判空逻辑在查询数据库之前完成空账号和空密码根本不会触及DAO层。第三登录成功后把整个user对象塞进Session后续页面要显示昵称、学号、宿舍楼号时直接从Session里取不用再查一次库。DAO层的login方法内部是按角色拼接SQL并查询public Object login(String userName, String password, String role) { Connection conn null; PreparedStatement ps null; ResultSet rs null; String sql null; try { conn DBUtil.getConnection(); if (student.equals(role)) { sql SELECT t_stuNum, t_name, t_dormBuildId, t_dormName FROM t_student WHERE t_stuNum? AND t_password?; } else if (dormAdmin.equals(role)) { sql SELECT t_dormManId, t_name, t_dormBuildId FROM t_dormAdmin WHERE t_userName? AND t_password?; } else { sql SELECT t_adminId, t_name FROM t_admin WHERE t_userName? AND t_password?; } ps conn.prepareStatement(sql); ps.setString(1, userName); ps.setString(2, password); rs ps.executeQuery(); if (rs.next()) { // 构造对应实体对象返回 } } catch (Exception e) { e.printStackTrace(); } finally { DBUtil.close(rs, ps, conn); } return null; }这里的SQL用PreparedStatement的占位符而不是字符串拼接主要原因是防SQL注入。账号密码里如果带单引号或or 11拼接SQL会被数据库当成语法执行用占位符之后参数会被当作纯字面量处理。这套系统虽然定位是毕设但只要是Java Web项目这个习惯必须养成答辩老师也爱问这个点。4.2 动态组合查询用StringBuilder拼接条件系统功能里有“组合查询信息”这个需求实际场景是系统管理员想查某栋楼、某个班级、或者某个性别的学生三个条件可以任选组合也可以全不选。这种查询用固定SQL没法做必须动态拼接WHERE子句。常见做法是用StringBuilder逐步追加条件核心逻辑如下public ListStudent searchStudents(Integer dormBuildId, String stuClass, String sex) { StringBuilder sql new StringBuilder(SELECT * FROM t_student WHERE 11 ); if (dormBuildId ! null dormBuildId 0) { sql.append( AND t_dormBuildId ).append(dormBuildId); } if (stuClass ! null !.equals(stuClass.trim())) { sql.append( AND t_stuClass ).append(stuClass).append(); } if (sex ! null !.equals(sex.trim())) { sql.append( AND t_sex ).append(sex).append(); } sql.append( ORDER BY t_studentId); // 执行查询并返回结果 }这里有一个非常关键的小技巧就是在WHERE后面固定写11。这样后续不管追加几个条件都只需要写AND xxx不用判断当前是不是第一个条件来决定写WHERE还是写AND。字符串拼接方式是示例代码最常用的写法它的前提条件是查询条件里没有用户直接输入的通配符和引号如果条件来自文本框且没有做校验我还是会坚持用PreparedStatement让每个条件用?占位再把值通过setString传进去。参数上要注意宿舍楼编号是整型可以用判断有没有选择班级和性别是字符串必须判空字符串否则用户清空输入框后传一个空串过来SQL照样拼接查询结果就会变空。这个“参数类型决定判空方式”的区别写错了就是活生生的翻车现场。4.3 三个角色模块权限差异与页面跳转三个角色登录后进入完全不同的功能页。系统管理员那边是一整套“管理后台”能维护学生信息、宿舍管理员信息、宿舍楼信息和来访者记录宿舍管理员那边是针对自己所在楼栋的日常运营功能包括缺勤登记、报修处理和来访登记学生端则简单得多个人信息页加一个“我的报修”申请入口。这里最值得说的经验是页面命名规范。我见过很多毕设项目的JSP文件名乱成一团什么a.jsp、test1.jsp都有系统一复杂根本分不清谁是谁。我复现这套系统时把文件分了三层目录admin/、dormAdmin/、student/不同角色的页面放各自目录下Servlet跳转时写全路径。这样后面改需求时我知道该动哪个文件不用满项目找。权限控制方面只靠页面跳转是不够的因为用户可以直接在浏览器地址栏输入admin/xxx.jsp来访问。更稳的做法是写一个Filter拦截所有JSP请求在每个Session里检查角色字段角色不匹配直接踢回登录页。原文没有提Filter我会在进阶章节里说怎么补这一层这属于低成本高回报的改动。5. 部署与避坑Tomcat、乱码、JDBC连接的常见问题5.1 端口被占用与Tomcat启动失败现象Eclipse里启动Tomcat控制台报Port 8080 required by Tomcat v8.0 Server at localhost is already in use。解决先查占用进程再改端口。Windows下用命令netstat -ano | findstr 8080看PID然后去任务管理器结束对应进程如果是系统服务占用直接改server.xml里的端口也行我一般把HTTP端口改成8081避免和本机其他Java进程撞车。netstat -ano | findstr 8080输出结果里最后一列是进程ID记住这个数字然后在任务管理器里结束它。这个命令在所有Windows版本通用属于排查端口类问题的基本功。5.2 中文乱码三个地方都要改成UTF-8现象登录后用管理员账号添加学生填的是“张三”数据库里存的是“???”页面上显示一堆问号。原因请求编码、数据库编码、JSP页面编码三者不一致。解决方案分三层。第一层JSP页面顶部加% page contentTypetext/html;charsetUTF-8 languagejava %第二层Servlet的doPost方法第一行加request.setCharacterEncoding(UTF-8)第三层MySQL连接串后面加参数useUnicodetruecharacterEncodingUTF-8。三层都到位之后中文不会再出问题。这里有一个隐藏点连接串里的符号在XML配置里要写成amp;如果直接写Tomcat解析XML时不会报错但连接参数会丢乱码依然存在。jdbc:mysql://localhost:3306/dorm_system?useUnicodetruecharacterEncodingUTF-8在db.properties里这一段写在等号后面在web.xml里如果用parameter-value包起来必须转义。这个细节容易忽略排查乱码时先查它。5.3 JDBC连不上MySQL驱动与URL是重点现象启动系统后登录页面报java.sql.SQLException: Communications link failure。原因多数情况是MySQL服务没启动或者驱动版本和MySQL版本不匹配。MySQL 5.5配Connector/J 5.1.x是稳定的组合换成Connector/J 8.x后URL里的驱动类名变了com.mysql.jdbc.Driver要改成com.mysql.cj.jdbc.Driver并且URL里要追加serverTimezoneAsia/Shanghai不然后面还会报时区错误。解决步骤很简单第一步确认MySQL服务在运行打开Windows服务管理器看MySQL55这类服务状态第二步确认db.properties里的URL、端口、账号密码都对得上第三步确认驱动JAR包已经放进WEB-INF/lib目录。这三步按顺序查完大部分连接问题都能定位。5.4 登录跳404Servlet路径没对上现象提交登录表单后浏览器地址栏路径变成/dorm/loginServlet页面却报404。原因表单的action属性值和Servlet的映射路径不一致两个地方只要差一个字母就找不到。解决思路打开Servlet类看注解WebServlet(/loginServlet)然后打开login.jsp看form标签里actionloginServlet这是两种情况。还有一种老式写法是配web.xml里的servlet-mapping如果同时用了注解和XML配置两处路径不一致Tomcat启动时会直接报告冲突。我在这个项目里会强调一个原则统一用注解方式不碰web.xml里的Servlet配置。注解写在哪跳转就参考哪这样路径问题能减少八成。5.5 SQL联表查不出数据连接条件写错了现象查询宿舍楼里的学生列表页面上表格是空的但明明数据库里有数据。原因联表查询的ON条件写错字段。这套系统里学生表的t_dormBuildId和宿舍楼表的t_dormBuildId是关联键但宿舍楼表的主键其实是t_dormId很多人会把这两个字段搞混写出ON t_student.t_dormBuildId t_dormBuild.t_dormId结果永远匹配不上。SELECT s.t_name, s.t_stuNum, b.t_dormName FROM t_student s LEFT JOIN t_dormBuild b ON s.t_dormBuildId b.t_dormBuildId正确的写法是两张表的t_dormBuildId做等值连接。这个坑之所以常见是因为t_dormId和t_dormBuildId长得太像从E-R图上又都是“宿舍”实体的属性。遇到查询结果为空先别怀疑数据把SQL放到Navicat里单独执行一遍看返回结果问题马上就能暴露。6. 验收方法与进阶改造别只会跑通登录6.1 先用一张测试清单过一遍系统能不能交不是看登录能不能进而是看每个角色该有的功能是不是都能闭环。我复现这套系统时会按下面的清单逐项验收每过一项打一个勾验收项操作路径预期结果学生注册登录页点注册填完整信息提交成功回登录页可登录学生登录学号密码登录进入学生首页显示姓名、宿舍楼、班级修改密码学生个人中心改密码旧密码验证通过新密码生效提交报修学生端填写报修内容宿舍管理员后台能看到新报修单宿舍管理员登录账号密码登录进入宿舍管理员首页缺勤登记宿舍管理员新增缺勤记录学生端能查看到该记录来访登记宿舍管理员登记来访者系统管理员端能看到来访记录系统管理员登录管理员账号登录进入后台能看到全部菜单组合查询按楼号班级查询学生只返回两个条件下匹配的学生修改学生信息系统管理员编辑学生宿舍楼学生端信息页同步更新这套清单跑完系统能不能交心里就有数了。注意组合查询那个验收项至少要测“只选楼号”“只选班级”“两个同时选”“全部不选”四种情况全部不选时应该返回全部学生这能区分出SQL拼接条件写没写对。6.2 三个低成本进阶方向系统跑通之后如果时间允许我推荐做三个改造。第一个是给密码加MD5加密登录时先加密再比对数据库里存的不再是明文这是生产系统的基本要求也特别适合写进论文的创新点里第二个是加一个Filter做登录状态校验解决直接输入URL跳过登录的问题第三个是把JDBC连接换成Druid或C3P0连接池页面响应速度会有明显提升这个改造不伤骨架DAO层的改动量很小。WebFilter(/*) public class LoginFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req (HttpServletRequest) request; HttpServletResponse resp (HttpServletResponse) response; String uri req.getRequestURI(); if (uri.endsWith(login.jsp) || uri.endsWith(loginServlet) || uri.contains(register)) { chain.doFilter(request, response); return; } Object user req.getSession().getAttribute(user); if (user null) { resp.sendRedirect(req.getContextPath() /login.jsp); return; } chain.doFilter(request, response); } }这段代码是三个改造里性价比最高的。它拦下所有请求登录页和注册页放行其余请求必须带着有效的Session用户才能访问否则一律踢回登录页。从那以后我每次做JSP项目都会把Filter放第一位先做权限拦截再做页面哪怕项目再小也不省这一步这个习惯帮我避开了无数次“网址直达后台”的尴尬。希望这篇拆解能帮你把这套Java学生宿舍管理系统真正跑起来跑出自己的版本。本文还有配套的精品资源点击获取
返回列表