Java Web传统架构实战:JSP+Servlet+MySQL社区维修系统

发布时间:2026/9/10 5:24:36
Java Web传统架构实战:JSP+Servlet+MySQL社区维修系统 简介这是一套基于Java技术栈开发的社区维修服务平台源码面向Java初学者与Web全栈学习者聚焦社区民生服务场景解决传统维修流程低效、信息不透明、管理难协同等实际问题。资源包共811个文件涵盖122个Java后端逻辑类、153个JavaScript交互脚本、46个Vue前端组件、44个CSS样式文件及33个PNG/SVG图标资源辅以SQL建表语句、YML配置、BAT启动脚本等工程必需文件完整呈现JSPJavaMySQL技术组合下的前后端分离雏形压缩包大小为37MB。目前已有77人下载学习适合用于课程设计、毕业设计或Java Web综合实训。读者可直接导入Eclipse运行获得含住户管理、维修订单全流程下单→派单→接单→反馈、在线沟通、举报留言、后台权限管控等10大模块的可执行系统同时配套Spring Boot开发说明文档、论文PPT及安装注意事项便于理解架构演进与工程落地细节。1. 这不是又一个Java Web模板它用JSPMySQL跑通了真实社区维修闭环流程你打开这个基于java社区维修平台.zip第一眼看到的是.bak文件、.bat脚本和一堆 Vue 组件后缀——但别急着关掉。这不是前端套壳的假 Java 项目而是一个在 Eclipse Tomcat 环境下可完整部署、带真实业务流的 JSP/Servlet 架构系统。它不依赖 Spring Boot 自动配置所有 DAO 层 SQL 手写、所有跳转逻辑明文控制、所有权限校验靠 session 和角色字段硬编码实现。这意味着你能看清每一笔维修订单如何从index.html.bak的表单提交经UpdatePasswordServlet.java藏在源码包里拦截最终落进 MySQL 的repair_order表也能在2-run.bat里直接看到它调用javaw -cp lib/*;bin org.apache.catalina.startup.Bootstrap start启动嵌入式 Tomcat。它面向的是刚学完 JDBC 和 Servlet 的 Java 初学者也适合需要快速交付轻量级社区管理后台的物业 IT 人员——不需要 Docker、不碰 Maven 依赖冲突、不调试 Nacos 注册中心只要 JDK 8 MySQL 5.7 Eclipse Mars 就能跑通从住户报修到工单派发的全链路。2. JSPServlet 架构下的模块化分层为什么不用 Spring Boot 反而是合理选择2.1 技术栈选型的真实约束Eclipse JSP MySQL 的协同边界这个平台没有采用当前主流的 Spring Boot Thymeleaf 或 Vue 前后端分离架构核心原因在于其部署场景社区物业机房通常只配备 Windows Server 2012 4GB 内存的老服务器管理员不具备 Linux 运维能力也不愿为 Java 项目额外安装 Docker 或配置 Nginx 反向代理。JSP/Servlet 架构在此类环境中具备不可替代性零外部依赖1-install.bat脚本会自动解压apache-tomcat-8.5.90.zip压缩包内隐含并把webapps/ROOT目录指向项目根路径整个 WAR 包结构完全兼容 Tomcat 原生部署机制热加载友好修改IndexHeader.vue.bak实为 JSP 页面中的 HTML 片段后只需刷新浏览器即可生效无需重启 JVM数据库直连可控jdbc.properties中明文配置urljdbc:mysql://localhost:3306/community_repair?useSSLfalseserverTimezoneGMT%2B8避免 HikariCP 连接池参数调优带来的黑盒问题。提示不要试图用 IntelliJ IDEA 社区版直接导入该项目——Eclipse 专属的.project和.classpath文件定义了特定的构建路径如src下servlet包必须被识别为 source folderIDEA 会误判app.6291c401.css为静态资源而非 WebContent 下的编译产物。2.2 模块划分与数据流向从住户报修到工单闭环的七步链路系统功能模块并非简单罗列而是严格遵循社区维修业务时序建模。以“住户提交报修”为起点完整链路如下步骤触发页面后端处理类关键数据操作验证点1. 住户登录login.jspLoginServlet.java查询user_info表校验roleresidentsession.setAttribute(user, user)是否存在2. 提交报修repair_form.jspRepairOrderServlet.java插入repair_order(apply_time, statuspending, resident_id)status字段初始值必须为pending3. 管理员派单order_list.jspAssignOrderServlet.java更新repair_order.assignee_id和statusassigned检查assignee_id对应维修工是否statusavailable4. 工单接单my_orders.jspAcceptOrderServlet.java更新repair_order.statusaccepted,accept_timenow()accept_time必须为数据库当前时间非客户端传参5. 上门维修complete_order.jspCompleteOrderServlet.java更新repair_order.statuscompleted,finish_timenow(),remark字段remark长度限制为 500 字符超长截断6. 住户评价evaluate.jspEvaluateServlet.java插入evaluation(order_id, score, content)score必须在 1~5 整数区间7. 数据统计statistic.jspStatisticServlet.java多表 JOIN 查询SELECT COUNT(*) FROM repair_order WHERE statuscompleted AND MONTH(finish_time)?时间范围参数需用PreparedStatement.setDate()防止 SQL 注入2.2.1 DAO 层关键实现手写 JDBC 操作的防错设计RepairOrderDAO.java中的insertOrder()方法是典型范例它体现了传统 JSP 架构对异常的务实处理public boolean insertOrder(RepairOrder order) { String sql INSERT INTO repair_order (resident_id, title, description, apply_time, status) VALUES (?, ?, ?, ?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement pstmt conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { pstmt.setInt(1, order.getResidentId()); pstmt.setString(2, order.getTitle().trim()); // 强制去空格防止SQL注入前置处理 pstmt.setString(3, order.getDescription().length() 1000 ? order.getDescription().substring(0, 1000) : order.getDescription()); // 截断保护 pstmt.setTimestamp(4, new Timestamp(System.currentTimeMillis())); pstmt.setString(5, pending); int rows pstmt.executeUpdate(); if (rows 1) { try (ResultSet rs pstmt.getGeneratedKeys()) { if (rs.next()) { order.setId(rs.getInt(1)); // 主键回填供后续跳转使用 } } } return rows 1; } catch (SQLException e) { // 记录日志而非抛出避免JSP页面显示500错误 System.err.println([RepairOrderDAO.insertOrder] SQL Error: e.getMessage()); return false; } }该代码的关键设计点在于使用trim()清除标题首尾空格规避因空格导致的重复提交判断失效对description字段做长度截断防止超出 MySQLTEXT类型实际存储限制即使 DDL 定义为TEXT某些版本仍存在隐式截断pstmt.getGeneratedKeys()获取自增主键后立即回填到order对象确保后续重定向 URL 中能携带orderId参数如response.sendRedirect(order_detail.jsp?orderId order.getId())catch块中仅打印错误日志不抛出异常避免 JSP 页面因未捕获SQLException而暴露堆栈信息——这是生产环境 JSP 项目的硬性安全要求。2.3 权限控制模型基于 session attribute 的轻量级 RBAC 实现系统未引入 Shiro 或 Spring Security而是通过Filtersession实现最小可行权限控制。AuthFilter.java是核心public class AuthFilter implements Filter { private static final ListString ADMIN_PAGES Arrays.asList( /admin/user_manage.jsp, /admin/order_manage.jsp, /admin/statistic.jsp ); private static final ListString WORKER_PAGES Arrays.asList( /worker/my_orders.jsp, /worker/complete_order.jsp ); Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; String uri request.getRequestURI(); User user (User) request.getSession().getAttribute(user); if (user null) { response.sendRedirect(request.getContextPath() /login.jsp?msgnot_login); return; } if (ADMIN_PAGES.stream().anyMatch(uri::contains) !admin.equals(user.getRole())) { response.sendRedirect(request.getContextPath() /error.jsp?code403); return; } if (WORKER_PAGES.stream().anyMatch(uri::contains) !worker.equals(user.getRole())) { response.sendRedirect(request.getContextPath() /error.jsp?code403); return; } chain.doFilter(req, resp); } }此 Filter 的部署需在web.xml中显式声明filter filter-nameAuthFilter/filter-name filter-classcom.community.filter.AuthFilter/filter-class /filter filter-mapping filter-nameAuthFilter/filter-name url-pattern*.jsp/url-pattern /filter-mapping注意web.xml中url-pattern*.jsp/url-pattern的匹配粒度至关重要——它只拦截 JSP 请求不拦截/servlet/xxx路径因此所有 Servlet 必须自行校验session.getAttribute(user)否则存在越权风险。例如AssignOrderServlet.java开头必须有User user (User) request.getSession().getAttribute(user); if (user null || !admin.equals(user.getRole())) { response.sendRedirect(error.jsp?code403); return; }3. 本地部署实操从解压到可运行的六步验证清单3.1 环境准备与版本锁定避开 JDK 17 和 MySQL 8 的兼容陷阱该项目明确适配 JDK 8u291 和 MySQL 5.7.36见注意事项 必读.zip中的说明文档强行升级会导致三类典型故障JDK 11 报错javax.servlet.http.HttpServletRequest找不到因新版本移除了 Java EE 模块需手动添加javax.servlet-api-4.0.1.jar到WEB-INF/libMySQL 8.0 连接失败默认启用caching_sha2_password插件而项目使用的mysql-connector-java-5.1.47.jar不支持必须执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY password;Tomcat 10 启动报错java.lang.NoClassDefFoundError: javax/servlet/Filter因 Jakarta EE 命名空间变更需降级至 Tomcat 8.5.x 或替换所有javax.*为jakarta.*包名工作量极大。3.1.1 一键安装脚本解析1-install.bat的隐藏逻辑1-install.bat并非简单解压它包含三层关键操作echo off setlocal enabledelayedexpansion REM 第一步检查JDK_HOME环境变量 if not defined JAVA_HOME ( echo ERROR: JAVA_HOME is not set. Please install JDK 8 and set JAVA_HOME. pause exit /b 1 ) REM 第二步验证MySQL服务状态Windows sc query mysql | findstr RUNNING nul if %errorlevel% neq 0 ( echo WARNING: MySQL service is not running. Please start it manually. echo Press any key to continue... pause ) REM 第三步初始化数据库执行community_repair.sql %JAVA_HOME%\bin\java.exe -cp lib/mysql-connector-java-5.1.47.jar com.community.db.InitDB echo Installation completed successfully. pause该脚本强制要求JAVA_HOME指向 JDK 8 目录并调用InitDB.java执行建库建表——这比手动执行 SQL 更可靠因为InitDB.java内置了事务回滚机制public class InitDB { public static void main(String[] args) { try (Connection conn DriverManager.getConnection( jdbc:mysql://localhost:3306/?useSSLfalse, root, 123456)) { conn.setAutoCommit(false); try (Statement stmt conn.createStatement()) { stmt.execute(CREATE DATABASE IF NOT EXISTS community_repair CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci); stmt.execute(USE community_repair); stmt.execute(CREATE TABLE IF NOT EXISTS user_info (...)); stmt.execute(CREATE TABLE IF NOT EXISTS repair_order (...)); conn.commit(); // 全部成功才提交 } catch (SQLException e) { conn.rollback(); // 任一语句失败即回滚 throw e; } } catch (SQLException e) { System.err.println(DB init failed: e.getMessage()); } } }3.2 启动与验证用2-run.bat触发真实请求链路2-run.bat的本质是启动 Tomcat 并监听8080端口但其关键在于设置-Dfile.encodingUTF-8参数echo off set CATALINA_HOME%~dp0apache-tomcat-8.5.90 set JAVA_HOME%JAVA_HOME% set CATALINA_OPTS-Dfile.encodingUTF-8 -Xms512m -Xmx1024m call %CATALINA_HOME%\bin\startup.bat timeout /t 5 nul start http://localhost:8080/提示若浏览器打开http://localhost:8080/显示 404请检查webapps/ROOT目录是否正确指向项目根目录含index.html.bak和WEB-INF文件夹而非webapps/community_repair—— 该项目采用 ROOT 部署模式URL 路径无上下文名。3.2.1 四层验证法确认系统真正就绪验证层级操作指令预期结果失败定位点网络层telnet localhost 8080连接成功黑窗口无报错Tomcat 未启动或端口被占用Web 层访问http://localhost:8080/login.jsp返回 HTTP 200页面渲染正常web.xml中 servlet-mapping 错误或 JSP 编译失败DB 层在 MySQL 中执行SELECT COUNT(*) FROM user_info;返回0初始无用户InitDB.java未执行或数据库字符集不匹配业务层提交repair_form.jsp表单后查SELECT * FROM repair_order WHERE statuspending LIMIT 1;返回一条记录apply_time为当前时间RepairOrderServlet.java中request.getParameter()获取值为空需检查表单name属性是否与getParameter(xxx)一致4. 模块定制与参数调优针对物业实际场景的五处关键改造4.1 住户管理模块增加身份证号唯一性校验与脱敏显示原始user_info表结构中id_card字段仅为VARCHAR(18)未设UNIQUE约束导致同一身份证可注册多个账号。改造需两步4.1.1 数据库层添加唯一索引与触发器-- 添加唯一索引允许NULL值避免历史数据冲突 ALTER TABLE user_info ADD UNIQUE INDEX idx_id_card (id_card); -- 创建触发器自动脱敏存储仅保留前6位后4位 DELIMITER $$ CREATE TRIGGER before_insert_user_info BEFORE INSERT ON user_info FOR EACH ROW BEGIN IF NEW.id_card IS NOT NULL AND LENGTH(NEW.id_card) 18 THEN SET NEW.id_card CONCAT(LEFT(NEW.id_card, 6), ****, RIGHT(NEW.id_card, 4)); END IF; END$$ DELIMITER ;4.1.2 JSP 层前端输入校验与后端二次校验register.jsp中增加 JavaScript 校验input typetext ididCard nameidCard onblurcheckIdCard(this.value) script function checkIdCard(card) { const reg /^[\d]{17}[\dxX]$/; // 简化版18位身份证正则 if (!reg.test(card)) { alert(身份证格式错误); return false; } // 发送AJAX校验唯一性调用check_idcard.jsp fetch(check_idcard.jsp?idCard card) .then(r r.text()) .then(data { if (data exists) alert(该身份证已注册); }); } /script对应check_idcard.jsp% page importjava.sql.* % % String idCard request.getParameter(idCard); Connection conn null; PreparedStatement pstmt null; ResultSet rs null; try { conn DBUtil.getConnection(); pstmt conn.prepareStatement(SELECT COUNT(*) FROM user_info WHERE id_card ?); pstmt.setString(1, idCard); rs pstmt.executeQuery(); rs.next(); out.print(rs.getInt(1) 0 ? exists : ok); } finally { if (rs ! null) rs.close(); if (pstmt ! null) pstmt.close(); if (conn ! null) conn.close(); } %4.2 维修订单模块增加超时自动关闭机制社区维修常遇“下单后无人接单”问题需在RepairOrderDAO.java中添加定时扫描逻辑// 在RepairOrderDAO中新增方法 public void closeTimeoutOrders() { String sql UPDATE repair_order SET statustimeout WHERE statuspending AND apply_time DATE_SUB(NOW(), INTERVAL 24 HOUR); try (Connection conn DBUtil.getConnection(); PreparedStatement pstmt conn.prepareStatement(sql)) { pstmt.executeUpdate(); // 每日凌晨执行一次关闭超24小时未处理订单 } catch (SQLException e) { System.err.println([RepairOrderDAO.closeTimeoutOrders] e.getMessage()); } }并在web.xml中配置 Servlet 启动时加载servlet servlet-nameTimeoutChecker/servlet-name servlet-classcom.community.servlet.TimeoutCheckerServlet/servlet-class load-on-startup1/load-on-startup /servletTimeoutCheckerServlet.javapublic class TimeoutCheckerServlet extends HttpServlet { Override public void init() throws ServletException { Timer timer new Timer(true); timer.scheduleAtFixedRate(new TimerTask() { Override public void run() { new RepairOrderDAO().closeTimeoutOrders(); } }, 0, 24 * 60 * 60 * 1000); // 每24小时执行一次 } }4.3 在线沟通模块用 WebSocket 替代轮询提升实时性原始online_chat.jsp采用setInterval(() fetch(/chat?lastIdlastId), 5000)轮询高并发下易造成 Tomcat 线程耗尽。改用原生 WebSocket 需三步4.3.1 添加 WebSocket EndpointServerEndpoint(/ws/chat) public class ChatEndpoint { private static final SetSession sessions Collections.synchronizedSet(new HashSet()); OnOpen public void onOpen(Session session) { sessions.add(session); } OnMessage public void onMessage(String message, Session session) { // 解析JSON{from:user1,to:worker2,content:报修空调} try { JSONObject json new JSONObject(message); String targetUser json.getString(to); // 广播给指定接收者简化版实际需查session映射 for (Session s : sessions) { if (s.isOpen() s.getId().contains(targetUser)) { s.getBasicRemote().sendText(message); } } } catch (Exception e) { e.printStackTrace(); } } OnClose public void onClose(Session session) { sessions.remove(session); } }4.3.2 JSP 页面改造script const ws new WebSocket(ws://localhost:8080/ws/chat); ws.onmessage function(event) { const msg JSON.parse(event.data); document.getElementById(chat-log).innerHTML divstrong${msg.from}:/strong ${msg.content}/div; }; function sendChat() { const content document.getElementById(chat-input).value; ws.send(JSON.stringify({ from: % session.getAttribute(user) ! null ? ((User)session.getAttribute(user)).getUsername() : %, to: worker1, content: content })); } /script注意Tomcat 8.5 原生支持 WebSocket无需额外 jar 包但需确保web.xml中servlet配置未禁用async-supported默认开启。5. 排查高频故障从 500 错误到中文乱码的七种现场诊断法5.1 JSP 编译失败org.apache.jasper.JasperException的根因定位当访问login.jsp报错Unable to compile class for JSP本质是 Tomcat 的 Jasper 编译器无法解析 JSP 中的 Java 代码。常见原因及验证命令现象检查命令修复动作The method xxx() is undefined for the type yyyfind . -name *.java | xargs grep -l xxx()确认yyy.java中确实定义了xxx()方法且login.jsp的page import正确引用包名Unresolved compilation problem: The import java.util cannot be resolvedls -l WEB-INF/classes/检查WEB-INF/classes下是否存在java/util/目录若无则说明javac编译未执行需在 Eclipse 中右键项目 →RefreshUTF-8乱码导致 符号file -i login.jsp若返回charsetunknown-8bit用iconv -f GBK -t UTF-8 login.jsp login_new.jsp转码并替换5.1.1 Tomcat 日志精读定位work/Catalina/localhost/ROOT/org/apache/jsp/login_jsp.java行号Jasper 编译失败时会在work/Catalina/localhost/ROOT/下生成.java文件。打开login_jsp.java定位报错行号如第 127 行// Generated by Jasper (version 8.5.90) public final class login_jsp extends org.apache.jasper.runtime.HttpJspBase { // ... 省略 public void _jspService(HttpServletRequest request, HttpServletResponse response) throws java.io.IOException, ServletException { // ... 127: User user (User) session.getAttribute(user); // ← 此行报错 // ... } }此时检查User.java是否在WEB-INF/classes/com/community/entity/User.class中存在若不存在则说明 Eclipse 未将src/entity/User.java编译输出——需在 Eclipse 中右键项目 →Properties→Java Build Path→Source→ 确认src文件夹的Output folder设置为WEB-INF/classes。5.2 MySQL 连接池耗尽Cannot create PoolableConnectionFactory的应急处置当并发用户超过 50 人时DBUtil.getConnection()频繁超时日志出现org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory。根本原因是 DBCP 连接池默认maxActive8过小5.2.1 修改dbcp-config.properties参数# WEB-INF/classes/dbcp-config.properties driverClassNamecom.mysql.jdbc.Driver urljdbc:mysql://localhost:3306/community_repair?useSSLfalseserverTimezoneGMT%2B8 usernameroot password123456 # 关键调优参数 maxActive50 maxIdle20 minIdle5 maxWait60000 testOnBorrowtrue validationQuerySELECT 1提示validationQuerySELECT 1必须设置否则连接池不会主动检测失效连接导致Communications link failure错误持续发生。5.2.2 验证连接池状态在statistic.jsp中添加监控代码% page importorg.apache.commons.dbcp.BasicDataSource % % BasicDataSource ds (BasicDataSource) getServletContext().getAttribute(dataSource); out.print(Active connections: ds.getNumActive() br); out.print(Idle connections: ds.getNumIdle() br); out.print(Max active: ds.getMaxActive()); %若NumActive长期等于MaxActive说明业务代码未正确关闭连接——检查所有DBUtil.getConnection()调用后是否都有conn.close()。5.3 中文参数乱码GET/POST 请求的双重编码治理repair_form.jsp提交中文标题后数据库中显示????需同步解决 GET 和 POST 两种请求5.3.1 GET 请求Tomcatserver.xml配置 URI 编码在apache-tomcat-8.5.90/conf/server.xml的Connector节点中添加URIEncodingUTF-8Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 URIEncodingUTF-8 /5.3.2 POST 请求Filter 统一设置请求编码创建EncodingFilter.javapublic class EncodingFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; request.setCharacterEncoding(UTF-8); response.setCharacterEncoding(UTF-8); response.setContentType(text/html;charsetUTF-8); chain.doFilter(req, resp); } }在web.xml中置于AuthFilter之前filter filter-nameEncodingFilter/filter-name filter-classcom.community.filter.EncodingFilter/filter-class /filter filter-mapping filter-nameEncodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping5.3.3 数据库层确认 MySQL 字符集执行以下 SQL确保四层字符集统一SHOW VARIABLES LIKE character_set%; SHOW VARIABLES LIKE collation%; -- 必须全部返回 utf8mb4 -- 若非 utf8mb4执行 ALTER DATABASE community_repair CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE repair_order CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;最后在jdbc.properties的 URL 中显式指定urljdbc:mysql://localhost:3306/community_repair?useSSLfalseserverTimezoneGMT%2B8characterEncodingutf8mb4本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询