news 2026/10/10 6:24:37

JSP小区水电费管理系统实战:从MVC架构到答辩演示全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JSP小区水电费管理系统实战:从MVC架构到答辩演示全流程

简介:面向毕业设计场景的小区水电费管理系统完整项目包,基于JSP+MySQL的B/S架构实现,适合Java Web方向学生借鉴参考。程序分设网站前台、后台管理员与注册用户三类角色,涵盖站内新闻、在线留言、用户注册、小区风景展示,以及后台新闻管理、用户审核、水电费类别与缴费信息管理、个人资料修改和水电费缴纳查询等功能,逻辑完整且贴近实际小区物业业务。资源包共713个文件,约10.12MB,以JSP页面、Java源码、SQL数据库脚本为主体,辅以CSS/JS前端样式、GIF演示截图、JAR依赖库及说明文档,并附带演示视频,便于快速部署与理解整体流程。已有134人学习下载。压缩包内结构清晰,除项目源码外还包含配置文件和数据库初始化脚本,可辅助完成设计文档撰写与答辩演示,适合需要完整项目案例进行二次开发或课程设计的读者。

1. 如果选题库里还躺着「JSP 小区水电费管理系统」,别急着说它过时

不少同学打开课程设计选题库,看到「小区水电费管理系统」这类 JSP 项目,第一反应往往是「这技术是不是早就淘汰了」。实际情况恰好相反:这类项目在答辩里普遍好过,因为它表面是一堆增删改查页面,背后却完整覆盖了 Java Web 的全部关键环节——数据库建模、会话控制、事务处理、分页查询、报表统计,每个点都是老师爱深问的东西。它解决什么问题?简单说,小区管理处要管住户、抄表、算水电费、记每笔缴费,住户要查欠费看记录,这套系统把两边需求全搬上浏览器。适合谁?适合想快速拿到完整可用项目、能把原理讲清楚、并让演示过程挑不出硬伤的同学,也适合刚学完 JSP、想看看真实完整项目长什么样的开发者来拆结构。

2. 先看清架构再动手:MVC 三层与六张表的关系决定你后面省不省心

拿到 zip 后直接导入 IDE 是最容易翻车的开场。先别着急,花二十分钟把项目骨骼看清楚。这套基于 JSP 的毕业设计通常不是把页面写死,而是按经典的 JSP + Servlet + JavaBean 结构组织,也就是学院派常讲的 MVC。先把视图、控制、模型三层分别指认出来,后面改功能、查 bug、写答辩词都会顺手很多。

2.1 JSP 项目里的 MVC 到底是谁,谁在干活

在这种老牌分层里,控制层是 Servlet,视图层是 JSP,模型层拆成两块:业务逻辑放在 Service 类里,数据访问放在 DAO 类里。一个请求的完整生命周期是这样的:浏览器把请求发给 Servlet 的 doGet 或 doPost 方法,Servlet 做参数校验,再调用 Service 处理业务,Service 内部通过 DAO 访问数据库,拿到结果后把数据放进 request 或 session 作用域,最后 forward 给 JSP 页面。JSP 用 JSTL 和 EL 表达式把数据渲染成 HTML 返回给浏览器。

这里有个新手最容易忽略的点:JSP 本身也会被编译成 Servlet,但它只适合做展示逻辑。如果你在 JSP 里看到大段<% ... %>直接写 JDBC 的代码,说明原项目结构已经混成一团。这种质量不高的毕业设计很常见,值不值得改?我一般会建议:时间够就抽一层 DAO 出来,时间紧就保留原状,但答辩时不要主动把那段展示出来。项目架构不是不能辩解,关键是能讲清你项目里的实际分层,哪怕只有两层也比一锅粥强。

视图、控制、模型的具体分工,可以对照下面这张表来理解:

层次JSP 项目中的对应物常见误区
视图层JSP 页面 + JSTL/EL 表达式在页面里写复杂 Java 业务逻辑
控制层Servlet 以及 Filter 过滤器把 SQL 语句直接写进 Servlet
业务与数据层Service + DAO 类所有增删改查挤在一个类里

2.2 表结构设计:水费电费为什么分开记,账单状态怎么流转

这套系统的数据模型是整个答辩的重头戏。水电费听起来简单,实际要解决三个设计问题:用量怎么算、账单怎么对账、费用类型怎么区分。常见做法是落到六张表左右:管理员表、住户表、水费记录表、电费记录表、缴费记录表,再根据是否分楼栋决定要不要加楼栋表。下面给出核心建表语句,可以直接照着执行:

-- 管理员表 CREATE TABLE admin ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL, password VARCHAR(100) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='管理员表'; -- 住户表 CREATE TABLE user_info ( id INT AUTO_INCREMENT PRIMARY KEY COMMENT '主键', room_no VARCHAR(20) NOT NULL COMMENT '房号,例如 3-2-501', owner_name VARCHAR(50) NOT NULL COMMENT '户主姓名', phone VARCHAR(20) COMMENT '联系电话', water_meter_no VARCHAR(30) COMMENT '水表编号', elec_meter_no VARCHAR(30) COMMENT '电表编号', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '建档时间', UNIQUE KEY uk_room (room_no) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='住户信息表'; -- 水费抄表记录表 CREATE TABLE water_record ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL COMMENT '关联住户ID', bill_month VARCHAR(10) NOT NULL COMMENT '账期,格式 2025-06', last_reading DECIMAL(10,2) COMMENT '上次读数', current_reading DECIMAL(10,2) COMMENT '本次读数', usage_amount DECIMAL(10,2) COMMENT '用量 = 本次读数 - 上次读数', unit_price DECIMAL(6,2) COMMENT '单价', total_money DECIMAL(10,2) COMMENT '金额', status TINYINT DEFAULT 0 COMMENT '0未缴 1已缴', FOREIGN KEY (user_id) REFERENCES user_info(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='水费抄表记录'; -- 电费记录表结构与水费一致,只是账期字段和业务含义不同 CREATE TABLE elec_record ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL COMMENT '关联住户ID', bill_month VARCHAR(10) NOT NULL, last_reading DECIMAL(10,2), current_reading DECIMAL(10,2), usage_amount DECIMAL(10,2), unit_price DECIMAL(6,2), total_money DECIMAL(10,2), status TINYINT DEFAULT 0, FOREIGN KEY (user_id) REFERENCES user_info(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='电费抄表记录'; -- 缴费流水表 CREATE TABLE payment_record ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL COMMENT '关联住户ID', bill_type TINYINT NOT NULL COMMENT '1水费 2电费', bill_id INT NOT NULL COMMENT '来源账单ID', pay_amount DECIMAL(10,2) NOT NULL COMMENT '缴费金额', pay_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '缴费时间', status TINYINT DEFAULT 0 COMMENT '0作废 1正常', FOREIGN KEY (user_id) REFERENCES user_info(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='缴费流水表';

为什么水费和电费分开建表?答辩时可以这么讲:两类费用的计量单位不同、单价不同,后续扩展方向也不同,比如电费要加峰谷电价、水费要加阶梯水价。如果合成一张表,会出现大量空字段,按类型过滤的 SQL 也会越写越别扭。分开建表后,统计各自费用的聚合查询非常干净。

另外要重点理解缴费状态字段。已缴账单必须留痕,所以缴费动作不能是把账单记录直接删掉,而是先在 payment_record 里插入流水,再更新账单表把 status 从 0 改成 1。这个状态流转逻辑会贯穿缴费模块的完整代码,后面第四章会展开。还有一个最容易答不上的问题:每月抄表时上个月读数怎么来?正确做法是读取该住户上一条抄表记录的 current_reading 作为本次的 last_reading,而不是让录入员手工填,否则会出现同一表号连续两个月读数对不齐的「玄学」数据。

2.3 同是 Java Web,为什么学院派答辩反而更认 JSP + Servlet

Spring Boot 三小时能搭好一个后台,但答辩老师很难从里面看出学生的基本功:自动配置帮你遮住了 XML 配置,内置容器让你说不清请求是怎么到达控制器的。而 JSP + Servlet 的全部环节都摆在明面上:web.xml 里怎么声明 Servlet、Filter 怎么拦截、数据怎么 forward 到页面,每一个都能问、能讲、能演示,这意味着稳。

当然,JSP 方案的短板也真实存在:页面逻辑与表现层耦合,不好支撑前后端分离。这个缺点被问到时,要有一套成熟口径,不要硬撑也不用认输。建议的回答是:「毕业设计想完整展示对 Java Web 底层机制的理解,Servlet/JSP 是这些机制的最小完整载体;项目内部同样做了 Service/DAO 分层和事务管理,换成 Spring 也只是把装配方式交还容器。」这套说法既大方承认技术选型有边界,又强调了你对工程结构的把控。答辩现场讲出层次感,比单纯说「老师给的题目就是这个」要体面得多。

3. 从 zip 到登录页:环境配置、导入项目与数据库连接的完整步骤

大多数人拿到的压缩包里有三样东西:源码目录、说明文档、演示视频。正确顺序是:先读说明文档里的环境要求和数据库账号信息,再建库导数据,最后导入工程。如果一上来就双击导入 IDE,后面光是找数据库连接配置就要白耗半小时。演示视频可以晚点看,它是给你核对操作结果用的,不是给你照着敲代码用的。

3.1 JDK/Tomcat/MySQL 版本搭配:JDK8、Tomcat8.5/9、MySQL5.7 是基线

JSP 老项目对版本非常敏感,版本错了经常出现编译不过或页面 500。经验上最稳妥的搭配是:JDK 8 + Tomcat 8.5 或 9.0 + MySQL 5.7,字符集统一 UTF-8。为什么不用 JDK 17?因为老项目里的依赖包大多是 javax.servlet 命名空间,而 Tomcat 10 已经换成 jakarta.servlet,旧代码导入后直接编译失败。只要看到源码里 web.xml 头部的命名空间还是旧的 javax,就别轻易用 Tomcat 10。

如果本机已经装了新版 JDK 或新版 Tomcat,我的建议是单独解压一个 Tomcat 9 绿色版,配置上 JAVA_HOME 指向本机 JDK 8,不影响其他开发环境。很多同学把项目从学长手里拿过来在自己机器上跑不起来,八成就是版本基线没对齐,而不是代码本身有问题。先把这套基线固定住,再谈后续优化。

3.2 导入项目的两种方式与目录结构核对清单

拿到项目先看目录结构。一个标准的 JSP Web 项目长这样:

project-name/ ├── src/ # Java 源码,Servlet/DAO/Service 都在这里 ├── WebContent/ # 也可能是 webapp,存放 JSP 与静态资源 │ ├── WEB-INF/ │ │ ├── web.xml # 部署描述文件 │ │ └── lib/ # 放 mysql-connector-java-x.x.jar │ └── *.jsp └── database/ # 初始化 SQL 通常放在这里

IDEA 导入方式:File -> New -> Project from Existing Sources,选中项目根目录,下一步时注意选择 Import project from external model 里的 Eclipse 选项,因为很多 JSP 老项目本身就是 Eclipse 工程结构。Eclipse 导入方式:File -> Import -> General -> Existing Projects into Workspace,选择根目录即可。导入后第一件事是检查 Project Structure 里有没有引入 Tomcat 运行时,没有的话 Servlet 相关的 import 会全部飘红,这是新手最容易卡半天的位置。

同时核对 WEB-INF/lib 下是否有 mysql-connector-java 的 jar 包。老项目经常漏掉这个文件,IDE 里不报错,一运行就报 ClassNotFoundException。这一步属于白纸黑字的文件检查,不要跳过。

3.3 建库、导数据与数据库连接的四处参数修改

先启动 MySQL 服务,然后用命令行或图形化客户端执行项目里的 SQL 文件。建议先看 SQL 文件里的建库语句,通常第一行是 CREATE DATABASE 某个库名;如果没有,就手动建一个同样名字的库:

mysql -u root -p CREATE DATABASE IF NOT EXISTS db_water_elec DEFAULT CHARACTER SET utf8mb4; USE db_water_elec; SOURCE /path/to/init.sql;

导完数据后,在工程里找到数据库连接配置文件。老项目里常见的位置是 src/db.properties,或者直接写在 DBUtil.java 类里。一般需要改四处:驱动类名、连接 URL 里的库名、用户名、密码。改完后的 db.properties 大致长这样:

jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/db_water_elec?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=你的密码

注意驱动类名的差别:MySQL 5.x 用 com.mysql.jdbc.Driver,MySQL 8.x 必须改成 com.mysql.cj.jdbc.Driver。URL 里的 useSSL=false 用于去掉新驱动的安全连接警告,serverTimezone=Asia/Shanghai 解决时区错乱导致时间字段比实际早 8 小时的问题,这两个参数也是经典翻车点。连接配置改完后,先在 IDE 或命令行里手工测试一次连接,确认能连上再启动 Tomcat,不要等到部署后看一整屏异常。

3.4 部署到 Tomcat 之后还差最后两步:路径检查与默认账号验证

部署方式上,IDEA 里配置一个 Tomcat Runtime,Artifact 选 war exploded 方式,Application context 填/db_water_elec。启动后访问地址是 http://localhost:8080/db_water_elec/ ,如果直接 404,先去看 Tomcat 启动日志,然后在 Deployment 配置里确认 Application context 名称,浏览器路径必须与它完全一致。

登录账号不要去猜,去看 SQL 文件里的 INSERT INTO admin 语句,或者翻说明文档里的初始账号说明,常见组合是 admin/admin123 或 admin/123456。连续输错几次如果出现「密码错误次数过多」提示,那说明项目本身带了失败计数功能,不是 bug。另外,如果原项目里没有单独的 DBUtil 类,而是在每个 Servlet 里裸写 JDBC,那连接管理和资源关闭的写法会被老师追问,建议把它整理成统一工具类,既减少重复代码,也让答辩展示更专业。

4. 把核心代码写明白:登录会话、账单闭环与缴费事务

看得懂源码和能讲清源码,在毕业设计里是两码事。这一章我把这套系统最常见的核心代码套路拆开讲,方便你读代码时有轮廓,改功能时知道动哪里,答辩被追问时也知道往哪个方向答。

4.1 登录与会话:不要在每个页面重复写判断,用一个登录过滤器

登录逻辑本身不复杂:查 admin 表,密码对就写 session。但「登录后才能访问」这件事如果每个页面复制粘贴一段判断,代码会失控且容易漏。正确做法是写一个登录过滤器,统一拦截所有请求:

@WebFilter(urlPatterns = {"/*"}) 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; // 放行登录页、登录接口和静态资源 String uri = request.getRequestURI(); if (uri.endsWith("login.jsp") || uri.endsWith("LoginServlet") || uri.contains("/static/")) { chain.doFilter(req, resp); return; } // 未登录用户重定向到登录页 if (request.getSession().getAttribute("admin") == null) { response.sendRedirect(request.getContextPath() + "/login.jsp"); return; } chain.doFilter(req, resp); } }

逻辑说明:chain.doFilter 必须调用,否则请求会被静默拦截,页面表现为「点了没反应」。路径判断里放行了 LoginServlet 和 login.jsp,因为登录接口本身不能被拦;静态资源也要放行,否则 CSS 全部加载不出来,页面变成纯文本,视觉效果和「系统坏了」一样。参数说明:getContextPath() 返回当前项目的部署路径,用它拼跳转地址,就算以后改了项目名也不会失效。

4.2 抄表与生成账单:读数差就是用量,小心上次读数

抄表录入是整个系统的数据源头。录入本月读数时,系统要做三件事:找到该住户上一条水费记录,把它的 current_reading 作为本次的 last_reading;计算 usage_amount = current_reading - last_reading;再乘单价算出 total_money。这三步在一个 Service 方法里完成,而不是分散在 JSP 里:

public void addWaterRecord(WaterRecord record) { // 1. 查最近一条抄表记录,作为本次 last_reading WaterRecord last = waterDao.findLatestByUser(record.getUserId(), "water"); if (last != null) { record.setLastReading(last.getCurrentReading()); } else { record.setLastReading(0); } // 2. 计算用量和金额,BigDecimal 避免浮点误差 record.setUsageAmount(record.getCurrentReading().subtract(record.getLastReading())); record.setTotalMoney(record.getUsageAmount().multiply(record.getUnitPrice())); // 3. 校验:新读数不能小于上次读数 if (record.getCurrentReading().compareTo(record.getLastReading()) < 0) { throw new BusinessException("本次读数不能小于上次读数"); } waterDao.insert(record); }

参数说明:金额计算用 BigDecimal 而不是 double,double 算钱会出现 0.1 + 0.2 不等于 0.3 的问题,账目对不上在演示时非常尴尬。subtract 和 multiply 返回新对象,不修改原对象,所以 record 的 lastReading 值需要在赋值后重新用于计算,这一点看代码时要留意。校验放在计算之后,是为了在负数用量出现时直接阻断后续的写库操作,避免污染月度报表。

4.3 缴费事务:更新状态和插入记录必须一起成功

缴费动作表面是「把未缴改成已缴」,实际包含两步写库:往 payment_record 插一条缴费流水,再把对应的水费或电费账单状态置为已缴。这两步必须在一个事务里完成,否则会出现「钱交了但账单还是未缴」的状态。JDBC 原生写法长这样:

Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 // 1. 插入缴费流水 paymentDao.insert(conn, payment); // 2. 更新账单状态 billDao.updateStatus(conn, billType, billId, 1); conn.commit(); } catch (Exception e) { if (conn != null) { conn.rollback(); } throw e; } finally { DBUtil.close(conn); }

逻辑说明:关键是同一个 Connection 在多个 DAO 方法之间传递。很多原版代码里 DAO 方法内部自己 new Connection,事务就失效了,因为两个连接各自自动提交,把两步变成了两个独立事务。所以实现事务时,DAO 方法要支持传入外部 Connection,而不是在内部创建。答辩时被问「事务怎么保证的」,就按这个方案来答:手动关闭自动提交,多个写操作共享同一连接,异常时回滚。

4.4 月度统计:一条 SQL 配合日期函数写出按年月的报表

统计页面通常是首页的柱状图或表格,展示近 12 个月的水电收费总额。低效做法是在 Java 里循环查 12 次,每次查一个月;高效做法是让数据库一次聚合,SQL 写好后前端只需要遍历一次:

SELECT DATE_FORMAT(pay_time, '%Y-%m') AS month, SUM(CASE WHEN bill_type = 1 THEN pay_amount ELSE 0 END) AS water_total, SUM(CASE WHEN bill_type = 2 THEN pay_amount ELSE 0 END) AS elec_total, COUNT(*) AS pay_count FROM payment_record WHERE pay_time >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH) GROUP BY DATE_FORMAT(pay_time, '%Y-%m') ORDER BY month;

这里用 CASE WHEN 加 SUM 实现行转列,把水费和电费合并到同一行返回,前端不用再做二次拼接。DATE_SUB 直接在数据库侧算截止时间,避免在 Java 里拼字符串再传参。GROUP BY 用 DATE_FORMAT,数据量不大时性能完全可以接受,不需要提前冗余一个月份字段来过度优化。把这套聚合 SQL 讲明白,比在页面里堆一堆循环逻辑更能体现数据库基本功。

5. 从 404 到中文乱码:这套项目最容易踩的六个排查点

这一节梳理的是这类 JSP 项目的高频事故单,每一条都按「现象—原因—解决」来写。照着这个顺序排查,效率最高,也避免在无关方向上浪费时间。

5.1 打开就是 404:部署路径与项目名不匹配

现象:Tomcat 启动无异常,但访问 http://localhost:8080/db_water_elec/login.jsp 报 404。 原因:多数是部署时的 Application context 与实际访问路径不一致,或者项目被打成了其他名字。浏览器地址栏的路径必须与部署配置里的 context 完全对应。 解决:先在 IDEA 的 Deployment 配置里确认 Application context,再把浏览器地址改成一致。不要只改地址栏硬试,那样试不出结果。更稳妥的做法是查看 Tomcat 的 webapps 目录下实际生成了哪个文件夹,目录名就是访问路径。要说明的是,项目名与 context 不匹配不属于代码 bug,属于部署配置问题,最容易在换电脑演示时冒出来。

5.2 中文全部变成问号:三层编码不一致

现象:登录后页面上的住户姓名全是「??」,新写入数据库的中文再读出来也是乱码。 原因:JSP 页面编码、Tomcat 请求解码、MySQL 连接字符集,三层只要有一层不是 UTF-8,整体就会乱。乱码问题根子上是「链路编码不一致」,不是某一处单独能解决的。 解决:页面头部保证有<%@ page contentType="text/html; charset=UTF-8" %>;GET 请求在 Tomcat 的 server.xml 中给 Connector 加 URIEncoding="UTF-8";JDBC URL 上的 characterEncoding=utf8 不要省略。改完重启再验证,不要只改一处,否则换个页面、换个接口照样翻车。用 MySQL 命令行直接查一次表里的中文,能快速定位是写入前乱码还是读取后乱码。

5.3 JSP 页面编译报错:JDK/Tomcat 版本失配

现象:src 下 Java 代码没有红叉,但 JSP 文件被标记大量错误,或访问页面时出现 500 错误并提示 Unable to compile class for JSP。 原因:高版本 Tomcat 把包名从 javax.servlet 换成了 jakarta.servlet,旧代码里的 import 全部失效。这类 JSP 项目是「版本敏感」的,基线不对会冒出一堆看着像代码问题的编译错。 解决:把 Tomcat 换回 8.5/9,或者全局替换所有 import 里的包名。替换包名工作量不小,且容易漏掉 web.xml 头部的命名空间,所以我的建议是先退回旧版 Tomcat 跑通项目,再谈升级。这个坑在拿到别人的项目时尤其常见,先确认对方用的是哪个版本再动手。

5.4 数据库连接失败:驱动类名、URL、端口三个层面

现象:启动后任何访问数据库的功能都报 Communications link failure,或直接 ClassNotFoundException。 原因:要么驱动 jar 没放进 WEB-INF/lib,要么驱动类名与 MySQL 版本不匹配,要么 URL 里的端口不是实际端口。 解决:先看 WEB-INF/lib 里是否真的有 mysql-connector-java 的 jar 包;再把驱动类名按 MySQL 版本对应;最后确认端口。为了快速圈定问题,可以在 IDE 里写一个 20 行左右的 main 方法,直接用 JDBC 连一次数据库。能连上就说明问题在 Web 层,连不上就是数据库层。分层排查比盯着 Tomcat 日志猜要快得多。

5.5 演示视频里的数据和本机对不上:以 SQL 初始化文件为准

现象:按演示视频里的账号登录,提示密码错误;或者视频里有好多住户数据,自己导入后的页面空空如也。 原因:演示视频是作者在自己电脑上录的,视频里的数据来自他本地的测试数据库;你导入的 SQL 文件才是本机数据的唯一来源。视频与文本资料不一致,是这类压缩包的老毛病,不值得花时间考据。 解决:优先查 SQL 文件里的 INSERT 语句,找到原始用户名和密码,通常注释就在语句上方;想复原视频里的展示效果,就把视频里出现的住户信息手动 INSERT 进自己的数据库再演示。这个坑表面是数据问题,本质是排查思路问题——以代码和 SQL 为准,而不是以视频画面为准。

5.6 8080 端口被占用:改端口还是关进程

现象:启动 Tomcat 时直接报错,提示 Port 8080 required by Tomcat ... is already in use。 原因:本机其他服务占了 8080,可能是另一个 Tomcat,也可能是其他开发服务。 解决:两个方向。要保留 8080 就找到占用进程并结束它,Windows 下 netstat -ano 查 PID 再用 taskkill,macOS 或 Linux 下用 lsof -i:8080;不想动现有服务就改 Tomcat 端口,在 conf/server.xml 里把 8080 改成 8081,访问地址也要同步改。注意:改端口后演示视频里的访问地址会失效,如果答辩时要跟视频保持一致,优先关掉占用进程而不是改端口。

6. 把源码变成答辩作品:演示前的功能核验清单与两段讲解词

代码能跑只是第一步,毕业设计的核心在演示和讲答。建议在答辩前按下面的清单完整走一遍:管理员能登录;住户能增删改查;能对同一住户的不同月份分别录入水表和电表读数;录入后自动生成的账单金额与手算一致;未缴账单能缴费,缴费后状态立即变为已缴;统计页的数据与手动汇总一致。按这个顺序演示,正好对应从数据库到业务再到报表的递进,演示节奏也最自然。

演示时不要一上来讲代码,先讲「我做了什么」,再在操作每个功能前报一句目的。被问到「为什么用 JSP 不用 Spring Boot」时,建议这样答:这套设计的目的是完整呈现请求从浏览器到 Servlet 再到 DAO 的路径,JSP/Servlet 是这条路径的最小载体,项目内部没有省略分层,换成 Spring 也只是更换容器与装配方式。被问到「账单金额怎么保证不错」时,答:金额计算全程使用 BigDecimal,上期读数由系统自动从历史记录带出,不是手工填写;缴费动作处于同一个数据库事务中,任一步失败都会回滚。这两句比任何花哨框架都更能证明工程意识。

有一点建议放在最后说:演示前至少完整走两遍流程,尤其要把退费或修改读数这类分支操作测一遍。我见过不少项目平时能跑,演示时因为提前造好的数据被手工改错而当场翻车。准备一个只有几行数据的干净演示库,所有功能都在这套数据上跑过一遍,比临场找数据可靠得多。希望这些经验能帮到你,祝你答辩顺利。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 6:22:28

上海GEO营销系统技术选型解析|以盾码无界公开路线为例,梳理企业知识库、检索增强生成、内容审核与监测证据链,区分模块能力和项目交付表现,明确接口、权限及试点核验边界

摘要&#xff1a;进入2026年第四季度&#xff0c;企业搜索“上海GEO营销公司哪家好”&#xff0c;真正需要判断的已不只是品牌能否出现在AI答案中&#xff0c;而是服务商能否说明出现的原因、信息是否准确、内容能否持续更新&#xff0c;以及后续咨询和交易由谁承接。本文以盾码…

作者头像 李华
网站建设 2026/10/10 6:22:17

绵阳锅炉维修除尘设备检修靠谱服务商筛选名录

锅炉维修除尘设备检修&#xff1a;从原理认知到靠谱服务商筛选全指南 锅炉及除尘设备的核心运行逻辑与维护必要性 锅炉作为承压类特种设备&#xff0c;是通过燃料燃烧将化学能转化为热能&#xff0c;进而产出蒸汽或热水的核心设备&#xff0c;广泛应用于绵阳本地的工业生产、食…

作者头像 李华
网站建设 2026/10/10 6:21:54

SpringBoot+Vue商城源码深度拆解:数据库设计、订单链路与部署避坑

拿到一套SpringBootVue的商城源码&#xff0c;最怕的不是代码看不懂&#xff0c;而是不知道从哪一行看起。这套米家风格的商城系统我前前后后跑过两遍&#xff0c;第一遍踩了不少环境坑&#xff0c;第二遍才算把前后端的数据流转、订单状态、库存扣减这些核心链路彻底吃透。技术…

作者头像 李华
网站建设 2026/10/10 6:21:44

2026薪酬调研报告怎么读?从分位值到调薪预算的实操指南

每年年底一到&#xff0c;圈子里就开始流传各种版本的“2026年企业薪酬调研报告”&#xff0c;有第三方平台挂出来的摘要&#xff0c;也有机构售卖的完整版&#xff0c;还有HR群里转来转去的截图。我每年都会认真刷一遍这类报告&#xff0c;但说句实话&#xff0c;大多数人是把…

作者头像 李华
网站建设 2026/10/10 6:21:08

AIGC检测轻松过!2026这3款降AI率网站太省心了!

谁还在为AI生成论文的AI率太高发愁&#xff1f;明明用AI省了时间&#xff0c;结果查重时AIGC率超标&#xff0c;直接被老师打回重写&#xff0c;熬夜改到崩溃真的太窒息了&#xff01;最近被问最多的就是“有没有可以自动降AI率的论文生成工具”&#xff0c;作为过来人&#xf…

作者头像 李华
网站建设 2026/10/10 6:20:24

这种水平的成本分析表,才是财务该做的!

很多财务做成本分析&#xff0c;最后交出去的还是一张Excel&#xff1a;本月成本多少、上月多少、预算多少、差异多少&#xff0c;再用红绿箭头标一下增减。表做得很满&#xff0c;领导看完却还会继续追问&#xff1a;为什么涨了&#xff1f;到底涨在哪&#xff1f;是采购价格的…

作者头像 李华