☰
JSP+SqlServer房产中介系统毕业设计:从环境搭建到避坑指南
2026/10/10 6:28:53 网站建设 项目流程

简介:一套基于JSP+SqlServer实现的房产中介系统毕业设计资料包,主要面向计算机相关专业的学生,可用于毕业设计、课程设计或工程实训。系统需用户注册登录后操作,围绕房屋供求信息管理展开,包含出租、出售、求购、求租信息的录入,个人资料维护,以及已发布房屋信息的管理,覆盖了中介业务常见模块。压缩包共151个文件,约6.65MB,以86个jsp页面代码为主体,配合jpg、gif等界面素材、htm静态页面、db和bak数据库备份文件,以及doc格式的设计论文,便于对照源码理解前端页面与后端逻辑。目前已有41人学习下载。资料可作为项目实现的参考资料,帮助初学者梳理登录权限控制、房屋信息录入与个人管理等功能的设计思路,同时论文文档也能辅助撰写毕业设计说明,适合需要在现有代码基础上自行调试和扩展功能的读者。

1. 这套JSP+SqlServer房产中介系统,代码能跑通不等于毕业设计能过

每年到这个节点,总会有人带着“源码能跑、论文凑完”的心态来问某房产中介系统怎么改。说实话,基于JSP+SqlServer做的这类毕业设计,代码量通常不大,核心业务也就房源、客源、预约、成交、用户权限这几张表在转,但只要其中一步环境没搭对——驱动版本、端口、事务、中文编码——演示现场就能直接从“跑通了”变成“翻车了”。这篇笔记就是按我自己带学生做这套系统的顺序写的:先讲清楚技术选型为什么成立,再给出一套能落地到本地的数据库脚本和JDBC连接写法,最后把踩过的坑一条条列出来。适合准备复现、改造或者正在写论文但代码还没跑顺的人。

2. 先立住技术栈与运行环境:JSP+SqlServer为什么还能用,以及本地复跑的前置清单

2.1 三层架构与两个选型理由:JSP的“能跑”与SqlServer的“在线”

先回答一个绕不开的问题:都这个年代了,为什么还要用JSP+SqlServer做房产中介系统。常见做法是把它分成三层:JSP负责页面展示,Servlet负责请求分发,DAO负责数据库操作,SqlServer只存数据。这套结构对毕业设计有一个很实际的好处,就是每一层都能单独讲清楚,论文里画架构图、写数据流、做测试用例都顺手。

选JSP的理由不是它性能多好,而是它“能跑”的路径最短。一个JSP页面丢进Tomcat就能出效果,不需要额外的前后端分离工程,也不需要Node或者Vue那把链子。选SqlServer的理由是它商业环境里用得多,且和Java配合的JDBC驱动已经非常成熟,和MySQL相比它的作业、存储过程、事务处理在论文里都能写出东西来。这两个组合放到房产中介这种业务模型规整、状态流转清晰的场景里,属于典型够用的组合。

但要说清楚,这两个选型也有代价:JSP页面里混着Java代码会让前端改起来费劲,SqlServer对运行环境的内存和配置比MySQL挑剔,这些问题后面第6章会具体展开。现在只需要记住——这个选型不是为了炫技,是为了让你花最少的时间把系统跑起来,把精力留给业务逻辑和论文里的图。

2.2 本地跑通的最小环境准备:JDK、Tomcat、SQL Server要装到哪个版本

很多人的问题不是代码错,是环境版本配不上。我一般建议按这个组合来装,兼容性最省心:

组件推荐版本说明
JDK1.8兼容Tomcat 8/9,避免新版JDK的模块化问题
Tomcat8.5或9.0对JSP和Servlet支持稳定,配置简单
SQL Server2008 R2及以上(Express版即可)Express免费且够用,注意安装时启用TCP/IP
Eclipse或IDEA任意能把项目部署进Tomcat即可

版本上最容易踩的坑是JDK版本过高。用JDK 11以上跑老项目,经常遇到“类找不到”或者Tomcat启动直接失败,原因多数是javax.servlet包在新版本里被移走了。所以JDK 1.8是这套系统最稳妥的底座。Tomcat 8.5和9.0都支持Servlet 3.1/4.0,对JSP+Servlet这套写法没什么限制。

SqlServer安装时有两个选项必须注意:一是实例名,默认的MSSQLSERVER在连接串里可以直接写localhost,但如果装了命名实例,连接串里就要写成localhost\\实例名;二是在Sql Server Configuration Manager里把TCP/IP协议启用,端口默认1433,这个不启用,后面JDBC连接必失败。装完后用如下命令验证服务状态是启动的:

net start | findstr /i "SQL SERVER"

这段命令的意思是在Windows服务列表里查找SQL Server相关服务,如果输出里能看到类似“SQL Server (MSSQLSERVER)”的行,说明数据库服务已经在运行。如果没输出,就去“服务”管理界面把SQL Server服务手动启动并设为自动。这一条建议在装完系统当天就验证一次,别等到跑代码时才查。

2.3 项目导入的目录对照:哪个目录放JSP,哪个目录放Servlet,别放错

拿到一个项目后,第一件事不是看代码,而是确认目录结构。标准的JSP+Servlet项目应该是这样的布局:

src/main/java -- Servlet类和DAO类 src/main/webapp -- 所有JSP页面 src/main/webapp/WEB-INF/web.xml -- Servlet映射和欢迎页配置 src/main/webapp/WEB-INF/lib -- 依赖的jar包(含sqljdbc驱动)

常见错误是把JSP页面放到WEB-INF目录里面。WEB-INF下的文件不能通过URL直接访问,如果你把login.jsp放进去,浏览器访问/login.jsp必然404。JSP应该放在webapp根目录或者webapp下的子目录里,WEB-INF只放web.xml和class、lib。这个规则很多第一次接触JSP的人会记混,建议在导入项目后就检查一遍。

另外强调的是sqljdbc驱动的jar包必须放在WEB-INF/lib下,不要放到Tomcat的lib目录里。放到Tomcat的lib虽然能让本项目跑起来,但换一台机器部署时容易漏掉,而且多个项目共用会造成版本冲突。驱动放项目里才是正确姿势。

3. 房产中介系统的库表设计:房源、客源、预约、成交四张核心表怎么落地

3.1 从业务流反推表结构:一次完整租赁交易涉及哪些实体

在设计库表前,先顺着业务流程走一遍。一个房产中介系统的核心闭环大致是:房东发布房源,租客浏览房源,租客发起预约看房,经纪人陪同看房,双方谈妥后成交登记。这个流程里至少涉及四类角色和一个核心状态位。

实体拆分下来就是:用户表(user)、房源表(house)、预约表(appointment)、成交表(deal)。用户表通过role字段区分房东、租客、经纪人;房源表记录房源的基本信息和状态;预约表记录谁约了什么时候看哪个房;成交表在预约和谈妥后产生,关联房源和双方用户。

这里最关键的设计决策,是“房源状态”不要散落在各个表里,而要集中维护在house表的一个status字段上。0代表待审核,1代表已上架,2代表已下架,3代表已成交。后面所有业务——搜索只查status=1,预约要求status=1,成交后把status改成3——都围绕这个字段做判断,代码逻辑会非常清爽。用数据库术语说,这是用一个字段表达聚合根的状态流转。

3.2 核心建表SQL与字段参数说明

下面是一套我在模拟项目X里验证过的核心建表脚本,直接放到SqlServer的“新建查询”里执行即可。先建用户表、房源表,再建依赖它们的预约表和成交表。

-- 用户表:房东、租客、经纪人统一存放,用role区分 CREATE TABLE t_user ( id INT IDENTITY(1,1) PRIMARY KEY, username NVARCHAR(50) NOT NULL UNIQUE, password NVARCHAR(50) NOT NULL, real_name NVARCHAR(20), phone NVARCHAR(20), role INT NOT NULL DEFAULT 2, -- 0=管理员 1=房东 2=租客 3=经纪人 create_time DATETIME DEFAULT GETDATE() ); -- 房源表:核心业务表,status字段控制全流程 CREATE TABLE t_house ( id INT IDENTITY(1,1) PRIMARY KEY, title NVARCHAR(100) NOT NULL, address NVARCHAR(200) NOT NULL, price DECIMAL(10,2) NOT NULL, house_type NVARCHAR(20), -- 户型,如 2室1厅 area DECIMAL(10,2), -- 面积,单位平米 owner_id INT NOT NULL REFERENCES t_user(id), status INT NOT NULL DEFAULT 0, -- 0=待审核 1=上架 2=下架 3=已成交 create_time DATETIME DEFAULT GETDATE() ); -- 预约表:记录租客的看房申请 CREATE TABLE t_appointment ( id INT IDENTITY(1,1) PRIMARY KEY, house_id INT NOT NULL REFERENCES t_house(id), customer_id INT NOT NULL REFERENCES t_user(id), agent_id INT REFERENCES t_user(id), -- 经纪人,可空 appoint_time DATETIME NOT NULL, status INT NOT NULL DEFAULT 0, -- 0=待确认 1=已确认 2=已取消 3=已完成 remark NVARCHAR(500), create_time DATETIME DEFAULT GETDATE() ); -- 成交表:最终签约登记 CREATE TABLE t_deal ( id INT IDENTITY(1,1) PRIMARY KEY, house_id INT NOT NULL REFERENCES t_house(id), customer_id INT NOT NULL REFERENCES t_user(id), agent_id INT NOT NULL REFERENCES t_user(id), deal_price DECIMAL(10,2) NOT NULL, deal_time DATETIME DEFAULT GETDATE() );

脚本的逻辑是逐层建表,先建不依赖其他表的t_user,再建依赖用户的t_house,最后建同时依赖前两者的t_appointment和t_deal。字段类型上,所有中文文本都用NVARCHAR而不是VARCHAR,这是SqlServer存储中文时避免乱码的关键——VARCHAR按数据库代码页存储中文,代码页不对就是问号;NVARCHAR存的是Unicode,基本不出乱码问题。

IDENTITY(1,1)是SqlServer的自增主键写法,等价于MySQL的AUTO_INCREMENT。外键约束在这里是有意保留的,因为毕业设计论文里需要画关系图,有主外键才能生成完整的数据库关系图。role字段用数字枚举,而不是直接存“房东”字符串,是为了避免用户改名时影响业务判断。价格用DECIMAL(10,2),能存到千万级别且保留两位小数,对房产中介的业务量级完全够用。

3.3 外键、状态位与唯一约束:让房源和预约对得上账

上面这段脚本埋了两个细节,值得单独讲。第一个是预约表里的agent_id允许为NULL。这是有意的:租客发起预约时,经纪人还没分配,如果这里设成NOT NULL,插入预约记录时就必须先填经纪人,违背业务流程。所以这个字段是可空的,在“经纪人接单”环节再用UPDATE补上。

第二个细节是成交表没有单独加状态字段,而是通过house表的状态来反映。一旦写入t_deal成交记录,就要同步把t_house的status改成3。这两个操作必须绑定在一起——先插成交表,再改房源状态,中间任何一步失败都要回滚。这个事务边界的控制,在第5章讲业务实现时会给出代码。

还有一条关于查询效率的实用建议:在t_appointment的house_id上建索引,在t_house的status上建索引。这是房产中介系统访问最频繁的两个查询条件——“查某个房源的预约记录”“查某个状态下的房源列表”。数据量只有几百条时索引效果不明显,但论文里性能对比部分会用到,建了索引写出来的分析更站得住脚。

4. JDBC连接SqlServer这一段:连接参数、驱动包与三层调用链路

4.1 选DriverManager还是连接池:代码量最少、最能应付演示的写法

连接数据库有两条路:一条是直接用DriverManager.getConnection(),每条业务代码里想连就连,用完就关;另一条是引入连接池,启动时初始化一批连接,用完归还。放在真实项目中我会选连接池,但放在毕业设计这个场景,我明确建议用DriverManager,理由有三条。

第一条是代码量。连接池要引入第三方库、配置数据源、处理异常,光配置代码就比DriverManager多出几十行,而DriverManager一个封装好的工具类就能解决全部连接问题。第二条是演示稳定性。连接池配置错误时经常出现“池满了”“连接泄漏”之类的抽象报错,DriverManager的错误信息直白——要么连不上,要么SQL错,答辩时更容易当场解释。第三条是论文篇幅。连接池会让论文的“关键技术”部分多一块内容,但这部分很难写出深度,反而可能被老师追问底层原理。

DriverManager的缺陷是每次请求都新建连接,性能确实不如连接池。但这个系统是单机演示场景,并发量极低,性能差异完全体现不出来。所以不要为了“看着专业”去上连接池,用DriverManager写好一个健壮的工具类,是这套系统最踏实的做法。

4.2 写一个靠得住的JdbcUtil:连接串、字符集与资源关闭顺序

JdbcUtil是整个系统最值得认真写的一个类,所有DAO都依赖它。下面是我惯用的写法,可直接套用:

package com.demo.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class JdbcUtil { // 连接参数集中管理,换机器只改这一处 private static final String DRIVER = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; private static final String URL = "jdbc:sqlserver://localhost:1433;databaseName=house_db;characterEncoding=UTF-8"; private static final String USER = "sa"; private static final String PASSWORD = "123456"; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(ResultSet rs, Statement stmt, Connection conn) { try { if (rs != null) rs.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (stmt != null) stmt.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }

说明几个关键点:驱动类名com.microsoft.sqlserver.jdbc.SQLServerDriver对应sqljdbc4.jar或新版mssql-jdbc驱动,如果你用的是老版sqljdbc.jar,这里的类名要换成com.microsoft.jdbc.sqlserver.SQLServerDriver,两个类名不一样,用错直接报“找不到驱动类”。

连接串jdbc:sqlserver://localhost:1433;databaseName=house_db中,1433是SqlServer默认端口,如果安装时改过端口要同步修改。这里我加了characterEncoding=UTF-8,这是为了配合页面和Tomcat的UTF-8编码,让中文参数在传输过程中不乱码。USER和PASSWORD默认写的sa和123456,实际使用要改成你自己的SqlServer登录名。

资源关闭顺序也写清楚了:先关ResultSet,再关Statement,最后关Connection。很多人只关Connection,觉得连接关了其他就自动释放,这在大多数情况下能用,但在高频率操作场景里会出现游标未释放导致的报错。养成三个参数都传、按顺序关的习惯,后面会少很多诡异问题。

4.3 一次完整请求的三层调用:JSP → Servlet → DAO → SqlServer

工具类写完后,要理解一次请求是怎么在三层之间走的。以“查询在售房源列表”为例,完整调用链是这样的:

<!-- house_list.jsp 页面片段:点击查询按钮,提交到ListHouseServlet --> <form action="ListHouseServlet" method="get"> <input type="text" name="keyword" placeholder="输入小区名或户型" /> <button type="submit">查询</button> </form>
// ListHouseServlet.java:接收请求,调DAO,转发到JSP @WebServlet("/ListHouseServlet") public class ListHouseServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String keyword = request.getParameter("keyword"); HouseDao dao = new HouseDao(); List<House> list = dao.findByKeyword(keyword); request.setAttribute("houseList", list); request.getRequestDispatcher("house_list.jsp").forward(request, response); } }
// HouseDao.java:执行SQL并封装结果 public List<House> findByKeyword(String keyword) { List<House> list = new ArrayList<>(); String sql = "SELECT * FROM t_house WHERE status=1 AND (title LIKE ? OR address LIKE ?)"; try (Connection conn = JdbcUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, "%" + keyword + "%"); ps.setString(2, "%" + keyword + "%"); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { House h = new House(); h.setId(rs.getInt("id")); h.setTitle(rs.getString("title")); h.setPrice(rs.getBigDecimal("price")); list.add(h); } } } catch (SQLException e) { e.printStackTrace(); } return list; }

这条链路的关键是每一层只干一件事:Servlet里不要写SQL,DAO里不要写HTML,JSP里不要写Java代码块。用PreparedStatement而不是拼接SQL字符串,既避免了SQL注入,又不用手动处理字符串里的引号。SQL里LIKE ?配合ps.setString填入%关键词%,是模糊查询的标准写法,直接拼字符串容易漏掉百分号导致查不到数据。

参数说明再强调一次:request.setCharacterEncoding("UTF-8")必须放在getParameter之前,否则POST请求的中文参数会乱码;GET请求的中文乱码则要在Tomcat的server.xml里给Connector加URIEncoding="UTF-8"。这两处是JSP中文问题的高发区。

5. 三个核心业务模块的实现要点:登录权限、房源上下架、预约看房

5.1 登录与角色菜单:房东、租客、经纪人同一张表怎么分流

登录模块的核心不是验证密码,而是登录成功后“带着角色进系统”。t_user表里role字段是这个逻辑的入口,登录成功后把用户对象和角色一起放进Session,JSP页面根据角色决定显示哪些菜单。

// LoginServlet.java 关键片段 User user = userDao.findByUsernameAndPassword(username, password); if (user != null) { HttpSession session = request.getSession(); session.setAttribute("loginUser", user); session.setAttribute("role", user.getRole()); // 按角色跳转到不同的首页 if (user.getRole() == 0) { response.sendRedirect("admin/index.jsp"); } else if (user.getRole() == 3) { response.sendRedirect("agent/index.jsp"); } else { response.sendRedirect("user/index.jsp"); } } else { request.setAttribute("msg", "用户名或密码错误"); request.getRequestDispatcher("login.jsp").forward(request, response); }

对应的JSP页面里,菜单用<c:if>标签控制显示。比如经纪人能看到“预约审核”菜单,租客能看到“我要看房”菜单,房东能看到“发布房源”菜单:

<!-- 菜单部分,需要用JSTL标签库 --> <c:if test="${sessionScope.role == 3}"> <li><a href="AppointmentListServlet">预约审核</a></li> </c:if> <c:if test="${sessionScope.role == 1}"> <li><a href="HousePublishServlet">发布房源</a></li> </c:if> <c:if test="${sessionScope.role == 2}"> <li><a href="HouseListServlet">浏览房源</a></li> </c:if>

这里有个容易被忽略的细节:Session里存的role只是登录时写进去的一个快照,如果数据库里用户的角色被改了,Session里的值不会自动更新。对这个系统来说影响不大,但答辩时老师可能会问“权限控制怎么实现的”,你要答得出来“通过Session中的角色字段控制展示菜单和Servlet访问权限”这一层。

菜单控制只是界面层面的权限,更严格的做法是在Servlet入口再做一次角色校验。我一般会建议做一个简单的过滤器(Filter),在doFilter里检查Session角色是否允许访问当前请求路径,不允许就直接拦截跳转到登录页。这样论文的“安全设计”部分就有内容写了。

5.2 房源发布与上下架:状态位翻转的SQL与页面联动

房源发布是房东的专属操作,流程是填写表单、提交、存库,新插入的房源status默认是0(待审核)。这一步的代码就是一次普通insert,没什么坑。真正的关键在上下架的操作——它本质是UPDATE语句把status字段改掉:

-- 上架操作:只有当前是“下架”状态才允许上架 UPDATE t_house SET status = 1 WHERE id = ? AND status = 2; -- 下架操作:只有当前是“上架”状态才允许下架 UPDATE t_house SET status = 2 WHERE id = ? AND status = 1;

这两条SQL的WHERE条件里带了原状态,是一个防呆设计。如果前端连续点两次按钮,第一次请求把status从1改成2,第二次请求到达时由于status已经不是1了,UPDATE影响的行数会是0,从而避免了重复操作。判断影响行数的方式是看executeUpdate()的返回值,返回1说明改成功了,返回0说明状态不对或房源不存在。

这个设计在论文的“并发控制”部分特别值得一提,体现了乐观锁的思路——通过状态条件来防止脏操作,而不是用数据库锁。页面上的联动逻辑也很直观:房源状态为1时显示“下架”按钮,状态为2时显示“上架”按钮,状态为3时两个按钮都不显示,只显示“已成交”的灰色标记。

实际实现时,也要考虑一个业务规则:已成交的房源不能直接上架再卖。所以UPDATE语句里必须保证只有status=1或status=2的记录才能被修改,status=3的记录不允许通过上下架接口改动。这是二手房交易系统里“一套房不能重复卖”的基本约束,落库层面就得挡住。

5.3 预约看房与成交登记:两张表的写入顺序与事务边界

预约看房的流程比上下架复杂一点:租客提交预约(写入t_appointment,状态0),经纪人确认预约(状态改为1或2),看房完成后经纪人登记成交(写入t_deal并同步修改t_house状态)。其中最后一步涉及两张表的写入,必须用事务包住:

public boolean confirmAndCreateDeal(int appointmentId, int houseId, int customerId, int agentId, BigDecimal dealPrice) { Connection conn = null; try { conn = JdbcUtil.getConnection(); conn.setAutoCommit(false); // 关掉自动提交,手动控制事务 // 第一步:把预约状态改成已完成 String updateAppointment = "UPDATE t_appointment SET status = 3 WHERE id = ? AND status = 1"; try (PreparedStatement ps = conn.prepareStatement(updateAppointment)) { ps.setInt(1, appointmentId); int rows = ps.executeUpdate(); if (rows == 0) { conn.rollback(); // 预约状态不对,回滚 return false; } } // 第二步:写入成交记录 String insertDeal = "INSERT INTO t_deal(house_id, customer_id, agent_id, deal_price) VALUES(?,?,?,?)"; try (PreparedStatement ps = conn.prepareStatement(insertDeal)) { ps.setInt(1, houseId); ps.setInt(2, customerId); ps.setInt(3, agentId); ps.setBigDecimal(4, dealPrice); ps.executeUpdate(); } // 第三步:把房源标记为已成交 String updateHouse = "UPDATE t_house SET status = 3 WHERE id = ? AND status = 1"; try (PreparedStatement ps = conn.prepareStatement(updateHouse)) { ps.setInt(1, houseId); int rows = ps.executeUpdate(); if (rows == 0) { conn.rollback(); // 房源已经不是上架状态,回滚 return false; } } conn.commit(); // 三步都成功,提交 return true; } catch (SQLException e) { e.printStackTrace(); try { if (conn != null) conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } return false; } finally { JdbcUtil.close(null, null, conn); } }

这段代码要解释清楚两个关键点。第一是setAutoCommit(false)的作用:它告诉SqlServer“先别自动落盘,等我三条语句都执行完再一起生效”。没有这一句,第一条UPDATE成功后数据库就提交了,万一后面的INSERT失败,预约状态就被改掉了但成交记录没落库,数据就不一致了。第二是回滚的时机判断,每一步都用executeUpdate()的返回值确认影响行数,任何一步为0都说明前置状态不对,直接回滚。

事务边界在哪里画,是这个系统设计中最能体现水平的细节。这里的边界是“预约完成+成交记录+房源状态变更”三件事绑在一起,缺一不可。而上下架操作是单表更新,不需要事务。预约创建则只需要插入一行,也不需要事务。原则是一句话:多个表的数据变更必须涉及“同一次业务流程的多个步骤”时才用事务,各表单独操作时别滥用。

6. 避坑与排查:JSP+SqlServer环境里最容易翻车的5个现场

6.1 启动Tomcat后访问404:多模块部署路径对不上

现象是Tomcat启动不报错,但浏览器访问项目路径时一直404,控制台也没有任何异常。

原因通常是两类:一类是项目没被部署到Tomcat的webapps目录,另一类是访问路径和项目的Context Path对不上。IDE里部署时如果没配好“Deployment”选项,项目会以ROOT身份或者带随机路径部署,和你访问的URL不一致。

解决方法是先在浏览器访问http://localhost:8080/确认Tomcat首页能打开,再打开Tomcat的conf/server.xml,看Context Path配置,或者在IDE的部署配置里确认Application Context是/house还是/。最稳妥的排查是看Tomcat启动日志里“Deploying web application”那一段,它会明确打印部署路径。

6.2 SqlServer连不上:端口1433、TCP/IP协议、sa密码三连问

现象是运行代码后报错:The TCP/IP connection to the host localhost, port 1433 has failed。

原因十有八九不是代码问题,而是SqlServer本身没开TCP/IP,或者端口不是1433,也可能是sa账号被禁用。这个报错信息本身就在告诉你:网络层就没通,还没到验证密码那一步。

解决按顺序走:先打开“Sql Server Configuration Manager”,找到“SQL Server网络配置”下的“MSSQLSERVER的协议”,右键TCP/IP选择“启用”;再双击TCP/IP进入属性,确认IP地址页签里IPALL的TCP端口是1433;然后用sa账号在Sql Server Management Studio里登录一次,确认密码没错且账号没有被禁用。三步都完成后再重跑代码。

6.3 页面中文乱码、SqlServer中文存成问号

现象是页面上显示的中文正常,但数据库里存的是“???”,或者反过来页面显示乱码。

原因拆分来看:数据库存成问号,是建表字段用了VARCHAR而不是NVARCHAR,代码页不支持中文;页面显示乱码,是请求或响应的字符集没统一,Tomcat默认用ISO-8859-1去解码传入参数。

解决的三个动作要一起做:建表字段统一用NVARCHAR;JSP页面顶部加<%@ page contentType="text/html;charset=UTF-8" language="java" %>;Tomcat的server.xml里给Connector加URIEncoding="UTF-8"。同时注意,插入中文时如果用的PreparedStatement且字段是NVARCHAR,建议在SQL中用N'中文'字面量,这是SqlServer对Unicode的标准写法,例如INSERT INTO t_user(username) VALUES(N'张三')。这个N前缀很容易被忽略,加了它从源头排除隐患。

6.4 驱动类找不到或版本打架:WEB-INF/lib与Tomcat/lib的边界

现象是报错:ClassNotFoundException: com.microsoft.sqlserver.jdbc.SQLServerDriver,或者No suitable driver found for jdbc:sqlserver://...。

原因分两种:一种是驱动jar确实没放到WEB-INF/lib里;另一种是放了但版本和JDK不匹配,比如新驱动要求JDK 8以上,而你的Tomcat跑在JDK 7上,Class.forName能加载但连接时异常。

解决方法是确认驱动jar包位置在src/main/webapp/WEB-INF/lib下,不要放在Tomcat的lib目录,避免项目迁移时丢包。同时检查驱动版本:sqljdbc4.jar对Java 6/7/8都兼容,新版mssql-jdbc对Java版本有明确要求,装JDK 1.8就用对应版本即可。一个自查技巧是写一个最简单的测试类,只跑Class.forName和getConnection两行代码,排除项目其他代码的干扰。

6.5 连接对象没关:演示十分钟后页面卡死

现象是系统刚启动时一切正常,点了十几个页面后突然卡住不动,Tomcat日志里出现数据库连接超时或者连接被拒绝。

原因是最典型的连接泄漏:DAO里用了DriverManager.getConnection(),但异常路径或正常路径没走close(),连接被占用后一直不释放,把SqlServer的连接数占满了。

解决方法是规范闭环管理。每个DAO方法里都要用try-with-resources或者finally确保连接关闭。这里有一个判断是否存在泄漏的简单办法:在Sql Server Management Studio里执行SELECT * FROM sys.dm_exec_connections WHERE session_id > 50,如果看到大量来自同一个应用进程的连接记录,说明泄漏已经发生。这也是SqlServer自带的一个实用的排查姿势。

7. 答辩前一天:把论文里的测试数据变成现场演示的加分操作

7.1 用一段SQL造出能反复演示的测试数据

答辩时最尴尬的一刻,是演示“查询房源”时数据库里只有一条数据,搜什么都不出效果。我习惯在答辩前用一个脚本造出二十条左右的房源数据,覆盖不同户型和价位区间,让每次搜索都有内容可展示。

-- 造演示数据:循环插入20套房源,覆盖不同户型和状态 DECLARE @i INT = 1; WHILE @i <= 20 BEGIN INSERT INTO t_house (title, address, price, house_type, area, owner_id, status) VALUES ( CONCAT('模拟小区', @i, '号房源'), CONCAT('演示区演示路', @i, '号'), 1500 + @i * 100, CASE WHEN @i % 3 = 0 THEN '2室1厅' WHEN @i % 3 = 1 THEN '1室1厅' ELSE '3室2厅' END, 40 + @i, 1, CASE WHEN @i BETWEEN 1 AND 15 THEN 1 WHEN @i BETWEEN 16 AND 18 THEN 2 ELSE 0 END ); SET @i = @i + 1; END;

这段脚本用WHILE循环批量插入,省去手写二十行INSERT的功夫。状态值做了分布:15条上架可搜索,2条下架,3条待审核,这样演示时可以展示不同状态下按钮的区别。插入时owner_id写死为1,前提是t_user表里id=1的用户真实存在。如果用户表是后来重建的,id可能变了,执行前先查一下再调整这个值。

7.2 现场演示的三个“后悔药”:预启动、缩库、禁外网

到了演示当天,有几个细节是经验之谈。第一个是至少提前半小时把Tomcat和SqlServer启动好,并且把要演示的页面全部点一遍,确认没有“第一次访问编译JSP时的短暂卡顿”。JSP页面第一次被访问时需要转成Servlet再编译,这期间会有几秒延迟,演示时等这几秒会非常尴尬,预启动就是提前触发编译。

第二个是把数据库切到Express版或缩减数据量。如果你用的是完整版的SqlServer,演示时数据量也不大,这个问题不明显;但如果导入了大量测试数据,查询变慢会被老师感知到。毕业设计的数据量控制在几十条就足够展示功能了,不要为了“看起来真实”塞几千条。

第三个是关闭电脑的自动更新和网络下载类软件的自动运行。很多现场翻车不是代码问题,而是演示做到一半系统弹窗更新、杀毒软件弹窗拦截。提前把无关进程关掉,把Windows更新暂停,把屏幕切换成演示模式,这些看起来和代码无关的准备,往往比代码本身更影响答辩结果。

还有一个经验想分享给所有人:答辩前把数据库脚本、连接串、驱动jar这三个东西单独拷到U盘里。如果现场设备不是你自己电脑,这三个缺一个都跑不起来。我见过A同学因为驱动没拷,在答辩现场花二十分钟现场下载,那种体验一次就够。这不是技术问题,是准备工作的教训,希望对你有用。

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

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

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

立即咨询