简介:这份资源是《基于JSP的在线家政网设计与实现》的完整毕业设计文档,面向计算机相关专业学生及Java Web初学者,帮助解决家政信息预约、家教预约与在线求职等功能的系统设计与实现问题。压缩包内共1个docx文件,约1.02MB,内容涵盖摘要、可行性分析、系统分析、数据库设计及功能模块图等章节,结构完整,可直接作为课程设计或毕业论文的参考模板。文档围绕JSP技术、B/S模式、SQL Server 2008数据库与MyEclipse开发平台展开,详细阐述了家政信息预约、家教预约、在线求职及个人后台管理等模块的结构化分析与实现思路,并附有中英文摘要与目录,便于快速定位核心内容。目前已有126人学习,适合需要掌握JSP动态网页开发、B/S架构与数据库设计流程的读者参考借鉴。
1. 基于 JSP 的在线家政网:从课程设计到能跑起来的预约系统
很多同学拿到「基于 JSP 的在线家政网设计与实现」这个题目时,第一反应是去搜现成源码,结果下载下来一堆跑不起来的压缩包,MyEclipse 一导入满屏红叉,SQL Server 连不上,Tomcat 一启动就 404。这个题目的本质,是用 JSP + Servlet + SQL Server 搭一个能完成「用户注册登录 → 浏览家政服务 → 在线预约 → 后台派单管理」这条主链路的 Web 系统。它适合计算机专业做课程设计或毕业设计的同学,也适合想练手 Java Web 传统技术栈的初学者。我下面讲的这套方案,不依赖任何来路不明的源码包,从建库建表到页面跳转全部自己写,保证你能在本地跑通并理解每一层在干什么。
2. 技术选型与开发环境:为什么是 JSP + SQL Server 这套组合
2.1 为什么课程设计场景下 JSP 仍然够用
JSP 的本质是「写起来像 HTML、跑起来是 Servlet」的服务端模板技术。浏览器请求一个.jsp文件,Tomcat 会先把它翻译成 Java 类,编译后再执行,最终把生成的 HTML 吐回浏览器。对于在线家政网这种以表单提交、列表展示、数据库增删改查为主的系统,JSP 配合少量 Servlet 和 JDBC 就能覆盖全部需求,不需要引入 Spring 那一套。
选它的现实理由有三条。第一,教学环境普遍装的是 MyEclipse 或 Eclipse + Tomcat,JSP 开箱即用,不用配 Maven 依赖树。第二,SQL Server 在机房和课程设计里出现频率极高,JDBC 驱动mssql-jdbc一个 jar 包就能连上。第三,答辩时老师问「请求怎么到后台的」,你能从form action一路讲到request.getParameter,链路清晰,不像框架那样被自动配置挡住看不见。
需要说清楚边界:JSP 不适合做高并发、前后端分离的现代项目,页面里混着 Java 代码也不利于维护。但作为「在线家政网」这种规模的管理系统,它是性价比最高的选择。我一般会建议把业务逻辑尽量往 Servlet 里挪,JSP 只负责展示,这样代码不会烂成一锅粥。
2.2 环境清单与版本对应关系
环境版本对不上是新手翻车的第一大来源。MyEclipse 自带的 Tomcat 版本、JDK 版本、JDBC 驱动版本三者必须匹配,否则会出现「类找不到」或「驱动不兼容」的报错。下面这张表是我实际验证过能跑通的组合,你可以照着装。
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| JDK | 1.8 | 与 Tomcat 8 兼容性最好,避免高版本模块化问题 |
| Tomcat | 8.5 | MyEclipse 内置或独立安装均可 |
| SQL Server | 2008 R2 或 2012 | 课程设计常用,Express 版即可 |
| JDBC 驱动 | mssql-jdbc-7.4.1.jre8 | 放WEB-INF/lib下 |
| IDE | MyEclipse 2017 或 Eclipse | 支持动态 Web 项目 |
装 SQL Server 时记得在安装向导里把「混合验证模式」打开并设好 sa 密码,否则后面 JDBC 用 sa 登录会直接被拒。装完用 SQL Server Management Studio 连一次,确认服务在跑,这一步别跳过。
2.3 建库建表:五张核心表撑起整个家政业务
在线家政网的数据模型不复杂,抓住「用户、服务、订单、员工、管理员」五个实体就够了。下面这段 SQL 可以直接在 SSMS 里执行,建库和建表一次完成。
-- 创建数据库 CREATE DATABASE HouseKeepingDB; GO USE HouseKeepingDB; GO -- 用户表:存注册的家政需求方 CREATE TABLE users ( uid INT IDENTITY(1,1) PRIMARY KEY, username NVARCHAR(50) NOT NULL UNIQUE, password NVARCHAR(50) NOT NULL, phone NVARCHAR(20), address NVARCHAR(200), regtime DATETIME DEFAULT GETDATE() ); -- 服务项目表:保洁、月嫂、维修等 CREATE TABLE services ( sid INT IDENTITY(1,1) PRIMARY KEY, sname NVARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL, intro NVARCHAR(500), imgurl NVARCHAR(200) ); -- 订单表:核心业务表,记录预约 CREATE TABLE orders ( oid INT IDENTITY(1,1) PRIMARY KEY, uid INT NOT NULL, sid INT NOT NULL, orderdate DATETIME DEFAULT GETDATE(), servicedate NVARCHAR(50), status INT DEFAULT 0, -- 0待派单 1已派单 2已完成 remark NVARCHAR(300), FOREIGN KEY (uid) REFERENCES users(uid), FOREIGN KEY (sid) REFERENCES services(sid) ); -- 员工表:家政服务人员 CREATE TABLE staff ( stid INT IDENTITY(1,1) PRIMARY KEY, stname NVARCHAR(50), stphone NVARCHAR(20), skill NVARCHAR(100) ); -- 管理员表 CREATE TABLE admin ( aid INT IDENTITY(1,1) PRIMARY KEY, aname NVARCHAR(50) NOT NULL, apwd NVARCHAR(50) NOT NULL ); -- 插入一条管理员和几条测试服务 INSERT INTO admin(aname, apwd) VALUES('admin', '123456'); INSERT INTO services(sname, price, intro) VALUES ('日常保洁', 80.00, '两小时标准保洁服务'), ('深度保洁', 200.00, '含厨房卫生间深度清洁'), ('家电维修', 120.00, '上门检测并维修小家电');建表时有两个细节容易忽略。orders表里的status用整数表示状态流转,比存中文字符串更省空间也更好做条件查询。servicedate我用NVARCHAR存预约时间字符串,是为了避开 SQL Server 日期转换的坑——新手用DATETIME接收前端传来的2024-05-01这种格式时,经常撞上conversion failed when converting date and/or time的报错,先用字符串存,展示时再格式化,能省掉大量调试时间。
3. 从零搭出可运行工程:MyEclipse 建项目与数据库连接
3.1 新建动态 Web 项目并规划目录结构
打开 MyEclipse,File → New → Web Project,项目名填HouseKeeping,JRE 选 1.8,Target Runtime 选你装好的 Tomcat 8.5。建完后目录结构应该是这样:
HouseKeeping/ ├── src/ # Java 源码,放 Servlet 和工具类 │ └── com.hk.util/ │ └── DBUtil.java ├── WebRoot/ │ ├── WEB-INF/ │ │ ├── lib/ # 放 mssql-jdbc 驱动 jar │ │ └── web.xml # 配置 Servlet 映射 │ ├── css/ │ ├── images/ │ ├── index.jsp # 首页 │ ├── login.jsp │ ├── register.jsp │ ├── service_list.jsp │ └── order_add.jsp把mssql-jdbc-7.4.1.jre8.jar复制到WEB-INF/lib下,MyEclipse 会自动把它加入构建路径。这一步做完,右键项目Build Path → Configure Build Path确认 Libraries 里能看到这个 jar,看不到就手动 Add JARs。
3.2 写一个能复用的数据库连接工具类
不要在每一个 JSP 里都写一遍DriverManager.getConnection,那样改一次密码要改十几个文件。抽一个DBUtil出来,全项目共用。
package com.hk.util; import java.sql.*; public class DBUtil { // 数据库连接参数,按你本机实际情况改 private static final String DRIVER = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; private static final String URL = "jdbc:sqlserver://localhost:1433;databaseName=HouseKeepingDB;encrypt=false"; private static final String USER = "sa"; private static final String PWD = "你的sa密码"; // 静态块只加载一次驱动 static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } // 获取连接 public static Connection getConn() throws SQLException { return DriverManager.getConnection(URL, USER, PWD); } // 统一关闭资源,顺序不能错 public static void close(Connection conn, Statement st, ResultSet rs) { try { if (rs != null) rs.close(); } catch (Exception e) {} try { if (st != null) st.close(); } catch (Exception e) {} try { if (conn != null) conn.close(); } catch (Exception e) {} } }逻辑说明:静态块保证驱动类在类加载时注册一次,避免重复加载。URL里的encrypt=false是 SQL Server 新版驱动的要求,不加会报 SSL 握手错误。close方法按ResultSet → Statement → Connection的顺序关闭,顺序反了会抛异常。参数说明:localhost:1433是默认实例端口,如果你装的是命名实例,要改成localhost\\实例名的写法。
3.3 用 Servlet 处理登录并跳转
JSP 负责显示表单,Servlet 负责收参数、查库、决定跳哪。下面这个LoginServlet是登录主流程。
package com.hk.servlet; import com.hk.util.DBUtil; import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; import java.sql.*; public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); String username = req.getParameter("username"); String password = req.getParameter("password"); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = DBUtil.getConn(); // 用占位符防注入,别拼字符串 String sql = "SELECT uid, username FROM users WHERE username=? AND password=?"; ps = conn.prepareStatement(sql); ps.setString(1, username); ps.setString(2, password); rs = ps.executeQuery(); if (rs.next()) { // 登录成功,把用户信息放进 session HttpSession session = req.getSession(); session.setAttribute("uid", rs.getInt("uid")); session.setAttribute("uname", rs.getString("username")); resp.sendRedirect("service_list.jsp"); } else { req.setAttribute("msg", "用户名或密码错误"); req.getRequestDispatcher("login.jsp").forward(req, resp); } } catch (Exception e) { e.printStackTrace(); } finally { DBUtil.close(conn, ps, rs); } } }逻辑说明:setCharacterEncoding("UTF-8")必须在取参数之前调用,否则中文用户名会乱码。用PreparedStatement加占位符是硬性要求,直接拼 SQL 字符串会留下注入漏洞,答辩时被问到会扣分。登录成功后用sendRedirect跳转,失败用forward带着错误信息回登录页,两者的区别是 redirect 会改变地址栏、forward 不会。
3.4 在 web.xml 里注册 Servlet 映射
Servlet 3.0 以后可以用注解,但课程设计里老师往往要求配web.xml,两种都写上最稳妥。
<servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.hk.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>配完后,login.jsp里的表单action写login,提交后就会进到LoginServlet。注意url-pattern前面的斜杠不能少,少了会匹配不上导致 404。
4. 核心业务落地:服务列表、在线预约与后台派单
4.1 服务列表页:JSP 里遍历结果集
服务列表是在线家政网的门面,用户进来第一眼要看到有哪些服务、多少钱。这个页面用 JSP 脚本片段直接查库并循环输出。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.sql.*, com.hk.util.DBUtil" %> <html> <head><title>家政服务列表</title></head> <body> <h2>可预约的家政服务</h2> <table border="1" cellpadding="8"> <tr><th>服务名称</th><th>价格</th><th>简介</th><th>操作</th></tr> <% Connection conn = DBUtil.getConn(); Statement st = conn.createStatement(); ResultSet rs = st.executeQuery("SELECT sid, sname, price, intro FROM services"); while (rs.next()) { %> <tr> <td><%= rs.getString("sname") %></td> <td>¥<%= rs.getBigDecimal("price") %></td> <td><%= rs.getString("intro") %></td> <td><a href="order_add.jsp?sid=<%= rs.getInt("sid") %>">立即预约</a></td> </tr> <% } DBUtil.close(conn, st, rs); %> </table> </body> </html>逻辑说明:pageEncoding="UTF-8"和contentType里的charset都要写,只写一个中文仍可能乱码。循环里把sid拼进预约链接,用户点「立即预约」时带着服务 ID 跳到下单页。参数说明:getBigDecimal读价格字段比getDouble精度更稳,涉及金额时优先用它。
4.2 在线预约:表单提交与订单入库
预约页要接收上一步传来的sid,让用户填服务时间和备注,提交后写进orders表。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% // 从 session 拿登录用户,没登录就踢回登录页 Object uid = session.getAttribute("uid"); if (uid == null) { response.sendRedirect("login.jsp"); return; } String sid = request.getParameter("sid"); %> <html> <head><title>预约服务</title></head> <body> <h2>填写预约信息</h2> <form action="orderSubmit" method="post"> <input type="hidden" name="sid" value="<%= sid %>"> 服务时间:<input type="text" name="servicedate" placeholder="如 2024-06-01 上午"><br> 备注说明:<textarea name="remark" rows="3"></textarea><br> <input type="submit" value="提交预约"> </form> </body> </html>对应的OrderSubmitServlet负责入库:
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); int uid = (Integer) req.getSession().getAttribute("uid"); int sid = Integer.parseInt(req.getParameter("sid")); String servicedate = req.getParameter("servicedate"); String remark = req.getParameter("remark"); Connection conn = null; PreparedStatement ps = null; try { conn = DBUtil.getConn(); String sql = "INSERT INTO orders(uid, sid, servicedate, remark, status) VALUES(?,?,?,?,0)"; ps = conn.prepareStatement(sql); ps.setInt(1, uid); ps.setInt(2, sid); ps.setString(3, servicedate); ps.setString(4, remark); ps.executeUpdate(); resp.sendRedirect("my_orders.jsp"); } catch (Exception e) { e.printStackTrace(); } finally { DBUtil.close(conn, ps, null); } }逻辑说明:sid用隐藏域传递,用户看不到但提交时会带上。status固定写 0 表示待派单,后续管理员改状态。参数说明:Integer.parseInt转换前最好判空,前端如果没传sid会抛NumberFormatException,加个 try-catch 更稳。
4.3 后台派单:管理员改订单状态并分配员工
后台是家政网区别于普通展示站的关键。管理员登录后看到所有待派单订单,选一个员工,把状态从 0 改成 1。
// 派单:更新订单状态并记录员工 String sql = "UPDATE orders SET status=1, remark=remark+' [已派单给"+stname+"]' WHERE oid=?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setInt(1, oid); ps.executeUpdate();后台列表页用WHERE status=0只查待派单的,处理完的自动从列表消失。这里有个设计取舍:把派单员工信息拼进remark字段是偷懒做法,规范做法是给orders加一个stid外键。课程设计里如果时间紧,拼字符串能跑通;如果想让数据库设计得分更高,就老老实实加字段。
4.4 状态流转与页面权限控制
订单状态 0→1→2 的流转要在每个入口做校验。用户不能自己把订单改成已完成,只有管理员能操作。实现方式是在后台 Servlet 开头判断 session 里有没有管理员标识。
Object aid = session.getAttribute("aid"); if (aid == null) { response.sendRedirect("admin_login.jsp"); return; }这段判断要放在后台每个 Servlet 的最前面,漏掉一个就等于开了后门。我见过有同学只在前台页面做了登录判断,后台 Servlet 直接能通过 URL 访问,答辩演示时被老师当场输入地址绕过,很尴尬。
5. 避坑与排查:那些让项目跑不起来的常见问题
5.1 驱动加载失败:ClassNotFoundException
现象:Tomcat 启动后访问登录页,控制台报java.lang.ClassNotFoundException: com.microsoft.sqlserver.jdbc.SQLServerDriver。
原因:JDBC 驱动 jar 没放进WEB-INF/lib,或者放了但没加入构建路径,编译时能找到、运行时找不到。
解决:确认WEB-INF/lib下有mssql-jdbc-xxx.jar,右键项目 Build Path 里也勾上。如果用的是 Tomcat 独立安装版,别把 jar 放到 Tomcat 的lib下,那样项目换环境就失效,放项目自己的WEB-INF/lib才是正解。
5.2 中文乱码:从表单到数据库全是问号
现象:注册时输入中文用户名,存进数据库变成???,或者页面显示成乱码。
原因:三个环节的编码没统一——JSP 页面编码、请求参数编码、数据库字段编码。
解决:JSP 顶部写pageEncoding="UTF-8";Servlet 里第一行写req.setCharacterEncoding("UTF-8");数据库字段用NVARCHAR而不是VARCHAR。三处都改完基本就不会乱码了。如果还有问题,检查 MyEclipse 的 Workspace 编码是不是设成了 GBK。
5.3 日期转换报错:conversion failed when converting date and/or time
现象:提交预约时抛SQLServerException: Conversion failed when converting date and/or time from character string。
原因:前端传的日期格式和数据库字段类型不匹配,比如字段是DATETIME但传了「2024-06-01 上午」这种带中文的字符串。
解决:要么把字段改成NVARCHAR存字符串,要么在前端用日期控件保证格式是yyyy-MM-dd,后端用SimpleDateFormat解析后再ps.setDate。我一般选前者,课程设计阶段少一层解析少一个坑。
5.4 端口占用:Tomcat 启动报 Address already in use
现象:启动 Tomcat 时报java.net.BindException: Address already in use: JVM_Bind。
原因:8080 端口被别的程序占了,常见的是之前没关干净的 Tomcat 进程,或者装了其他 Web 服务。
解决:命令行执行netstat -ano | findstr 8080找到占用进程的 PID,任务管理器里结束它。或者改 Tomcat 的conf/server.xml,把Connector port="8080"改成 8081,重启即可。
5.5 404 找不到 Servlet:映射配了但访问不到
现象:表单提交后浏览器显示 404,控制台没有报错。
原因:web.xml里url-pattern和表单action对不上,或者 Servlet 类名、包名写错。
解决:action写login,url-pattern写/login,两边保持一致。检查servlet-class是不是完整的com.hk.servlet.LoginServlet。改完web.xml一定要重启 Tomcat,热部署有时不生效。
6. 让家政网更像真实产品:分页查询与图片上传的进阶处理
基础功能跑通后,想让项目在答辩时更出彩,有两个方向值得花时间:列表分页和图片上传。这两块是「能跑」和「像个产品」之间的分水岭。
先说分页。服务列表和订单列表一旦数据多了,一页全展示又慢又难看。SQL Server 分页在 2012 以后可以用OFFSET ... FETCH,写法比老的ROW_NUMBER()清爽。
-- 每页 5 条,查第 2 页 SELECT sid, sname, price, intro FROM services ORDER BY sid OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY;OFFSET后面是跳过的行数,公式是(当前页码-1) × 每页条数,FETCH NEXT后面是每页条数。JSP 里接收page参数,算出偏移量再拼进 SQL。注意OFFSET FETCH必须配合ORDER BY使用,不写排序会直接报错。总页数用SELECT COUNT(*)单独查一次,页面上渲染上一页/下一页链接。
再说图片上传。家政服务配上实拍图,页面质感立刻不一样。JSP 处理文件上传不能直接用request.getParameter,要用commons-fileupload组件。表单必须加enctype="multipart/form-data",Servlet 里用ServletFileUpload解析。
// 判断是否是 multipart 请求 if (ServletFileUpload.isMultipartContent(req)) { DiskFileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); List<FileItem> items = upload.parseRequest(req); for (FileItem item : items) { if (!item.isFormField()) { // 是文件,保存到 images 目录 String fileName = System.currentTimeMillis() + "_" + item.getName(); String savePath = getServletContext().getRealPath("/images") + "/" + fileName; item.write(new File(savePath)); // 把文件名存进数据库 imgurl 字段 } } }这里有个血泪经验:getRealPath拿到的是部署目录,项目重新发布后上传的图片会丢。稳妥做法是把图片存到项目外的固定目录,数据库只存相对路径。另外文件名要用时间戳重命名,否则同名文件会互相覆盖。
最后说一个验证技巧。功能写完后,别只点一遍正常流程就交差,要专门测边界:用户名留空提交、预约时间填乱码、直接访问后台 URL、连续提交两次订单。这些异常路径能跑通,说明你的校验逻辑是完整的。我自己做课程设计时养成的习惯是,每写完一个 Servlet,先在浏览器地址栏手动拼参数访问一次,看看没登录时会不会被拦住。这个习惯帮我省掉了答辩现场被问住的后悔药。希望帮到你。
本文还有配套的精品资源,点击获取