ARTICLE DETAIL

资讯详情

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

Servlet+JSP+MySQL宿舍管理系统:从环境搭建到避坑实战

Servlet+JSP+MySQL宿舍管理系统:从环境搭建到避坑实战 简介这是一套面向计算机专业学生与JavaWeb初学者、课程设计或毕业设计开发者的学生宿舍管理系统完整源码采用Servletjspmysql技术栈实现适合用来学习传统MVC分层开发、数据库交互与后台管理模块设计。压缩包共295个文件约16.88MB其中30个java源文件与30个class文件对应登录、宿舍、楼宇、管理员、学生等核心业务逻辑13个jsp页面承载前端展示40个css与10个js负责界面样式与交互25个jar为项目依赖库另含sql脚本、db数据文件及png、gif等图片素材结构完整可直接导入运行。目前已有5921人学习下载说明该案例在同类课程设计中具有较高参考价值。读者可从中获得一套可直接运行的宿舍管理项目源码理解Servlet请求分发、BaseDao封装、验证码工具类等实现思路并借助现成数据库脚本快速搭建环境对照源码梳理功能模块与排错方法为二次开发或答辩准备提供便利。1. 从一份只有 class 文件的宿舍管理系统说起Servletjspmysql 到底能跑出什么打开资源包第一眼看到的不是源码目录而是一串编译产物BaseDao.class、LiveServlet.class、DormitoryServlet.class、BuildingServlet.class、DormitoryManagerServlet.class、StudentServlet.class、CpachaUtil.class、AdminServlet.class、LoginServlet.class。做过传统 javaWeb 项目的人看到这串名字脑子里基本能还原出整个系统的骨架——登录鉴权、验证码生成、宿舍楼管理、宿舍房间管理、学生信息管理、宿管人员管理外加一个抽出来的 BaseDao 做数据库公共操作。这套基于 Servletjspmysql 开发的学生宿舍管理系统走的是最经典的 Model1/Model2 混合路线Servlet 接请求、调 DAO、转发或重定向到 jsp 渲染mysql 存数据。它适合两类人一类是要交毕设、做课程设计、需要一套能跑通、结构清晰、方便改的 javaWeb 完整案例的学生另一类是刚学完 servlet 生命周期、jsp 入门想找一个真实项目把 request、response、session、JDBC 串起来练手的开发者。下面不聊虚的直接拆这套东西怎么落地、参数怎么配、坑在哪。2. 环境搭建与项目导入从 mysql 安装到 idea 运行 javaweb 项目配置2.1 先把 mysql 装明白别让 error 2002 拦住你这套系统依赖 mysql 存宿舍、学生、宿管、管理员四类核心数据数据库跑不起来后面 Servlet 连不上页面全是 500。Windows 下常见做法是去 mysql 下载官网拿 mysql installer选 Server only 或 Custom装完把 bin 目录加进 PATH。装完先验证服务# 查看 mysql 服务状态Windows net start | findstr mysql # 登录验证-u 用户名 -p 表示密码交互输入 mysql -u root -p # 登录后看版本和默认字符集 SELECT VERSION(); SHOW VARIABLES LIKE character_set%;逻辑说明net start确认服务已启动避免后面报error 2002 (hy000): cant connect to local mysql server through socket。登录后查字符集是关键宿舍管理系统里学生姓名、楼栋名、备注都可能是中文字符集不对就是一堆问号。参数上mysql 8 默认utf8mb4如果建库时用了latin1后面 jsp 页面中文全乱。建库语句建议显式指定CREATE DATABASE dorm_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE dorm_db;utf8mb4比utf8多支持 emoji 和部分生僻字虽然宿舍系统用不上 emoji但统一用utf8mb4能少一类玄学乱码。建完库再导入资源里的 sql 文件导入前先SET NAMES utf8mb4;否则 source 进去的中文可能已经坏了。2.2 idea 新建 jsp 项目与 Servlet 环境配置热词里idea新建jsp项目、vscode编写servlet代码需要怎么配置环境问的人多这里说清楚这套系统是传统 war 结构不是 Spring Boot所以别用 Spring Initializr。idea 里走 File → New → Project → Java Enterprise勾选 Web ApplicationApplication Server 选 Tomcat勾上 Create web.xml。项目结构应该是dorm-system/ ├── src/ │ └── com/dorm/ │ ├── dao/BaseDao.java │ ├── servlet/LoginServlet.java │ ├── servlet/StudentServlet.java │ ├── servlet/DormitoryServlet.java │ ├── servlet/BuildingServlet.java │ ├── servlet/DormitoryManagerServlet.java │ ├── servlet/AdminServlet.java │ ├── servlet/LiveServlet.java │ └── util/CpachaUtil.java ├── web/ │ ├── WEB-INF/ │ │ ├── web.xml │ │ └── lib/ │ ├── jsp/ │ └── index.jspWEB-INF/lib下必须放mysql-connector-java的 jar版本要和 mysql 服务端匹配mysql 5.7 用 5.1.x 连接器mysql 8 用 8.0.x 连接器。放错版本最典型的现象是启动时报No suitable driver found或者连接时抛Unknown system variable query_cache_size。Servlet 的编译依赖由 Tomcat 提供不需要额外引 servlet-api.jar但如果你用 vscode 写就得手动把 servlet-api.jar 加进 classpath否则HttpServlet根本解析不了。2.3 Tomcat 部署与 war 打包传统 jsp 项目打包 war 是常规操作。idea 里 Artifacts 配置 Web Application: Archive输出dorm-system.war丢进 Tomcat 的 webapps 目录启动后访问http://localhost:8080/dorm-system/。这里有个参数容易翻车Tomcat 10 开始把javax.servlet换成了jakarta.servlet而这套系统用的是javax包部署到 Tomcat 10 会直接报ClassNotFoundException: javax.servlet.http.HttpServlet。解决办法是换 Tomcat 8.5 或 9.0别硬上 10。另一个参数是 context pathwar 包名就是访问路径改包名等于改路径别在 server.xml 里乱配 Context容易和自动部署冲突。3. BaseDao 与 JDBC 连接数据库连接池、参数化查询和中文乱码3.1 BaseDao 抽了什么为什么值得单独看BaseDao.class 出现在资源列表里说明作者把 JDBC 的重复代码抽到了基类。典型实现是封装getConnection、close、executeUpdate、executeQuery四个方法子类 DAO 继承后只写 SQL。这种设计在 javaWeb 项目完整案例里很常见好处是连接管理集中坏处是如果 BaseDao 写死连接参数换环境就得改源码。常见做法是把 url、user、password 抽到db.propertiesjdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/dorm_db?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/ShanghaiuseSSLfalse jdbc.usernameroot jdbc.passwordyour_password参数逐个说useUnicodetruecharacterEncodingutf8mb4保证中文进出不乱serverTimezoneAsia/Shanghai解决 mysql 8 时区报错The server time zone value ?D1ú±ê×?ê±?? is unrecognizeduseSSLfalse关掉 SSL 握手本地开发省事生产环境要开。注意 mysql 8 的驱动类名是com.mysql.cj.jdbc.Driver不是老的com.mysql.jdbc.Driver写错会警告但通常还能连只是不推荐。3.2 连接池要不要上mysql 的数据库连接池怎么选热词里mysql的数据库连接池是高频问题。这套系统如果每次请求都DriverManager.getConnection并发一上来就崩因为建连接开销大。常见做法是引 Druid 或 C3P0。Druid 配置示例// DruidDataSource 初始化放在 BaseDao 静态块或监听器里 DruidDataSource ds new DruidDataSource(); ds.setDriverClassName(com.mysql.cj.jdbc.Driver); ds.setUrl(jdbc:mysql://localhost:3306/dorm_db?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/ShanghaiuseSSLfalse); ds.setUsername(root); ds.setPassword(your_password); ds.setInitialSize(5); // 初始连接数 ds.setMaxActive(20); // 最大活跃连接 ds.setMinIdle(5); // 最小空闲 ds.setMaxWait(3000); // 获取连接超时毫秒逻辑说明initialSize是启动就建的连接maxActive是并发上限maxWait是拿不到连接时等多久。宿舍管理系统并发不高20 足够。如果不想引第三方库用 Tomcat 自带的 JNDI 数据源也行在context.xml里配 Resourceweb.xml 里配 resource-ref代码里InitialContext.lookup。但 JNDI 配置步骤多新手容易在NameNotFoundException上卡半天所以资源里如果没带连接池直接用 DriverManager 也能跑只是别拿去压测。3.3 参数化查询与 SQL 注入StudentServlet、DormitoryServlet 这些类里如果拼接 SQL就是典型的注入风险。比如登录查询SELECT * FROM admin WHERE username name 输入 OR 11就能绕过。正确做法是 PreparedStatementString sql SELECT * FROM student WHERE stu_no ? AND name ?; try (Connection conn BaseDao.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, stuNo); ps.setString(2, name); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { // 映射到 Student 对象 } } }?占位符由驱动做转义参数按顺序 set索引从 1 开始。这里注意setString对数字字段也能用mysql 会隐式转换但日期字段建议用setDate或setTimestamp否则可能报Incorrect date value。另外 try-with-resources 保证连接和语句自动关闭比手动 finally 里 close 更稳漏关连接是连接池耗尽的第一大原因。4. Servlet 生命周期与验证码LoginServlet、CpachaUtil 怎么串起来4.1 Servlet 生命周期在登录流程里的体现热词servlet生命周期问得多放到这个项目里看最直观。LoginServlet 继承 HttpServlet第一次被访问时容器调init之后每次请求走service根据 method 分发到doGet或doPost应用卸载时调destroy。登录表单一般用 POST所以重写doPostWebServlet(/login) public class LoginServlet extends HttpServlet { Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(utf-8); // 解决 POST 中文乱码 resp.setContentType(text/html;charsetutf-8); String username req.getParameter(username); String password req.getParameter(password); String inputCpacha req.getParameter(cpacha); // 从 session 取验证码比对 Object realCpacha req.getSession().getAttribute(cpacha); if (realCpacha null || !realCpacha.toString().equalsIgnoreCase(inputCpacha)) { req.setAttribute(msg, 验证码错误); req.getRequestDispatcher(/jsp/login.jsp).forward(req, resp); return; } // 查库校验账号密码成功则写 session // ... } }逻辑说明setCharacterEncoding必须在getParameter之前调否则 POST 中文已经按默认编码解析完了再设没用。GET 请求的中文乱码要在 Tomcat 的 server.xml Connector 上加URIEncodingUTF-8或者干脆登录用 POST。getSession()拿到的 session 是同一浏览器会话共享的验证码存 session、登录校验读 session这是最简做法。参数上equalsIgnoreCase让验证码不区分大小写用户体验好一点。4.2 CpachaUtil 验证码生成与前端刷新CpachaUtil.class 是验证码工具类典型实现用java.awt画图生成随机字符、画干扰线、输出到ImageIO.write。Servlet 里这样用WebServlet(/cpacha) public class CpachaServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setContentType(image/jpeg); resp.setHeader(Pragma, no-cache); resp.setHeader(Cache-Control, no-cache); resp.setDateHeader(Expires, 0); String code CpachaUtil.generateCode(4); // 4 位验证码 req.getSession().setAttribute(cpacha, code); CpachaUtil.outputImage(code, resp.getOutputStream()); } }参数说明generateCode(4)的 4 是位数位数越多越安全但越难认no-cache三件套防止浏览器缓存验证码图片导致点刷新不变。前端 jsp 里img srccpacha onclickthis.srccpacha?tnew Date().getTime()加时间戳强制重新请求。坑在于如果验证码 Servlet 的 url-pattern 和登录 Servlet 冲突或者 session 超时用户填完验证码提交时 session 已失效就会一直提示验证码错误。常见做法是把 session 超时设长一点在 web.xml 里配session-configsession-timeout30/session-timeout/session-config单位是分钟。4.3 登录态校验与过滤器LoginServlet 登录成功后把用户信息写进 session其他 Servlet 比如 StudentServlet 在doGet开头要检查 session 里有没有用户没有就跳登录页。每个 Servlet 都写一遍太啰嗦常见做法是加一个 FilterWebFilter(/*) 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(/login) || uri.endsWith(/cpacha) || uri.contains(/static/)) { chain.doFilter(request, response); return; } Object user req.getSession().getAttribute(user); if (user null) { resp.sendRedirect(req.getContextPath() /jsp/login.jsp); return; } chain.doFilter(request, response); } }逻辑说明WebFilter(/*)拦截所有请求白名单放行登录相关和静态资源其余检查 session。req.getContextPath()拿到项目名保证重定向路径正确不写死/dorm-system。坑在于 Filter 的 url-pattern 如果写成/而不是/*可能拦不到子路径另外 Filter 里不要return后忘了chain.doFilter否则请求直接断掉页面白屏。5. 宿舍管理业务模块BuildingServlet、DormitoryServlet、StudentServlet 的增删改查与分页5.1 楼栋、宿舍、学生三层数据模型这套系统的业务层级是楼栋Building→ 宿舍Dormitory→ 学生Student外加宿管DormitoryManager和管理员Admin。表结构大致是 building 表存楼号、楼层数、备注dormitory 表存宿舍号、所属楼栋 id、床位数student 表存学号、姓名、性别、宿舍 id。外键关系决定了删除顺序删楼栋前要先删或解绑该楼栋下的宿舍删宿舍前要先处理学生。常见做法是逻辑删除加is_deleted字段而不是物理删除避免外键约束报错。-- 分页查询学生带宿舍和楼栋信息 SELECT s.stu_no, s.name, s.gender, d.dorm_no, b.building_no FROM student s LEFT JOIN dormitory d ON s.dorm_id d.id LEFT JOIN building b ON d.building_id b.id WHERE s.is_deleted 0 ORDER BY s.id DESC LIMIT ?, ?;LIMIT ?, ?第一个问号是偏移量(pageNo-1)*pageSize第二个是每页条数。参数化时注意 mysql 的 LIMIT 占位符在 PreparedStatement 里支持但有些老驱动不认报语法错误那就用字符串拼接并强制转 int别直接拼用户输入。5.2 Servlet 里的请求分发与 jsp 个人信息展示页面热词jsp个人信息展示页面对应的就是 StudentServlet 查单条后转发到 jsp。Servlet 里通常用一个action参数区分操作WebServlet(/student) public class StudentServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action req.getParameter(action); if (detail.equals(action)) { int id Integer.parseInt(req.getParameter(id)); Student stu studentDao.findById(id); req.setAttribute(stu, stu); req.getRequestDispatcher(/jsp/student_detail.jsp).forward(req, resp); } else if (list.equals(action)) { int pageNo req.getParameter(pageNo) null ? 1 : Integer.parseInt(req.getParameter(pageNo)); int pageSize 10; ListStudent list studentDao.findByPage(pageNo, pageSize); int total studentDao.count(); req.setAttribute(list, list); req.setAttribute(totalPage, (total pageSize - 1) / pageSize); req.getRequestDispatcher(/jsp/student_list.jsp).forward(req, resp); } } }逻辑说明action是轻量路由避免为每个操作建一个 Servlet。forward是服务器内部跳转URL 不变request 域数据能带到 jspredirect是浏览器重新请求request 数据丢失。分页总页数用(total pageSize - 1) / pageSize向上取整别用total / pageSize否则最后一页数据会被吞。jsp 里用 JSTL 的c:forEach遍历 list别在 jsp 里写 Java 脚本片段虽然能跑但难维护。5.3 中文乱码与日期格式的集中处理宿舍管理系统里学生姓名、楼栋备注、宿管姓名都是中文乱码来源有三处数据库字符集、连接 url 参数、Servlet 的 request/response 编码。三处都设成 utf8mb4/utf-8 基本能解决。日期字段比如入住时间mysql 里是date或datetimeJava 里映射成java.util.Datejsp 展示时用fmt:formatDate value${stu.checkInDate} patternyyyy-MM-dd/需要引入 JSTL 的 fmt 标签库。如果直接${stu.checkInDate}输出的是Tue Jan 01 00:00:00 CST 2024这种用户看不懂。参数上 pattern 按需改yyyy-MM-dd HH:mm:ss带时分秒。坑在于 JSTL 的 jar 要放 WEB-INF/lib且 jsp 顶部% taglib prefixc urihttp://java.sun.com/jsp/jstl/core %uri 写错会报The absolute uri cannot be resolved。6. 避坑与排查这套传统 jsp 项目最容易翻车的五个地方6.1 现象启动报 ClassNotFoundException: javax.servlet.http.HttpServlet原因Tomcat 10 及以上版本把 Servlet API 从javax.servlet迁移到了jakarta.servlet而这套系统编译时依赖的是javax包。解决换 Tomcat 8.5 或 9.0或者把源码里所有javax.servlet改成jakarta.servlet并换对应依赖。最省事的是换 Tomcat 版本别改代码。6.2 现象页面中文全是问号或乱码原因三个环节任一没对齐——数据库/表/字段字符集不是 utf8mb4、JDBC url 没带 characterEncoding、Servlet 没设 request/response 编码。解决按 2.1 和 3.1 逐项检查建库建表显式 utf8mb4url 加useUnicodetruecharacterEncodingutf8mb4Servlet 里req.setCharacterEncoding(utf-8)放在取参数之前。GET 请求还要在 Tomcat Connector 加URIEncodingUTF-8。6.3 现象验证码一直提示错误明明输入对了原因session 超时、验证码图片被浏览器缓存、或者前后端取的 session 不是同一个。解决web.xml 里把 session-timeout 调大验证码响应头加 no-cache 三件套前端 img 的 src 加时间戳。还有一种情况是用户开了多个标签页后一个标签刷新了验证码前一个标签提交的是旧值这是交互问题不是代码 bug。6.4 现象mysql 8 连接报 serverTimezone 或 SSL 错误原因mysql 8 驱动要求显式指定时区且默认尝试 SSL 连接。解决url 加serverTimezoneAsia/ShanghaiuseSSLfalse。如果还报Public Key Retrieval is not allowed再加allowPublicKeyRetrievaltrue。生产环境别关 SSL本地开发图省事可以关。6.5 现象分页最后一页数据重复或丢失原因LIMIT偏移量算错或者排序字段不唯一导致分页边界不稳定。解决偏移量用(pageNo-1)*pageSize排序用主键或唯一字段别用可能重复的 name 排序。另外总条数查询和列表查询要在同一事务或同一时刻否则并发插入会导致总页数和实际数据对不上虽然宿舍系统并发低但习惯要养好。7. 进阶技巧把 CpachaUtil 换成更稳的验证码以及用监听器预热连接池验证码这块java.awt画图在无头服务器Linux 没装图形库上会抛HeadlessException这是很多人把项目从 Windows 挪到 Linux 后遇到的第一个翻车点。解决办法是启动参数加-Djava.awt.headlesstrue或者换纯 Java 的验证码库如 kaptcha。kaptcha 的配置更简单// kaptcha 配置放在 web.xml 或 Spring 配置里 DefaultKaptcha kaptcha new DefaultKaptcha(); Properties props new Properties(); props.put(kaptcha.textproducer.char.length, 4); props.put(kaptcha.image.width, 120); props.put(kaptcha.image.height, 40); props.put(kaptcha.textproducer.font.size, 30); Config config new Config(props); kaptcha.setConfig(config); String code kaptcha.createText(); BufferedImage image kaptcha.createImage(code);参数说明char.length是验证码位数image.width/height是图片尺寸font.size是字号。kaptcha 自带干扰线和背景渐变比手写的 CpachaUtil 更难被 OCR 识别。坑在于 kaptcha 的 jar 要放 WEB-INF/lib且它依赖javax.servletTomcat 10 下同样要换版本。另一个进阶点是连接池预热。BaseDao 如果每次请求才建连接第一个请求会慢。常见做法是写一个ServletContextListener在contextInitialized里初始化 DruidDataSource 并放进 ServletContext应用启动就连好 5 个连接。contextDestroyed里调ds.close()避免 Tomcat 关闭时连接泄漏报警告。这个监听器在 web.xml 里配listener或者用WebListener注解。我一般还会在监听器里跑一次SELECT 1做健康检查连不上就打印明确日志而不是等用户访问才报错。从那以后我每次拿到这种传统 jsp 项目都强制先确认三件事Tomcat 版本是不是 9 以下、mysql 连接 url 有没有带时区和编码参数、WEB-INF/lib 下的 jar 是不是齐。这三步走完八成启动问题直接消失。希望帮到你。本文还有配套的精品资源点击获取
返回列表