JavaWeb家政系统:Servlet+JSP+MySQL全流程实战指南
简介本资源是一套完整的JavaWeb家政服务管理系统毕业设计作品面向计算机专业本科生及Java初学者解决传统家政服务信息分散、供需匹配低效、管理流程不规范等实际问题。压缩包共3626个文件涵盖1592个JavaScript交互脚本、533个PNG界面图标、219个CSS样式文件、205个JPG宣传图、62个JSP页面及45个Java核心业务类辅以SQL数据库脚本、系统运行视频AVI、多模块Action控制器类及完整论文文档整体大小44.26MB。已有2317人学习下载资源结构清晰按登录、用户管理、家政人员/雇主双端服务、资讯与留言模块分层组织配套项目运行视频录制与详细功能测试用例便于读者快速理解B/S架构下MVC模式的落地实现掌握MySQL数据建模、JSPServlet开发及前后端协同调试全流程。1. 这不是又一个“学生管理系统”JavaWeb家政服务系统真能跑通MySQLTomcatJSP全流程你手头那份标着“毕业设计.rar”的压缩包大概率正躺在某个课程资料群的聊天记录底部被当成“水作业的救命稻草”。但我要说句实话这个家政服务系统是少数几个从登录页到留言管理、从用户注册到家政人员排班全链路走通且数据库表结构经得起推敲的JavaWeb实战项目。它没用Spring Boot偷懒硬是用原生ServletJSPMySQLTomcat 7/8 搭出B/S架构闭环——这意味着你能看清每个request怎么进Filter、session怎么存用户角色、JDBC连接池怎么配、甚至ckplayer.cer证书怎么嵌进视频播放模块。它适合两类人一是刚学完JDBC和MVC分层、想验证自己能不能把书上零散知识点串成完整系统的初学者二是需要快速搭建一个可演示、有真实业务字段如家政人员技能标签、服务价格区间、雇主信用分、能应付答辩提问的毕设党。别被“家政”二字劝退——它的核心逻辑多角色权限、服务发布-预约-评价、资讯CMS完全可迁移到物业、维修、陪诊等轻服务类系统。2. 从解压到首页渲染五步跑通JavaWeb家政系统运行环境2.1 环境清单与版本对齐为什么必须用Tomcat 7.0.96而不是最新版这个项目源码里所有web.xml的schema声明、servlet配置写法、甚至AppAction.class编译字节码版本都锚定在Java 1.7 Servlet 3.0规范。我试过直接丢进Tomcat 10结果连login.jsp都报404——因为Tomcat 10默认使用Jakarta EE命名空间而项目里所有import javax.servlet.*全得改成jakarta.servlet.*牵一发而动全身。正确做法是# 下载并解压指定版本官网archive区可查 wget https://archive.apache.org/dist/tomcat/tomcat-7/v7.0.96/bin/apache-tomcat-7.0.96.zip unzip apache-tomcat-7.0.96.zip export CATALINA_HOME/path/to/apache-tomcat-7.0.96提示CATALINA_HOME必须指向解压后的根目录不是bin子目录Windows用户请用set CATALINA_HOME...且确保路径无中文空格。2.2 数据库初始化三张关键表决定你能否看到首页轮播图项目没提供SQL脚本别慌——数据库设计章节的3.2.3小节明确列出了12张表但真正启动首页必需的是user用户基础信息、admin管理员账号和content首页资讯轮播数据。我从ContentAction.class反编译确认了查询逻辑SELECT * FROM content WHERE status1 ORDER BY sort DESC LIMIT 5。所以建库时至少要插入这三条-- 创建数据库字符集必须为utf8mb4否则中文乱码 CREATE DATABASE home_service CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 插入首页轮播内容id, title, img_path, url, sort, status INSERT INTO content VALUES (1, 金牌月嫂服务, /upload/img/yuesao.jpg, #, 1, 1), (2, 深度保洁套餐, /upload/img/baojie.jpg, #, 2, 1), (3, 老人陪护专线, /upload/img/peihu.jpg, #, 3, 1); -- 插入默认管理员账号admin密码123456角色role0 INSERT INTO admin VALUES (1, admin, e10adc3949ba59abbe56e057f20f883e, 系统管理员, 13800138000, 0);注意密码字段是MD5加密存储e10adc3949ba59abbe56e057f20f883e对应明文123456img_path路径必须与项目中/upload/img/物理目录一致否则轮播图显示为裂图。2.3 WAR包构建用最原始的方式打包避开IDE自动配置陷阱别急着用IDEA点“Add Framework Support”——这个项目.class文件已编译好直接扔进WEB-INF/classes就行。标准流程是# 1. 创建标准目录结构 mkdir -p home_service/WEB-INF/{classes,lib} mkdir -p home_service/upload/{img,video,doc} # 2. 复制编译好的class文件注意包路径 cp -r src/com/ home_service/WEB-INF/classes/ # 实际需按项目src目录结构复制常见路径com/action/AppAction.class → home_service/WEB-INF/classes/com/action/AppAction.class # 3. 复制依赖jar从lib目录取重点mysql-connector-java-5.1.47.jar、commons-fileupload-1.3.1.jar cp lib/*.jar home_service/WEB-INF/lib/ # 4. 复制前端资源js/css/img必须放对位置 cp -r WebRoot/* home_service/ # 5. 打包成WARLinux/macOS用zipWindows用PowerShell压缩为ZIP后改后缀 cd home_service zip -r ../home_service.war .关键细节web.xml必须放在WEB-INF/下且welcome-file-list中指定index.jspckplayer.cer证书文件要放在WEB-INF/classes/下VideoAction.class会通过getClass().getResourceAsStream(/ckplayer.cer)加载。2.4 Tomcat部署与端口校验两个配置文件决定你能否访问localhost:8080部署不是把WAR丢进webapps就完事。先检查conf/server.xml!-- 确保Connector端口是8080且redirectPort8443 -- Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 / !-- 关键添加URIEncodingUTF-8否则GET参数中文乱码 -- Connector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 URIEncodingUTF-8 /再检查conf/context.xml确保启用JNDI数据源项目用JDBC直连但部分Action可能调用!-- 在Context标签内添加 -- Resource namejdbc/homeService authContainer typejavax.sql.DataSource maxActive100 maxIdle30 maxWait10000 usernameroot passwordyour_password driverClassNamecom.mysql.jdbc.Driver urljdbc:mysql://localhost:3306/home_service?useUnicodetrueamp;characterEncodingutf8/血泪经验如果首页CSS不生效90%是web.xml里welcome-file没写对或index.jsp里引用的/css/style.css路径与实际WebRoot/css/物理路径不匹配用浏览器开发者工具Network面板看404请求路径比猜强十倍。2.5 首次访问调试从404到登录框的三次关键日志定位启动Tomcat后别急着开浏览器。先盯住logs/catalina.outtail -f $CATALINA_HOME/logs/catalina.out现象1启动成功但访问http://localhost:8080/home_service返回404→ 原因WAR包名home_service.war解压后生成home_service目录但server.xml中Host appBasewebapps未配置Context映射→ 解决在conf/Catalina/localhost/下新建home_service.xml内容为Context docBase/path/to/home_service reloadabletrue/现象2首页打开但轮播图空白控制台报Uncaught ReferenceError: $ is not defined→ 原因index.jsp引入jQuery路径错误项目用的是/js/jquery-1.8.3.min.js但实际文件在WebRoot/js/下→ 解决检查WebRoot/js/是否存在该文件若无则从CDN下载并放对位置。现象3点击登录按钮无反应Network面板显示login.action404→ 原因web.xml中servlet-mapping的url-pattern写成/login.action但AppAction.class实际映射的是/app/login→ 解决打开web.xml找到servlet-mapping将url-pattern/login.action/url-pattern改为url-pattern/app/login/url-pattern。3. 多角色权限体系拆解Admin/User/Worker三层控制如何靠一个Action基类实现3.1 Action基类设计AppAction如何用getRole()统一拦截未授权访问翻开源码你会发现所有*Action.class如UserAction.class、AdminAction.class都继承自AppAction。这个基类不是摆设——它在execute()方法开头就做了三件事// AppAction.java 片段反编译还原 public String execute() { HttpServletRequest request ServletActionContext.getRequest(); HttpSession session request.getSession(false); // 1. 检查session是否存在 if (session null) return login; // 跳转登录页 // 2. 从session取用户角色key为user_role Integer role (Integer) session.getAttribute(user_role); if (role null) return login; // 3. 根据当前Action类型校验角色权限关键 String actionName this.getClass().getSimpleName(); // 如AdminAction if (actionName.contains(Admin) role ! 0) { request.setAttribute(error, 无管理员权限); return error; } if (actionName.contains(Worker) role ! 1) { request.setAttribute(error, 无家政人员权限); return error; } // UserAction允许role1或2家政人员/雇主 return doExecute(); // 子类实现的具体逻辑 }逻辑说明role0是管理员role1是家政人员role2是雇主。这种硬编码角色值虽不优雅但胜在清晰——你能在UserAction.class里直接看到if(role2)判断雇主操作比Spring Security的注解式权限更直观。3.2 用户登录流程从login.jsp到UserAction的三次跳转与Session写入登录不是简单查密码。整个流程涉及三个关键跳转login.jsp提交表单到/app/login注意不是/login.action表单form action/app/login methodpostname字段对应UserAction的private String name;属性框架自动注入。UserAction.execute()执行登录验证public String execute() { // 1. 根据name查数据库区分admin/user/worker三张表 User user userDao.findByName(name); // 实际代码会先查admin表再查user表... if (user ! null MD5Util.verify(password, user.getPassword())) { // 2. 写入session关键字段 session.setAttribute(user_id, user.getId()); session.setAttribute(user_name, user.getName()); session.setAttribute(user_role, user.getRole()); // role2代表雇主 return success; // 跳转到index.jsp } return login; // 密码错回登录页 }index.jsp顶部的权限判断c:choose c:when test${sessionScope.user_role 0} a hrefadmin/index.jsp后台管理/a /c:when c:when test${sessionScope.user_role 1} a hrefworker/index.jsp我的服务/a /c:when c:otherwise a hrefuser/index.jsp我的订单/a /c:otherwise /c:choose参数说明user_role是唯一权限开关user_id用于后续所有操作的条件查询如SELECT * FROM order WHERE user_id?user_name用于页面右上角显示。3.3 家政人员管理模块WorkerAction如何实现技能标签的多对多关联家政人员有“育儿”“养老”“保洁”多个技能数据库用三张表实现worker人员主表、skill技能字典、worker_skill关联表。WorkerAction新增人员时的关键代码// WorkerAction.java public String addWorker() { // 1. 先保存worker主表 int workerId workerDao.add(worker); // 返回自增ID // 2. 解析前端传来的skillIds参数格式1,3,5 String skillIds request.getParameter(skillIds); if (skillIds ! null !skillIds.trim().isEmpty()) { String[] ids skillIds.split(,); for (String id : ids) { // 3. 向关联表插入记录 workerSkillDao.add(workerId, Integer.parseInt(id)); } } return list; // 刷新列表页 }前端配合add_worker.jsp中技能用复选框组input typecheckbox nameskillIds value1 育儿 input typecheckbox nameskillIds value2 养老 input typecheckbox nameskillIds value3 保洁这样request.getParameter(skillIds)就能拿到逗号分隔字符串避免JSON解析开销。3.4 留言管理模块DocAction的分页查询为何用LIMIT ?,?而非MyBatis项目没用ORM框架所有SQL手写在DAO层。DocAction.list()方法调用docDao.findPage(startIndex, pageSize)其SQL为SELECT d.id, d.content, d.create_time, u.name as user_name FROM doc d LEFT JOIN user u ON d.user_id u.id ORDER BY d.create_time DESC LIMIT ?,?为什么不用MyBatis因为毕业设计强调底层可控性——startIndex由pageNum和pageSize计算int startIndex (pageNum - 1) * pageSize;。你在list_doc.jsp里能看到分页栏的c:forEach begin1 end${page.totalPage} ...page对象是DocAction塞进request的PageBean实例包含totalRecord、totalPage、currentPage等字段。这种手动分页虽笨重但让你彻底明白limit偏移量怎么算。3.5 视频播放模块VideoAction如何绕过浏览器跨域限制加载本地视频VideoAction.class不直接返回视频流而是返回一个含ckplayer.cer证书的播放页。关键逻辑public String playVideo() { String videoId request.getParameter(id); Video video videoDao.findById(videoId); // 将视频路径传给JSP注意不是绝对路径是相对WebRoot的路径 request.setAttribute(videoPath, /upload/video/ video.getFileName()); return play; // 跳转到play.jsp }play.jsp中ckplayer初始化div ida1/div script typetext/javascript src/js/ckplayer/ckplayer.js/script script typetext/javascript var flashvars { f: ${videoPath}, // 动态注入路径 c: 0, p: 1, e: 1, i: /images/preview.jpg }; var params { bgcolor: #FFF, allowFullScreen: true, allowScriptAccess: always }; CKobject.embed(/js/ckplayer/ckplayer.swf, a1, ckplayer_a1, 100%, 400, false, flashvars, params); /script注意ckplayer.cer是Flash播放器所需证书必须放在WEB-INF/classes/下若用Chrome 80访问需在地址栏输入chrome://flags/#unsafely-treat-insecure-origin-as-secure启用不安全源因为Flash已淘汰现代浏览器默认禁用。4. 避坑指南五个让90%新手卡住的致命细节与现场修复方案4.1 现象首页轮播图显示“图片加载失败”Network面板显示404 /upload/img/yuesao.jpg→ 原因/upload/目录不在Web应用根路径下而是Tomcat的独立静态资源目录项目期望/upload/是WebRoot的同级目录但默认部署时upload被当成了Web应用内部路径。→ 解决在conf/server.xml的Host标签内添加静态资源映射Context docBase/absolute/path/to/upload path/upload /然后把yuesao.jpg等文件放到/absolute/path/to/upload/img/下。Windows用户注意路径用正斜杠。4.2 现象登录成功后跳转index.jsp但右上角显示“欢迎null”→ 原因UserAction中session.setAttribute(user_name, user.getName())执行了但user.getName()返回null——因为数据库user表的name字段为空而login.jsp提交的name参数其实是用户名username不是真实姓名。→ 解决修改UserAction.login()从user表查username字段而非name字段或在数据库初始化时确保name字段有值INSERT INTO user(username, name, password) VALUES(zhangsan,张三,...);4.3 现象点击“发布服务”进入add_service.jsp提交后报java.lang.ClassNotFoundException: org.apache.commons.fileupload.FileItem→ 原因lib目录下缺少commons-fileupload-1.3.1.jar或版本不匹配项目用1.3.1你放了1.5。→ 解决下载commons-fileupload-1.3.1.jar和配套的commons-io-2.4.jar缺一不可放入WEB-INF/lib/重启Tomcat。验证ls WEB-INF/lib/ | grep fileupload应输出commons-fileupload-1.3.1.jar。4.4 现象管理员后台admin/index.jsp打不开报org.apache.jasper.JasperException: /admin/index.jsp (line: 12, column: 1) The absolute uri: http://java.sun.com/jsp/jstl/core cannot be resolved→ 原因JSTL标签库缺失。项目用c:forEach等标签但WEB-INF/lib/没有jstl-1.2.jar。→ 解决下载jstl-1.2.jar注意不是jstl-api放入WEB-INF/lib/同时检查web.xml是否声明了JSP 2.0以上版本web-app xmlnshttp://java.sun.com/xml/ns/j2ee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance version2.4。4.5 现象RandomCreditCardNumberGenerator.class生成的信用卡号在数据库存不进去报Data truncation: Data too long for column card_number at row 1→ 原因user表的card_number字段类型是VARCHAR(16)但生成器产生带空格的19位字符串如4532 1234 5678 9012。→ 解决修改RandomCreditCardNumberGenerator.generate()方法去掉空格return number.toString().replaceAll( , ); // 确保返回纯数字字符串或直接改数据库字段ALTER TABLE user MODIFY card_number VARCHAR(19);5. 数据库表结构逆向工程从12张表梳理出家政服务的核心业务实体关系5.1 三张主表user、admin、worker的字段设计意图项目用三张独立表存储不同角色而非单表加role字段这是为简化查询。各表关键字段对比表名主键核心业务字段设计意图是否可合并adminidusername,password,real_name,phone系统超级管理员无业务属性❌ 必须独立权限最高useridusername,password,real_name,phone,address,credit_score雇主有信用分、收货地址⚠️ 可与worker合并但项目选择分离workeridusername,password,real_name,phone,skills,price_range,work_years家政人员有技能标签、报价区间、从业年限⚠️ 同上关键发现skills字段是TEXT类型存逗号分隔字符串如育儿,养老而非关联表。这是为降低复杂度——WorkerAction用String.split(,)解析省去多表JOIN。但代价是无法用SQL直接查“所有会育儿的人员”必须用LIKE %育儿%性能差。5.2 关联表设计worker_skill与order表如何支撑服务匹配逻辑worker_skill是典型的多对多桥接表CREATE TABLE worker_skill ( id int(11) NOT NULL AUTO_INCREMENT, worker_id int(11) DEFAULT NULL, skill_id int(11) DEFAULT NULL, PRIMARY KEY (id), KEY worker_id (worker_id), KEY skill_id (skill_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;而order表记录服务订单关键字段字段类型含义关联表idINT PK订单ID—user_idINT雇主IDuser.idworker_idINT家政人员IDworker.idservice_typeVARCHAR(20)服务类型保洁/月嫂—statusTINYINT状态0待接单/1已接单/2已完成—业务逻辑当雇主发布需求系统查worker_skill找匹配skill_id的worker_id再查worker表获取人员详情。OrderAction.listByUser()的SQL是SELECT o.*, w.real_name, w.phone FROM order o LEFT JOIN worker w ON o.worker_id w.id WHERE o.user_id ? ORDER BY o.create_time DESC5.3 资讯与留言表content与doc的字段冗余与优化空间content首页资讯和doc用户留言结构高度相似字段contentdoc是否冗余优化建议idPKPK否—titleVARCHAR(100)VARCHAR(100)是可抽象为base_content表contentTEXTTEXT是同上create_timeDATETIMEDATETIME是同上statusTINYINTTINYINT是同上sortINT—否content需排序doc无需现实考量毕业设计不追求极致范式这种“看似冗余实则解耦”的设计反而降低维护成本——改content字段不影响doc业务逻辑。5.4 E-R图还原从代码反推实体关系的三个关键约束项目文档提到“总体E-R图”但没给图。我们从DAO层代码反推user与order是一对多OrderDao.findByUserId(int userId)存在且order.user_id是外键worker与order是一对多OrderDao.findByWorkerId(int workerId)存在order.worker_id是外键content与admin是多对一ContentAction中admin_id字段未出现但content表无admin_id字段说明资讯由系统自动发布无管理员归属。验证方式在OrderAction.class中搜索worker_id找到updateStatus()方法里UPDATE order SET status?, worker_id? WHERE id?证实worker_id可为空待接单状态故关系为“worker可接多单一单只属一worker”。5.5 字符集与排序规则为什么utf8mb4是唯一安全选项项目所有表都声明CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci这不是跟风。原因有二emoji支持家政人员简介可能含、等符号utf8实际是utf8mb3最多存3字节不支持4字节emoji中文排序utf8mb4_unicode_ci按Unicode标准排序张三和李四按拼音序排列而utf8mb4_general_ci已废弃。操作验证登录MySQL后执行SHOW CREATE TABLE user;确认ENGINEInnoDB DEFAULT CHARSETutf8mb4若为utf8立即转换ALTER TABLE user CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;6. 真实答辩场景预演用三个命令快速验证系统健壮性与你的掌控力6.1 一键检测数据库连接telnet比ping更能暴露网络层问题答辩前5分钟导师突然问“你确定MySQL连得上” 别慌用telnet直击本质# Linux/macOS telnet localhost 3306 # WindowsPowerShell Test-NetConnection -ComputerName localhost -Port 3306现象解读若返回Connected to localhost.→ MySQL服务正常端口开放若返回Connection refused→ MySQL未启动或bind-address在my.cnf中设为127.0.0.1导致拒绝远程但本机应通若卡住无响应 → 防火墙拦截或MySQL配置了skip-networking。这比mysql -u root -p登录更有说服力——它证明网络层通畅排除了驱动、URL、密码等应用层干扰。6.2 实时监控Tomcat线程jstack抓取阻塞点让导师信服你懂性能当导师质疑“高并发下会不会卡死”别只说“应该不会”。用jstack现场取证# 1. 查找Tomcat进程PIDLinux/macOS ps aux | grep tomcat | grep -v grep # 2. 抓取线程快照假设PID12345 jstack 12345 tomcat_threads.log # 3. 搜索阻塞关键词 grep -A 10 -B 5 BLOCKED\|WAITING tomcat_threads.log关键指标若输出中大量线程处于java.lang.Thread.State: BLOCKED (on object monitor)且等待同一把锁如at com.dao.UserDao.findByName(UserDao.java:23)说明DAO层未用连接池存在线程争抢。此时可坦然回答“当前用DBCP连接池最大连接数设为20测试表明200并发下平均响应时间800ms”。6.3 模拟真实用户行为用curl链式调用完成一次完整服务预约答辩演示时别只点鼠标。用curl命令行展示你理解整个流程# 1. 模拟雇主登录获取JSESSIONID curl -c cookies.txt -d namezhangsanpassword123456 http://localhost:8080/home_service/app/login # 2. 发布服务需求携带cookies curl -b cookies.txt -d service_type保洁price200address北京市朝阳区 http://localhost:8080/home_service/app/add_service # 3. 查询刚发布的订单验证数据落库 curl -b cookies.txt http://localhost:8080/home_service/app/list_order输出验证第三步返回的HTML中应包含td保洁/tdtd200/td。这比截图更有技术含量——它证明你掌握了HTTP会话、表单提交、服务端重定向等底层机制。6.4 数据库压力测试用sysbench跑100并发看TPS是否达标导师若问“能撑多少人”别答“几百个”。用数据说话# 1. 准备sysbenchUbuntu sudo apt-get install sysbench # 2. 准备测试数据1万行 sysbench oltp_read_write --db-drivermysql --mysql-userroot --mysql-password123456 \ --mysql-dbhome_service --tables1 --table-size10000 prepare # 3. 压测100线程持续60秒 sysbench oltp_read_write --db-drivermysql --mysql-userroot --mysql-password123456 \ --mysql-dbhome_service --threads100 --time60 run关键结果关注transactions:行末的tps值。若tps120.50可回答“在i5-8250U8G内存环境下系统可持续处理120笔/秒的读写事务满足中小型家政公司日均5000订单需求”。从那以后我每次部署JavaWeb项目都强制走一遍telnet检测curl链路验证jstack快照留存。不是为了炫技而是当答辩现场Tomcat突然假死我能30秒内定位到是DBCP连接池耗尽而不是手忙脚乱重启服务器。希望帮到你。本文还有配套的精品资源点击获取