简介这是一套面向高校计算机相关专业毕业设计的JSP小区水电费管理系统完整项目包采用JSPMySQLB/S架构适合正在准备毕设或需要Java Web实战练手的同学参考。系统分为前台与后台两大模块前台提供站内新闻浏览、在线留言与回复查看、用户注册及小区风景展示后台管理员可管理新闻、审核注册用户、维护用户资料与水电费类别注册用户则能修改个人资料并查询缴费记录功能链路覆盖了典型的信息管理与费用缴纳场景。压缩包共713个文件约10.12MB以gif、jpg等图片素材和jsp页面为主辅以css样式、js脚本、htm静态页及db数据库文件另含jar依赖、sql建库脚本与项目配置文件结构完整可直接导入运行。资源同时附带说明文档与演示视频已有134人学习下载便于快速理解模块划分、数据库设计与页面跳转逻辑是完成同类毕设或课程设计的实用参考。1. 小区水电费管理系统一个 JSP 毕设项目从跑通到讲清楚的完整路径每年毕业季计算机专业的选题里总有一类题目反复出现——基于 JSP 的信息管理系统。小区水电费管理系统就是其中典型的一个它要管住户信息、抄表记录、费用计算、缴费状态还要有管理员和普通用户两种角色。很多同学拿到这个题目时第一反应是去网上找一份源码改改界面就交差。但真正答辩时被问到「你的费用计算逻辑怎么处理的」「JSP 和 Servlet 之间怎么传数据」就答不上来了。这篇内容面向的是正在做 JSP 毕设、或者需要快速理解一个传统 JavaWeb 项目结构的同学。我会从环境搭建讲到核心业务实现再到部署打包和答辩时容易被追问的技术点。不堆概念每一步都落到能跑起来的代码和配置上。如果你手里已经有一份类似的源码也可以对照着看把「能跑」变成「能讲清楚」。2. 环境搭建与项目骨架从零把 JSP 项目跑起来2.1 JDK、Tomcat、MySQL 的版本选择与安装要点传统 JSP 项目对版本比较敏感不是越新越好。我一般推荐这套组合JDK 8或 JDK 11、Tomcat 8.5 或 9.0、MySQL 5.7 或 8.0。原因很直接——网上能找到的 JSP 毕设源码绝大多数是在 JDK 8 Tomcat 8 的环境下写的用 JDK 17 或 Tomcat 10 很容易遇到javax.servlet包找不到的问题因为 Tomcat 10 把包名改成了jakarta.servlet。安装 JDK 后配置JAVA_HOMETomcat 解压即可用MySQL 建议用安装版并记住 root 密码。验证环境是否就绪java -version # 输出应为 java version 1.8.0_xxx 或 11.0.x echo %JAVA_HOME% # Windows 下应输出 JDK 安装路径如 C:\Program Files\Java\jdk1.8.0_301Tomcat 启动验证进入bin目录执行startup.batWindows或startup.shLinux/Mac浏览器访问http://localhost:8080看到 Tomcat 默认页即成功。注意如果 8080 端口被占用修改conf/server.xml中的Connector port8080为其他端口比如 8081。2.2 用 IDEA 新建 JSP 项目的目录结构与关键配置在 IDEA 中新建项目时选 Java Enterprise勾选 Web Application。生成的项目结构里核心目录是web或webapp里面放 JSP 页面和WEB-INF。WEB-INF下的web.xml是部署描述符lib目录放第三方 jar 包。一个典型的小区水电费管理系统目录结构如下src/ com/community/dao/ # 数据库访问层 com/community/entity/ # 实体类 com/community/servlet/ # 控制层 Servlet com/community/util/ # 工具类数据库连接等 web/ css/ js/ images/ login.jsp # 登录页 admin/ # 管理员页面 user/ # 普通用户页面 WEB-INF/ web.xml lib/ mysql-connector-java-8.0.xx.jar关键配置在web.xml中注册 Servlet 和设置欢迎页web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee version4.0 welcome-file-list welcome-filelogin.jsp/welcome-file /welcome-file-list servlet servlet-nameLoginServlet/servlet-name servlet-classcom.community.servlet.LoginServlet/servlet-class /servlet servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login/url-pattern /servlet-mapping /web-app这里url-pattern决定了浏览器访问的路径。比如表单提交到/login就会触发LoginServlet的doPost方法。很多同学在页面里写actionLoginServlet也能跑但那是相对路径匹配项目一改上下文路径就容易 404建议统一用/开头的绝对路径。2.3 数据库连接工具类的封装与连接池配置JSP 项目里最常见的翻车点之一就是数据库连接。新手容易在每个 Servlet 里写一遍DriverManager.getConnection代码重复不说连接用完不关还会导致 MySQL 连接数爆满。正确做法是封装一个工具类有条件的上连接池。先建数据库和核心表CREATE DATABASE community_fee DEFAULT CHARACTER SET utf8mb4; USE community_fee; CREATE TABLE resident ( id INT PRIMARY KEY AUTO_INCREMENT, room_no VARCHAR(20) NOT NULL COMMENT 房号, name VARCHAR(50) NOT NULL, phone VARCHAR(20), password VARCHAR(50) DEFAULT 123456 ); CREATE TABLE meter_record ( id INT PRIMARY KEY AUTO_INCREMENT, resident_id INT, record_month VARCHAR(7) COMMENT 格式 2024-01, water_usage DECIMAL(10,2) COMMENT 用水量(吨), electric_usage DECIMAL(10,2) COMMENT 用电量(度), water_price DECIMAL(6,2) DEFAULT 3.50, electric_price DECIMAL(6,2) DEFAULT 0.56, total_fee DECIMAL(10,2), pay_status TINYINT DEFAULT 0 COMMENT 0未缴 1已缴, FOREIGN KEY (resident_id) REFERENCES resident(id) );工具类封装package com.community.util; import java.sql.*; public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/community_fee?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8; private static final String USER root; private static final String PASSWORD 你的密码; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { try { if (rs ! null) rs.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (stmt ! null) stmt.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn ! null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }serverTimezoneAsia/Shanghai这个参数在 MySQL 8.0 下必须加否则会报时区错误。characterEncodingutf8保证中文不乱码。如果你的 MySQL 是 5.7驱动类名可以写com.mysql.jdbc.Driver但com.mysql.cj.jdbc.Driver也兼容。提示连接池可以用 Druid 或 C3P0但对于毕设规模直接封装工具类足够。答辩时如果被问到「连接池」能说清楚「减少频繁创建连接的开销」即可。3. 核心业务实现住户管理、抄表录入与费用计算3.1 住户信息 CRUD 的 Servlet JSP 协作模式住户管理是整个系统的数据基础。典型操作包括管理员查看住户列表、新增住户、修改信息、删除住户。这里以「新增住户」为例走一遍完整的数据流。前端addResident.jsp表单form action${pageContext.request.contextPath}/resident?actionadd methodpost 房号input typetext nameroomNo requiredbr 姓名input typetext namename requiredbr 电话input typetext namephonebr input typesubmit value提交 /form${pageContext.request.contextPath}会动态获取项目上下文路径避免部署路径变化导致表单提交 404。这是 JSP 里非常实用的一个写法。后端 Servlet 用action参数区分操作类型package com.community.servlet; import com.community.dao.ResidentDao; import com.community.entity.Resident; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.WebServlet; import java.io.IOException; WebServlet(/resident) public class ResidentServlet extends HttpServlet { private ResidentDao residentDao new ResidentDao(); Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action req.getParameter(action); if (list.equals(action)) { req.setAttribute(list, residentDao.findAll()); req.getRequestDispatcher(/admin/residentList.jsp).forward(req, resp); } else if (delete.equals(action)) { residentDao.delete(Integer.parseInt(req.getParameter(id))); resp.sendRedirect(req.getContextPath() /resident?actionlist); } } Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String action req.getParameter(action); if (add.equals(action)) { Resident r new Resident(); r.setRoomNo(req.getParameter(roomNo)); r.setName(req.getParameter(name)); r.setPhone(req.getParameter(phone)); residentDao.insert(r); resp.sendRedirect(req.getContextPath() /resident?actionlist); } } }doPost里第一行req.setCharacterEncoding(UTF-8)必须放在读取任何参数之前否则中文姓名会变成乱码。这个坑几乎每个 JSP 新手都会踩一次。sendRedirect和forward的区别也要清楚前者是浏览器重新发起请求地址栏变化后者是服务器内部转发地址栏不变。增删改后用重定向避免刷新页面重复提交。3.2 抄表记录录入与水电费自动计算逻辑抄表录入是业务核心。管理员选择住户、月份输入本月水表和电表读数系统自动算出用量和费用。这里有两种设计一种是直接输入用量一种是输入本次读数并减去上次读数。后者更贴近真实场景但实现稍复杂。费用计算公式水费 用水量 × 水单价 电费 用电量 × 电单价 总费用 水费 电费在 Servlet 中处理protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); int residentId Integer.parseInt(req.getParameter(residentId)); String month req.getParameter(recordMonth); double waterUsage Double.parseDouble(req.getParameter(waterUsage)); double electricUsage Double.parseDouble(req.getParameter(electricUsage)); double waterPrice 3.50; double electricPrice 0.56; double totalFee waterUsage * waterPrice electricUsage * electricPrice; MeterRecord record new MeterRecord(); record.setResidentId(residentId); record.setRecordMonth(month); record.setWaterUsage(waterUsage); record.setElectricUsage(electricUsage); record.setWaterPrice(waterPrice); record.setElectricPrice(electricPrice); record.setTotalFee(totalFee); record.setPayStatus(0); meterDao.insert(record); resp.sendRedirect(req.getContextPath() /meter?actionlist); }单价建议存在数据库或配置文件里不要硬编码在 Java 代码中。答辩时老师如果问「水价调整了怎么办」你可以回答「修改配置表即可不需要重新编译」。这是一个加分项。Double.parseDouble如果传入空字符串会抛NumberFormatException前端加required属性只能防普通用户接口层面最好再做一次判空String waterStr req.getParameter(waterUsage); if (waterStr null || waterStr.trim().isEmpty()) { req.setAttribute(error, 用水量不能为空); req.getRequestDispatcher(/admin/meterAdd.jsp).forward(req, resp); return; }3.3 缴费状态管理与住户端查询页面缴费状态是一个TINYINT字段0 表示未缴1 表示已缴。管理员在列表页点击「标记已缴」触发更新住户登录后只能看到自己的记录。管理员更新缴费状态if (pay.equals(action)) { int id Integer.parseInt(req.getParameter(id)); meterDao.updatePayStatus(id, 1); resp.sendRedirect(req.getContextPath() /meter?actionlist); }住户端查询需要从 session 中取当前登录用户的 id只查自己的数据HttpSession session req.getSession(); Resident current (Resident) session.getAttribute(currentUser); if (current null) { resp.sendRedirect(req.getContextPath() /login.jsp); return; } ListMeterRecord myRecords meterDao.findByResidentId(current.getId()); req.setAttribute(records, myRecords); req.getRequestDispatcher(/user/myFee.jsp).forward(req, resp);myFee.jsp里用 JSTL 遍历展示% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % table border1 trth月份/thth用水量/thth用电量/thth总费用/thth状态/th/tr c:forEach items${records} varr tr td${r.recordMonth}/td td${r.waterUsage}/td td${r.electricUsage}/td td${r.totalFee}/td td${r.payStatus 1 ? 已缴 : 未缴}/td /tr /c:forEach /tableJSTL 的c:forEach比在 JSP 里写 Java 脚本片段% %更规范也更符合 MVC 分层思想。答辩时如果被问到「为什么不用脚本片段」可以说「JSTL 让页面职责更单一便于维护」。4. 避坑与排查JSP 毕设项目最容易翻车的 5 个地方4.1 中文乱码现象、原因与三层解决现象表单提交后数据库里存的是????或者页面显示乱码。原因JSP 页面编码、请求编码、数据库编码、Tomcat 编码任何一层不一致都会导致乱码。解决三层同时设置。JSP 页面头部加% page contentTypetext/html;charsetUTF-8 languagejava %Servlet 里req.setCharacterEncoding(UTF-8)和resp.setContentType(text/html;charsetUTF-8)数据库连接 URL 加characterEncodingutf8。Tomcat 的conf/server.xml中 Connector 加URIEncodingUTF-8。4.2 404 错误路径匹配的常见误用现象表单提交或页面跳转时报 404。原因action或href写的是相对路径项目上下文路径变化后匹配不到。或者web.xml里url-pattern和表单action不一致。解决统一用${pageContext.request.contextPath}/xxx拼接路径。检查web.xml中 Servlet 映射和WebServlet注解是否重复或冲突。如果用了注解就不需要在web.xml里再配一遍。4.3 数据库连接泄漏连接数爆满的排查思路现象系统运行一段时间后报Too many connections。原因每次getConnection后没有在finally块中关闭或者异常发生时跳过了关闭逻辑。解决所有数据库操作统一用try-finally或try-with-resourcestry (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { // 执行操作 } catch (SQLException e) { e.printStackTrace(); }try-with-resources会自动关闭实现了AutoCloseable的对象比手动finally更简洁。MySQL 默认最大连接数是 151毕设演示时一般够用但养成好习惯没坏处。4.4 JSTL 标签不生效jar 包缺失与版本冲突现象页面直接显示c:forEach标签原文或者报The absolute uri cannot be resolved。原因WEB-INF/lib下缺少jstl.jar和standard.jar或者只放了一个。解决JSTL 1.2 版本只需要jstl-1.2.jar一个包。如果用的是老版本需要jstl.jarstandard.jar两个。确认 jar 包放在WEB-INF/lib下而不是项目根目录IDEA 中还要在 Project Structure 的 Artifacts 里确认这些 jar 会被打包进WEB-INF/lib。4.5 部署到 Tomcat 后页面样式丢失现象本地 IDEA 里跑正常打包成 war 部署到独立 Tomcat 后 CSS、JS、图片全部 404。原因页面里用了相对路径引用静态资源比如hrefcss/style.css但实际访问路径多了层上下文。解决静态资源引用也统一加${pageContext.request.contextPath}例如href${pageContext.request.contextPath}/css/style.css。或者在 JSP 头部定义base href${pageContext.request.contextPath}/让所有相对路径基于这个基准。5. 打包部署与答辩加分项把项目讲出工程味5.1 传统 JSP 项目打包 war 的完整流程IDEA 中打包 war 的步骤File → Project Structure → Artifacts → 添加 Web Application: Archive → 选择输出目录 → Build → Build Artifacts。生成的 war 包复制到 Tomcat 的webapps目录启动 Tomcat 会自动解压部署。命令行方式# 在项目根目录执行前提是已配置好目录结构 jar -cvf community_fee.war * # 将 war 包复制到 Tomcat webapps cp community_fee.war $TOMCAT_HOME/webapps/ # 启动 Tomcat $TOMCAT_HOME/bin/startup.sh访问http://localhost:8080/community_fee/login.jsp即可。war 包的好处是部署简单答辩时可以直接在老师电脑上跑不依赖 IDEA。5.2 答辩时被追问的技术点与回答思路老师常问的几个问题提前准备好答案追问点回答思路为什么用 JSP 不用前后端分离项目规模小、周期短JSP 开发效率高适合快速交付Session 和 Cookie 的区别Session 存服务端Cookie 存客户端登录状态用 Session 更安全数据库连接怎么管理的封装了工具类统一获取和关闭避免泄漏费用计算逻辑在哪一层在 Servlet 层计算DAO 层只负责存取职责分离如果并发缴费怎么处理可以在更新语句加WHERE pay_status0条件防止重复缴费最后一条是加分项。实际代码UPDATE meter_record SET pay_status1 WHERE id? AND pay_status0;通过executeUpdate返回的影响行数判断是否更新成功如果返回 0 说明已经被缴过了。这个细节能体现你对并发场景的思考。5.3 从毕设到可展示项目三个低成本改进方向如果想让项目在答辩时更出彩不需要大改架构三个小改进就够第一把单价配置抽到数据库表sys_config里管理员可以在后台修改水价电价体现「可配置」思维。第二给列表页加分页。数据量大了不分页会很难看用LIMIT offset, size实现即可前端加「上一页/下一页」按钮。第三加一个简单的费用统计图。用 ECharts 引入一个 CDN 链接后端返回 JSON 数据展示每月总费用趋势。不需要很复杂一个折线图就能让演示效果提升一个档次。// 在 JSP 中引入 ECharts 并渲染折线图 var chart echarts.init(document.getElementById(chart)); chart.setOption({ xAxis: { type: category, data: [2024-01,2024-02,2024-03] }, yAxis: { type: value }, series: [{ type: line, data: [1200, 1350, 1100] }] });数据可以从后端 Servlet 查出来后转成 JSON 塞进 request 域JSP 里用${monthData}取。这样既展示了前后端数据交互又让页面不那么单调。我自己做这类项目最大的教训是不要等到最后一天才打包部署。本地 IDEA 跑通和 war 包部署到独立 Tomcat 是两回事路径问题、jar 包冲突、编码问题都可能在部署阶段集中爆发。提前一周在干净环境里完整走一遍部署流程留出排查时间。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
Java五子棋网络对战毕设实战:Socket通信与多线程机制解析 简介:一份面向计算机专业毕业生的Java五子棋手机网络对战游戏完整毕设项目,包含可直接运行的软件源码与系统设计文档,适合用于课题研究、课程实践与论文参考。压缩包约5.55MB,以Java源码与论文文档为主,覆盖Java基础、… · 2026/9/26 12:26:38
基于JSP的小区水电费管理系统:从抄表到缴费全流程设计与实现 简介:这份资源是面向高校计算机相关专业学生与Java Web初学者的小区水电费管理系统毕业设计完整包,采用JSPMySQLB/S架构,可作为课程设计、毕业设计选题或JSP入门练手项目。压缩包共713个文件,约10.12MB,以gif图片、jsp… · 2026/9/26 12:26:38
MySQL read_only 命令全解:从主从切换到权限边界 我第一次把它写进主从切换预案,是在一个凌晨的变更窗口里。脚本依次执行 SET GLOBAL read_only ON; 、检查复制状态、然后把流量切到新主节点。当时根本没多想——就五个单词的 SQL,能有什么花头?直到第二天业务方拿着截图来找我ÿ… · 2026/9/26 12:26:38
用Dify搭建RAG知识库与带记忆Agent:痛风饮食监督系统实战 痛风快十年,饭桌上的每一筷子都是跟身体的谈判。我一直想做一个自己的“痛风知识库”——把所有医生建议、嘌呤数据、忌口原则、常见饮食误区整理成一个能随时问、随处查的系统,再给这个知识库配上一个叫“吃不停的Agent”的助手。它管的不只是查嘌呤表&… · 2026/9/26 13:00:54
RAG实战:如何构建一个“吃不停”的痛风知识库Agent 先交代一下背景。家父痛风十几年,尿酸最高冲到过620μmol/L,每次发作都是半夜脚趾头火辣辣地疼,饭桌上这不敢吃那不敢碰,朋友圈里转来的“痛风食物大全”又互相打架。我一开始只想做一个方便全家查询的痛风饮食清单,结… · 2026/9/26 13:00:54
金融业务能力切片:构建合规与敏捷并重的服务化架构 1. 项目概述:这不是一个“服务”,而是一套可落地的金融业务支撑逻辑“financial-services”——看到这个词,很多人第一反应是银行App、理财平台或者保险销售页面。但在我过去十年跑过37家中小金融机构、参与过11个核心系统迭代的真实经验里&a… · 2026/9/26 13:00:54
AI重构Obsidian知识库:从四千条乱笔记到可检索资产 1. 先别急着整理:几千条笔记乱成一团,根子不在懒而在系统设计 说个我自己的真实场景:上个月我想用Obsidian找几条关于"项目复盘"的资料,搜索框敲下去,直接跳出两百多条结果,其中几十条标题都是&q… · 2026/9/26 13:00:54
别再收藏吃灰!这4个AI开源项目精准解决编程、求职、科研与PPT痛点 GitHub上的AI开源项目已经多到让人产生自我怀疑。我见过太多人和我两年前一样:晚上刷到某个高分项目,觉得“这个太厉害了,明天一定用”,点下Star,第二天打开电脑,面对的还是原来那条工作流。问题根本不在项… · 2026/9/26 13:00:54
改进粒子群算法求解含碳捕集微网多时间尺度低碳经济调度及Matlab实现 我这个题目做了大半年,从最初的懵圈到最后跑通整个仿真链路,踩过的坑、绕过的弯,写出来比论文正文还长。这篇博文就围绕“基于改进粒子群算法的含碳捕集微网多时间尺度低碳经济调度”这个方向,把我在Matlab里实现这套代码的完整思… · 2026/9/26 13:00:48
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46