☰
JSP+SqlServer房产中介系统源码实战:从环境搭建到论文交付
2026/9/26 4:30:20 网站建设 项目流程

简介:这是一套面向高校计算机相关专业学生的房产中介系统毕业设计参考资料,采用JSP结合SqlServer技术实现,适合作为毕设项目、课程设计或工程实训的选题方案,也适合希望学习Java Web开发的小白与进阶者参考。资源包共151个文件,约6.65MB,以86个jsp页面文件为核心,配合jpg、gif等图片素材、少量htm与html静态页、js脚本,以及db数据库文件和doc设计文档,结构完整便于对照学习。系统涵盖用户注册登录、房屋供求信息录入(出租、出售、求购、求租)、个人信息管理及个人已发布房屋信息维护等模块,涉及房屋位置、套型、单价、发布时间等关键字段设计。目前已有41人学习浏览。读者可从中获取完整源码、配套论文与数据库文件,理解权限控制与信息管理模块的实现思路,并在此基础上自行调试、修改与扩展功能。

1. 从一份 JSP+SqlServer 房产中介系统源码说起:它到底能跑出什么

很多计算机专业的同学在选题时会遇到一个尴尬:想做一个「看起来像真实业务系统」的题目,又不想陷进 Spring Cloud 那一套微服务泥潭。房产中介系统恰好卡在这个位置上——它有房源、客户、合同、经纪人四条主线,业务关系足够复杂,但又不至于复杂到一个人啃不动。用 JSP + SqlServer 这套经典组合去实现,代码量可控,数据库表结构清晰,论文也好写。

这套方案的核心价值在于:它把「房源录入 → 客户登记 → 带看记录 → 成交签约」这条业务链完整地串了起来。你拿到源码后,能直接看到 JSP 页面怎么调 Servlet、Servlet 怎么拼 SQL、SqlServer 怎么建表和存过程。对于还在用 IDEA 新建 JSP 项目、对web.xml和serverlet映射关系犯迷糊的人来说,这是一个能跑通的参照物。

适合谁看?三类人:一是正在做基于 JSP 的毕设选题、需要一份可运行骨架的同学;二是想理解传统 JSP Model2 模式到底怎么落地的人;三是需要一份带论文的完整交付物、但不想从零造轮子的人。下面我会按「环境搭起来 → 数据库建起来 → 核心功能跑起来 → 坑填起来」的顺序,把这条路走一遍。

2. 环境与数据库:把 JSP+SqlServer 的底座先立稳

2.1 JDK、Tomcat、IDEA 的版本咬合关系

JSP 项目能不能跑起来,第一道坎不是代码,是版本。我见过太多人卡在「Tomcat 启动报 404」或者「JSP 页面显示源码」上,根因都是版本没对齐。常见做法是:JDK 8 配 Tomcat 8.5 或 9.0,IDEA 用 2021 以后的版本。JDK 8 是因为老项目的 Servlet API 大多基于javax.servlet,而 Tomcat 10 已经换成了jakarta.servlet,包名一变,所有import全红。

在 IDEA 里新建项目时,选「Java Enterprise」→ 勾选「Web Application」,不要选「Spring Initializr」。这一步决定了 IDEA 会不会自动帮你生成web目录和web.xml。如果你用的是社区版 IDEA,没有 Java Enterprise 选项,那就手动建一个普通 Java 项目,然后在项目结构里添加 Web Facet,指定web.xml路径。

Tomcat 的配置在 Run/Debug Configurations 里,点「+」选 Tomcat Server → Local,在 Deployment 标签页把war包加进去。这里有个细节:Application context 建议设成/house,不要留空。留空的话访问路径是http://localhost:8080/,容易和 Tomcat 默认页冲突。

# 验证 JDK 版本,必须是 1.8.x java -version # 验证 Tomcat 能独立启动,先排除 Tomcat 自身问题 # 进入 Tomcat 的 bin 目录 cd /path/to/tomcat/bin # Windows 下执行 startup.bat,Linux/Mac 下执行 startup.sh ./startup.sh # 浏览器访问 http://localhost:8080,看到 Tomcat 默认页说明 Tomcat 本身没问题

上面这段命令的逻辑是:先确认 JDK 版本,再单独启动 Tomcat 排除容器问题。如果 Tomcat 默认页都出不来,那就不用往下查项目代码了,先解决端口占用或JAVA_HOME没配的问题。参数上,JAVA_HOME必须指向 JDK 根目录而不是bin目录,这是新手最容易搞错的地方。

2.2 SqlServer 建库:从安装到执行建表脚本

SqlServer 的安装本身不复杂,但有几个选项会影响后续开发。安装时选「基本」或「自定义」都行,关键是实例名。默认实例叫MSSQLSERVER,命名实例比如SQLEXPRESS。连接字符串里要写清楚,否则 JDBC 连不上。常见做法是用 SqlServer 身份验证而不是 Windows 身份验证,因为 JSP 项目里配数据库连接时,写死用户名密码比配 Windows 集成认证省事。

安装完之后,打开 SqlServer Management Studio(SSMS),新建一个数据库叫HouseDB。然后执行建表脚本。房产中介系统的核心表一般有这几张:House(房源)、Customer(客户)、Agent(经纪人)、Visit(带看记录)、Contract(合同)。表之间的关系是:一个经纪人负责多套房源,一个客户可以有多条带看记录,一条带看记录对应一套房源。

-- 创建数据库 CREATE DATABASE HouseDB; GO USE HouseDB; GO -- 房源表:核心字段包括标题、面积、价格、户型、状态 CREATE TABLE House ( HouseID INT IDENTITY(1,1) PRIMARY KEY, Title NVARCHAR(100) NOT NULL, Area DECIMAL(8,2) NOT NULL, Price DECIMAL(12,2) NOT NULL, RoomType NVARCHAR(50), Status INT DEFAULT 0, -- 0待售 1已售 2下架 AgentID INT, CreateTime DATETIME DEFAULT GETDATE() ); -- 客户表 CREATE TABLE Customer ( CustomerID INT IDENTITY(1,1) PRIMARY KEY, Name NVARCHAR(50) NOT NULL, Phone NVARCHAR(20), Demand NVARCHAR(200), RegisterTime DATETIME DEFAULT GETDATE() ); -- 带看记录表:客户和房源的关联 CREATE TABLE Visit ( VisitID INT IDENTITY(1,1) PRIMARY KEY, CustomerID INT, HouseID INT, VisitTime DATETIME, Remark NVARCHAR(200) );

建表脚本的逻辑说明:IDENTITY(1,1)让主键自增,省去手动维护 ID 的麻烦。NVARCHAR而不是VARCHAR,是因为中文标题和备注需要 Unicode 支持,用VARCHAR存中文会出现乱码。DECIMAL(12,2)存价格,保留两位小数,比FLOAT精确。Status字段用整数表示状态,比字符串省空间,查询也快。

参数上要注意:Area用DECIMAL(8,2)意味着最大 999999.99 平方米,对房产够用了。Price用DECIMAL(12,2)最大到百亿级别,也够。如果你要存更大的数,把精度往上调,但别用FLOAT,浮点数在金额计算上会出玄学问题。

2.3 JDBC 连接配置:把 Java 和 SqlServer 接上

数据库建好之后,下一步是在 JSP 项目里配 JDBC 连接。SqlServer 的 JDBC 驱动是mssql-jdbc,下载 jar 包放到WEB-INF/lib目录下。连接 URL 的格式是:

// 数据库连接工具类 public class DBUtil { // 注意:127.0.0.1 可以换成 localhost,但有时 localhost 解析慢 // databaseName=HouseDB 对应你建的库名 // encrypt=false 在本地开发时关掉加密,避免证书报错 private static final String URL = "jdbc:sqlserver://127.0.0.1:1433;databaseName=HouseDB;encrypt=false"; private static final String USER = "sa"; private static final String PASSWORD = "你的密码"; public static Connection getConnection() throws SQLException { try { // 加载驱动类,SqlServer 新版驱动用这个类名 Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } return DriverManager.getConnection(URL, USER, PASSWORD); } }

这段代码的逻辑是:静态常量存 URL、用户名、密码,getConnection()方法返回一个Connection对象。Class.forName加载驱动,虽然新版 JDBC 可以省略这步,但显式写出来兼容性更好。encrypt=false是关键参数,SqlServer 新版驱动默认开启加密,本地开发没有证书会报PKIX错误,加上这个参数就绕过了。

参数说明:端口1433是 SqlServer 默认端口,如果你装的时候改了,这里要同步改。sa是超级管理员账号,生产环境不要用,但毕设阶段用sa最省事。密码必须和你安装时设的一致,忘了就去 SSMS 里重置。

提示:如果连接报「TCP/IP 连接失败」,去 SqlServer 配置管理器里把 TCP/IP 协议启用,并确认 IP 地址里的 TCP 端口是 1433。

3. 核心功能落地:房源、客户、带看三条线的代码实现

3.1 房源列表分页:JSP 页面 + Servlet + SQL 分页查询

房源列表是房产中介系统里访问量最大的页面。如果一次性把几百条房源全查出来,页面会卡,所以必须分页。分页的核心是 SQL 的OFFSET FETCH子句,SqlServer 2012 以后都支持。

// HouseServlet.java 中的分页查询方法 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int page = 1; int pageSize = 10; // 从前端拿页码,没传就默认第一页 String pageStr = request.getParameter("page"); if (pageStr != null && !pageStr.isEmpty()) { page = Integer.parseInt(pageStr); } List<House> houseList = new ArrayList<>(); String sql = "SELECT * FROM House ORDER BY CreateTime DESC " + "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { // OFFSET 的计算:(当前页 - 1) * 每页条数 ps.setInt(1, (page - 1) * pageSize); ps.setInt(2, pageSize); ResultSet rs = ps.executeQuery(); while (rs.next()) { House h = new House(); h.setHouseID(rs.getInt("HouseID")); h.setTitle(rs.getString("Title")); h.setArea(rs.getBigDecimal("Area")); h.setPrice(rs.getBigDecimal("Price")); houseList.add(h); } } catch (SQLException e) { e.printStackTrace(); } request.setAttribute("houseList", houseList); request.setAttribute("currentPage", page); request.getRequestDispatcher("/house_list.jsp").forward(request, response); }

逻辑说明:OFFSET ? ROWS FETCH NEXT ? ROWS ONLY是 SqlServer 的分页语法,第一个问号是跳过多少行,第二个问号是取多少行。(page - 1) * pageSize算出偏移量。用PreparedStatement而不是Statement,是为了防 SQL 注入,同时参数设置也更清晰。

参数说明:pageSize设成 10 是常见做法,页面不会太长,查询也快。如果你想让用户自己选每页条数,就把pageSize也做成参数从请求里取。ORDER BY CreateTime DESC让最新房源排前面,符合用户习惯。

JSP 页面里用 JSTL 的c:forEach遍历houseList,然后拼 HTML 表格。分页条用c:if判断当前页是不是第一页或最后一页,决定「上一页」「下一页」按钮是否可点。

3.2 客户登记与带看记录:Model2 模式下的表单处理

JSP Model2 的核心是「JSP 只负责显示,Servlet 负责控制,JavaBean 负责数据」。客户登记这个功能,完整流程是:customer_add.jsp显示表单 → 提交到CustomerServlet→ Servlet 调 DAO 插入数据库 → 重定向到客户列表页。

// CustomerServlet.java 中的 doPost 方法 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置编码,防止中文乱码,这行必须在 getParameter 之前 request.setCharacterEncoding("UTF-8"); String name = request.getParameter("name"); String phone = request.getParameter("phone"); String demand = request.getParameter("demand"); // 简单校验:姓名和电话不能为空 if (name == null || name.trim().isEmpty() || phone == null || phone.trim().isEmpty()) { request.setAttribute("error", "姓名和电话不能为空"); request.getRequestDispatcher("/customer_add.jsp").forward(request, response); return; } String sql = "INSERT INTO Customer (Name, Phone, Demand) VALUES (?, ?, ?)"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, name); ps.setString(2, phone); ps.setString(3, demand); ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } // 插入成功后重定向到列表页,避免刷新重复提交 response.sendRedirect("customer_list"); }

逻辑说明:request.setCharacterEncoding("UTF-8")必须放在所有getParameter之前,否则中文会变成乱码。校验不通过时用forward回到表单页并带错误信息,校验通过后用sendRedirect跳转,这是为了防止用户刷新页面导致重复插入。

参数说明:setString的索引从 1 开始,不是 0。sendRedirect的路径是相对于当前 Servlet 的,如果CustomerServlet映射的是/customer_add,那重定向到customer_list就是/customer_list。

带看记录的功能类似,只是多了一个「选择房源」和「选择客户」的下拉框。下拉框的数据在页面加载时从数据库查出来,放到request域里,JSP 用c:forEach渲染成option。

3.3 合同成交:事务处理与状态联动

合同成交是业务闭环的最后一步。当经纪人确认成交时,需要同时做两件事:插入一条合同记录,把房源状态改成「已售」。这两步必须在一个事务里,否则可能出现「合同插入了但房源还是待售」的数据不一致。

// ContractServlet.java 中的成交处理方法 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); int houseID = Integer.parseInt(request.getParameter("houseID")); int customerID = Integer.parseInt(request.getParameter("customerID")); BigDecimal dealPrice = new BigDecimal(request.getParameter("dealPrice")); Connection conn = null; try { conn = DBUtil.getConnection(); // 关闭自动提交,开启事务 conn.setAutoCommit(false); // 第一步:插入合同 String sql1 = "INSERT INTO Contract (HouseID, CustomerID, DealPrice, SignTime) " + "VALUES (?, ?, ?, GETDATE())"; try (PreparedStatement ps1 = conn.prepareStatement(sql1)) { ps1.setInt(1, houseID); ps1.setInt(2, customerID); ps1.setBigDecimal(3, dealPrice); ps1.executeUpdate(); } // 第二步:更新房源状态为已售 String sql2 = "UPDATE House SET Status = 1 WHERE HouseID = ?"; try (PreparedStatement ps2 = conn.prepareStatement(sql2)) { ps2.setInt(1, houseID); ps2.executeUpdate(); } // 两步都成功,提交事务 conn.commit(); } catch (SQLException e) { // 任何一步失败,回滚 if (conn != null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } e.printStackTrace(); } finally { if (conn != null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } response.sendRedirect("contract_list"); }

逻辑说明:conn.setAutoCommit(false)关闭自动提交,之后的所有 SQL 都在同一个事务里。两步都执行成功后调conn.commit(),任何一步抛异常就调conn.rollback()。finally块里恢复自动提交并关闭连接,避免连接泄漏。

参数说明:GETDATE()是 SqlServer 的内置函数,返回当前时间。setBigDecimal用于金额字段,比setDouble精确。事务的隔离级别用默认的READ COMMITTED就行,毕设场景不需要调。

注意:如果成交时房源已经被别人买了,UPDATE语句会影响 0 行,但代码里没检查。严谨的做法是在UPDATE后判断executeUpdate()的返回值,如果是 0 就抛异常回滚。

4. 避坑与排查:JSP+SqlServer 组合里最容易翻车的 5 个点

4.1 中文乱码:从页面到数据库的全链路排查

现象:JSP 页面输入中文,存到数据库变成???,或者从数据库读出来显示成乱码。

原因:乱码可能出现在三个环节——JSP 页面编码、Servlet 请求编码、数据库字段编码。JSP 页面头部要写<%@ page contentType="text/html;charset=UTF-8" language="java" %>,Servlet 里要写request.setCharacterEncoding("UTF-8"),数据库字段要用NVARCHAR而不是VARCHAR。三个环节缺一个都会乱。

解决:按顺序检查。先看 JSP 页面page指令的charset,再看 Servlet 里setCharacterEncoding的位置是否在getParameter之前,最后看数据库字段类型。如果数据库已经建好了不想改表,可以在 JDBC URL 里加sendStringParametersAsUnicode=true,但治标不治本。

4.2 SqlServer 连接报「无法导入数据 数据无效」

现象:用 SSMS 导入 Excel 或 CSV 数据时,报「数据无效」或「截断字符串」。

原因:SqlServer 导入向导对数据类型很敏感。如果目标字段是DECIMAL,源数据里有空字符串或非数字字符,就会报这个错。另外,NVARCHAR(50)的字段如果源数据超过 50 个字符,也会截断报错。

解决:导入前先检查源数据的类型和长度。数字字段里的空值用NULL代替,不要留空字符串。字符串字段的长度先设大一点,导入成功后再改小。如果数据量不大,直接用INSERT语句手动插,比导入向导可控。

4.3 Tomcat 启动报 404:web.xml 映射与访问路径对不上

现象:Tomcat 启动没报错,但访问http://localhost:8080/house/house_list显示 404。

原因:Servlet 的@WebServlet注解或web.xml里的url-pattern和实际访问路径不一致。比如注解写的是/houseList,访问时写的是/house_list,大小写和下划线不一致就找不到。

解决:在@WebServlet("/house_list")里确认路径,然后在浏览器地址栏里逐字对照。如果用了web.xml配置,检查<servlet-mapping>的<url-pattern>是否以/开头。另外,Application context 如果是/house,访问路径里必须带这个前缀。

4.4 JSP 页面显示源码而不是渲染结果

现象:浏览器里看到的是 JSP 的原始代码,而不是渲染后的 HTML。

原因:JSP 文件放错了目录。JSP 必须放在web目录下(或子目录),不能放在WEB-INF里。放在WEB-INF里的 JSP 只能通过 Servletforward访问,不能直接通过 URL 访问。如果你直接访问WEB-INF下的 JSP,Tomcat 会返回 404 或源码。

解决:把 JSP 文件移到web目录下,或者通过 Servlet 的forward方法跳转。WEB-INF下的 JSP 是受保护的,这个设计是为了防止用户直接访问未经过 Servlet 处理的页面。

4.5 数据库连接池耗尽:Connection 没关闭的连锁反应

现象:系统运行一段时间后,所有数据库操作都超时,重启 Tomcat 才能恢复。

原因:每次getConnection()都新建连接,但用完没close()。SqlServer 默认最大连接数有限,连接耗尽后新的请求就排队等,等到超时。

解决:所有Connection、PreparedStatement、ResultSet都要在finally块里关闭,或者用 try-with-resources 语法自动关闭。上面的代码示例里用了 try-with-resources,Connection在 try 括号里声明,方法结束自动关闭。如果你用的是手动关闭,确保close()写在finally里,不要写在 try 里,否则异常发生时close()不会执行。

5. 从能跑到能交:论文写作与源码整理的几个实用技巧

5.1 论文里的系统设计章节怎么写才不空

很多同学的论文卡在「系统设计」这一章,写出来全是「本系统采用 B/S 架构,前端用 JSP,后端用 Servlet」这种废话。评委想看到的是:你为什么选这个架构,而不是 Spring Boot?你的数据库表为什么这么设计,字段类型是怎么定的?

我的习惯是:每张表配一段设计说明,写清楚字段含义、类型选择理由、和其他表的关联关系。比如House表的Status字段,不要只写「状态」,要写「0 表示待售,1 表示已售,2 表示下架,用整数而不是字符串是为了查询效率」。E-R 图不要用工具自动生成就完事,手动调整布局,让实体之间的关系一目了然。

5.2 源码目录结构:让评委一眼看懂你的分层

JSP 项目的目录结构容易乱,因为 JSP 文件、Java 类、静态资源混在一起。我一般会按这个结构整理:

目录存放内容说明
src/com/house/dao数据库访问类每个实体一个 DAO,比如HouseDao.java
src/com/house/servletServlet 控制器按功能命名,比如HouseServlet.java
src/com/house/entityJavaBean 实体类和数据库表一一对应
src/com/house/util工具类DBUtil.java放这里
web/JSP 页面按模块建子目录,比如web/house/
web/WEB-INF/lib第三方 jar 包mssql-jdbc.jar放这里
web/css、web/js静态资源样式和脚本

这个结构的好处是分层清晰,DAO 只负责 SQL,Servlet 只负责流程控制,JSP 只负责显示。评委翻源码时能快速定位到核心逻辑,不会在一堆文件里迷路。

5.3 答辩时被问到「为什么不用框架」怎么答

这个问题几乎每个用 JSP 做毕设的人都会被问。我的回答思路是:先承认框架的优势,再说明本项目的定位。可以说「Spring Boot 确实开发效率更高,但本项目的目的是展示 JSP Model2 模式下 MVC 的底层实现原理。如果用框架,很多细节被封装了,反而看不清请求是怎么从页面到 Servlet 再到数据库的。对于学习阶段来说,理解底层比会用框架更重要。」

这个回答既没有贬低框架,也解释了选型理由。如果你在论文里也写了类似的选型分析,答辩时就能自圆其说。

5.4 源码交付前的自检清单

交源码之前,我会做一遍「干净环境测试」:把项目复制到另一台电脑上,只装 JDK 和 Tomcat,不装 IDEA,用命令行编译和部署。这一步能发现很多「在我电脑上能跑」的问题,比如依赖的 jar 包没放进lib、数据库连接写的是绝对路径、Tomcat 版本不一致等。

具体操作是:用javac编译src下的所有 Java 文件,把编译后的class文件放到WEB-INF/classes,把整个web目录打包成war,丢到 Tomcat 的webapps下启动。如果这样能跑通,说明交付物是完整的。

# 编译 Java 源码,-d 指定输出目录,-cp 指定依赖 jar 包 javac -d web/WEB-INF/classes -cp "web/WEB-INF/lib/*" src/com/house/**/*.java # 打包 war,在 web 目录的上一级执行 jar -cvf house.war -C web . # 把 war 复制到 Tomcat 的 webapps 目录 cp house.war /path/to/tomcat/webapps/ # 启动 Tomcat /path/to/tomcat/bin/startup.sh

这段命令的逻辑是:先编译,再打包,再部署。-cp "web/WEB-INF/lib/*"让编译器能找到mssql-jdbc.jar。jar -cvf的-C web .表示切换到web目录再把所有内容打包进去,这样 war 包里的根目录就是web下的内容,不会多一层目录。

提示:如果编译报「找不到符号」,检查-cp里的 jar 包路径是否正确,Windows 下用分号分隔多个路径,Linux/Mac 下用冒号。

这套 JSP+SqlServer 的房产中介系统,从技术栈上看确实不新,但它把 MVC 的请求流转、JDBC 的增删改查、事务的基本用法都串了一遍。对于还在纠结「毕业设计做什么」的人来说,与其追新框架追到一半弃坑,不如把这套经典组合吃透。我当年做毕设时,光是一个中文乱码就折腾了两天,后来发现就是setCharacterEncoding写在了getParameter后面。这种坑踩过一次,以后就再也不会犯了。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询