JavaWeb房地产项目源码解析:从Servlet分层到部署避坑 简介一套基于JavaWeb的房地产管理项目期末大作业源码面向高校JavaWeb课程设计、毕业设计与综合实训场景。项目围绕房源管理、信息查询、数据统计与用户交互展开覆盖房源列表、房源详情、交易记录等核心模块适合需要完整项目参考的初学者快速理解前后端协作方式。压缩包共103个文件约3.12MB包含57个Java源文件、11个JSP页面、6个CSS样式、3个JavaScript脚本以及2个HTML页面另有GIF图标与字体资源用于前端展示Java与JSP负责后端业务逻辑前端文件承担页面表现与交互。资源目录按业务模块与静态资源清晰分开便于快速定位Java类、JSP页面和样式文件二次开发门槛较低已有113人学习下载。通过源码可系统查看JavaBean、JSP、Servlet与前端资源整合的完整流程掌握传统JavaWeb项目的分层写法与页面组织方式也能借助清晰注释和项目结构改造为适合自身的期末作品用作教学参考或实训模板均较合适。1. 期末大作业别东拼西凑这份 JavaWeb 房地产项目源码改完就能答辩期末大作业要用 JavaWeb 做房地产系统最难受的不是不会写而是找来的源码要么缺数据库脚本要么直接是 Spring Boot 全家桶老师一看就知道不是你自己写的。这份 98 个文件的房地产项目源码是个例外57 个 Java 文件把后端分层撑起来11 个 JSP 页面覆盖登录、列表、详情这些核心场景Bootstrap 样式也是现成的离线也能演示。它不是只能截图交差的假 Demo而是能跑通“用户登录→房源列表→房源详情→后台管理”完整链条的 JavaWeb 管理系统。适合正在赶期末大作业的同学、需要一个能改的 JavaWeb 完整案例的开发者以及想练手把老项目迁到 IDEA 的新手。2. 先读懂项目骨架98 个文件里有哪些 JavaWeb 核心技术2.1 文件组成拆解Java 源文件、JSP 和静态资源各司其职拿到源码别急着导入 IDE先花十分钟把文件清单过一遍后面能少踩很多坑。这套源码一共 98 个文件几类关键角色如下文件类型数量职责Java 源文件57实体类、DAO、Service、Servlet 控制器、工具类JSP 页面11服务端渲染页面把 Java 数据输出成 HTMLCSS 样式表6Bootstrap 主题和自定义页面样式JavaScript 脚本3前端表单校验、交互逻辑GIF 图像10页面装饰图和状态图标HTML 页面2登录页这类静态壳页面IML 项目文件1IDEA 模块描述导入项目时会被识别.gitignore / LICENSE各 1版本管理忽略规则和许可声明Java 源文件占了 57 个意味着后端不是一坨逻辑全堆在 Servlet 里而是按层拆开的。常见拆分是 entity 对应数据库表dao 做 JDBC 操作service 处理业务servlet 接收请求和跳转页面util 负责数据库连接和编码处理。这套结构其实就是教材上讲的三层架构展示层、业务层、数据层。你在答辩时只要把这条讲清楚老师就会觉得你懂架构而不是只会拖控件。从文件前缀也能看到技术选型bootstrap.css、bootstrap.min.css、bootstrap-theme.css 这三件套确定前端基于 Bootstrap 3glyphicons-halflings-regular.eot 是字体图标文件配套 Glyphicons 用。静态资源全部内置意味着演示时可以不依赖外网 CDN断网状态下按钮和图标依然正常显示。这对现场答辩很重要很多同学的页面在实验室网络环境下样式全崩多半是因为引了外部链接。提示表格里的文件分类可以原样搬进大作业的“项目结构”章节比贴十行代码更有说服力。2.2 从请求到响应的完整链路JSP、Servlet、DAO 怎么协作要判断一个 JavaWeb 项目能不能跑核心是看懂一条请求的走向。以“查看房源列表”为例顺序是这样浏览器输入 URL 后请求先到 web.xml 或注解里配置的 Servlet 映射HouseServlet 收到请求调用 HouseService 的 list 方法HouseService 再调 HouseDAO 的 findByPage构造 SQL 去数据库查询查询结果封装成 ListHouse 返回给 ServletServlet 把列表放到 request 作用域用 forward 转发给 house_list.jspJSP 用 EL 表达式和 JSTL 标签把数据循环渲染出来。下面这个 Servlet 片段是这条链路的后端入口为看清主流程我简化了异常处理WebServlet(/house/list) public class HouseServlet extends HttpServlet { private HouseService houseService new HouseService(); Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 接收分页参数不传就默认第一页 int pageNo Integer.parseInt(request.getParameter(pageNo) null ? 1 : request.getParameter(pageNo)); int pageSize 10; // 调用业务层拿分页结果 ListHouse houseList houseService.listByPage(pageNo, pageSize); // 把结果放进 request转发给 JSP 渲染 request.setAttribute(houseList, houseList); request.getRequestDispatcher(/WEB-INF/jsp/house_list.jsp) .forward(request, response); } }这段代码有三个参数值得注意。pageNo 是页码pageSize 是每页条数这里写成固定值 10 是为了控制演示数据规模实际可以从前端隐藏域或下拉框读取。request.setAttribute 的 key 是“houseList”JSP 里取数据的名字必须和它一致。getRequestDispatcher 用的是 forward 而不是 sendRedirect区别在于 forward 不改变浏览器地址栏而且是服务端内部转发request 里塞的数据能带到 JSPsendRedirect 会发一个新的 HTTP 请求request 里的数据就丢了。再看 DAO 层这段负责真正和数据库打交道public ListHouse findByPage(int pageNo, int pageSize) { String sql SELECT id, title, area, price, status FROM t_house ORDER BY id DESC LIMIT ?, ?; ListHouse list new ArrayList(); try (Connection conn DbUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { // 偏移量从 (pageNo-1)*pageSize 开始第二页取第 11 条 ps.setInt(1, (pageNo - 1) * pageSize); ps.setInt(2, pageSize); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { House h new House(); h.setId(rs.getInt(id)); h.setTitle(rs.getString(title)); h.setArea(rs.getBigDecimal(area)); h.setPrice(rs.getBigDecimal(price)); h.setStatus(rs.getString(status)); list.add(h); } } } catch (Exception e) { e.printStackTrace(); } return list; }LIMIT ?, ? 是 MySQL 分页的标准写法两个问号分别是偏移量和每页条数。PreparedStatement 用占位符传参既能防 SQL 注入也能让同一条 SQL 反复执行时被数据库缓存。第一次接触这段代码的同学容易栽在偏移量上第 n 页的偏移量是 (pageNo - 1) * pageSize而不是 pageNo * pageSize第二页应该从第 11 条开始取不是从第 20 条。配合 JSP 那一段循环渲染的逻辑也很直观%-- items 对应 Servlet 里 setAttribute 的 keyvar 是循环变量 --% c:forEach varhouse items${houseList} tr td${house.id}/td td${house.title}/td td${house.area}平方米/td td${house.price}/td td${house.status}/td /tr /c:forEachc:forEach 的 items 指向 Servlet 里 setAttribute 的名字var 是循环变量名。${house.title} 这种 EL 表达式会自动调用 House 的 getTitle 方法所以实体类的字段名和 JSP 里的属性名必须一一对应。少了 JSTL 库时页面会报错报错信息一般是“can not find the tag library descriptor”这时去 web.xml 同级的 lib 目录确认有没有 jstl.jar 和 standard.jar。2.3 为什么这个结构适合当大作业分层边界与扩展点理解了请求链路再回看这套源码为什么适合直接改。传统 Servlet JSP 项目写得好不好就看层与层之间边界有没有被打破。这套代码的边界是清晰的第一实体类只放字段和 getter/setter不掺数据库操作表结构变了只动 DAO第二Service 层面向业务方法编程不直接暴露 SQL第三Servlet 里不写 SQL只做参数解析、调用业务和页面跳转。这些边界对期末大作业尤其重要。答辩时老师大概率会问“你改过哪一块”如果你能回答“我只动了 DAO 里的 SQLService 和 Servlet 没改”这个回答比背一堆概念加分得多。反过来说如果源码本身是逻辑全堆在 Servlet 里的粘糕代码你想改一个查询条件都要顺着调用链翻半小时那就没有一点可维护性可言。两个值得练手扩展的点一是把房源列表改成卡片式布局只改 JSP 循环体里的 HTML 结构二是给房源表加一个“状态”筛选下拉框需要在 DAO 里拼动态 SQL。第二点要注意拼 SQL 时不要直接拼接用户输入而是动态加占位符再 setString比如先拼出 where 11再根据条件追加 and status ?。这个写法能向老师证明你懂 PreparedStatement 和动态查询的边界。这个项目没有引入 Spring 全家桶数据库连接靠工具类手动注册驱动这对大作业反而减负。你可以在答辩时主动说我了解 Spring 的 IOC 和 AOP 思想但项目刻意保留原生 Servlet 结构方便把 JavaWeb 底层原理讲清楚。这句话的意思是告诉老师我不是不会用框架是想让作业体现这门课的核心知识点。3. 把项目跑起来IDEA 导入、数据库配置与 Tomcat 启动3.1 环境选型JDK 1.8、Tomcat 8.5、MySQL 5.7 是怎么配出来的看文件列表里有 IML 文件能确定这个工程是用 IDEA 创建的。导入前先把环境统一一下我见过太多项目跑不起来的案例八个里有五个是版本问题。JavaWeb 老项目最省心的一套组合是 JDK 1.8、Tomcat 8.5、MySQL 5.7。JDK 选 1.8 的理由很简单Tomcat 8.5 对 Java 8 的支持最成熟JSP 编译器 Jasper 在 1.8 上运行流畅教科书和网络课程默认配置也是这一套。JDK 11 或更高版本会让部分 JSP 标签库和反射代码出现兼容性警告不必在期末阶段跟自己过不去。MySQL 选 5.7 是因为 5.7 的 JDBC 驱动加载方式和时区处理比较省事如果你本机只有 MySQL 8也不是不能用在后面连接 URL 上加 serverTimezoneAsia/Shanghai 就行。Tomcat 不需要安装到系统目录用 IDEA 的集成配置就行。解压版 Tomcat 放在任意目录都可以IDEA 的 Run Configuration 里指定一下 Tomcat Home 指向解压目录即可。环境变量要确保 JAVA_HOME 指向 JDK 安装目录Tomcat 启动脚本依赖 JAVA_HOME 来定位 java 命令JAVA_HOME 配错最典型的报错是“Cannot find specified VM install”。3.2 导入源码到 IDEA两种方式与部署配置导入有两种情况。源码目录里有 IML 文件时直接用 File → Open 选中项目根目录IDEA 会识别模块信息没有或损坏时用 File → New → Project from Existing Sources选择项目根目录模块类型选 Java 或 EclipseIDEA 会自动重新扫描类路径。具体步骤照着走打开 IDEA在 Project Structure → SDKs 里确认已经添加 JDK 1.8。用 File → Open 打开源码目录等 IDEA 索引完成。进入 Project Structure → Modules把项目语言级别 Language level 设为 8。进入 Project Structure → Libraries确认没有红色缺失的 jar 依赖。点击 Run → Edit Configurations添加 Tomcat Server → Local。在 Deployment 标签页添加 Artifact把项目打成 war exploded 包Application context 设为/。第 6 步是新手最容易漏的一环。很多人导入完直接点运行Tomcat 日志没有任何异常浏览器访问却 404原因就是 Deployment 下面空的IDEA 没有自动帮你部署任何东西。没有 Artifact就没有可访问的上下文路径这是 JavaWeb 项目在 IDEA 里翻车的第一大来源。如果想理解部署路径的映射关系可以看 server.xml 里的 Context 写法来类比!-- path 是浏览器访问的上下文路径docBase 是项目在磁盘上的位置 -- Context path/ docBasehouse_estate reloadabletrue /path 是浏览器访问的上下文路径docBase 是项目在磁盘上的位置reloadable 为 true 表示类或 JSP 改动后自动重载。IDE 集成部署时会在内部生成类似配置实际使用中你不需要手改 server.xml但理解这个映射关系能帮你排查 404如果你部署时把 path 改成了 /house浏览器就要访问 http://localhost:8080/house/login.jsp而不是根路径。3.3 数据库初始化建表、导入数据和修改连接参数数据库是这套源码能不能正常跑起来的命根子。先建库建表给一个最小可用的脚本包含房源表 t_house 和用户表 t_user-- 创建数据库utf8mb4 比 utf8 更省心冷门字符也不会静默出错 CREATE DATABASE IF NOT EXISTS house_estate DEFAULT CHARACTER SET utf8mb4; USE house_estate; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, role VARCHAR(20) DEFAULT user, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE t_house ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, area DECIMAL(10,2) NOT NULL, price DECIMAL(12,2) NOT NULL, status VARCHAR(20) DEFAULT on_sale, owner_name VARCHAR(50), create_time DATETIME DEFAULT CURRENT_TIMESTAMP );字段里的 username 设置 UNIQUE 约束作用是防止注册重复账号status 存 on_sale、sold 这类业务状态字符串而不是布尔值这样以后加“pending”这类状态不用改表结构。表名前缀 t_ 是我个人的命名习惯源码里的 SQL 写的是哪张表就以源码为准你要做的是让表名和 DAO 里的 SQL 完全对得上。连接参数一般集中在 jdbc.properties 或 DbUtil 工具类里。以 properties 文件为例jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/house_estate?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password123456useUnicode 和 characterEncoding 保证中文不乱码serverTimezone 只有 MySQL 8 驱动才需要加5.7 可不加。如果你的 MySQL 端口不是 3306jdbc.url 里的端口要同步改。改完连接参数后注意重新编译一次因为 Java 里的属性文件会复制到 classes 目录不重新编译的话运行时的 Tomcat 用的还是旧参数这是“我明明改了却没用”的最常见原因。3.4 启动与验证没有 404 才算第一步全部配置到位后点击 Tomcat 的运行按钮观察 IDEA 底部 Log 面板。出现“Artifact is deployed successfully”或者“Deployment of web application archive ... has finished”说明项目已经被发布到 Tomcat。如果日志里有红色异常优先看有没有 SQL 相关报错数据库连接问题在启动阶段最常见。启动后用浏览器访问 http://localhost:8080/能看见登录页或系统首页说明项目本体已经跑通。想验证后端接口是否真的在响应可以用这条命令# -i 能看到响应头200 正常302 说明被过滤器拦到登录页404 才需要查部署路径 curl -i http://localhost:8080/house/list返回 200 说明接口正常返回 302 可能是被登录过滤器拦下来重定向到登录页这同样是过滤器在工作的表现只有返回 404 才需要回到 Deployment 排查部署路径。我第一次拿这类 JavaWeb 老项目练手时卡在 404 上一小时最后发现就是 Deployment 没加 Artifact。从那以后我每次新导入一个项目固定动作只有两下先看 Libraries 有没有红标再看 Deployment 有没有 Artifact。这两处没问题项目基本能在十分钟内跑起来。4. 把核心业务改明白登录权限、房源管理和数据统计4.1 用户登录的完整流程与权限拦截一个房地产管理系统登录和权限是最先被验收的功能。整套流程分三步登录页提交表单LoginServlet 校验账号密码成功后把用户对象放进 session。之后的每一次请求都经过一个过滤器判断 session 里有没有用户没有就重定向回登录页。这个 AuthFilter 是整个权限控制的核心WebFilter(/*) public class AuthFilter implements Filter { 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(); // 静态资源和登录接口不拦截否则页面会没有样式 if (uri.endsWith(.css) || uri.endsWith(.js) || uri.endsWith(.png) || uri.endsWith(.gif) || uri.endsWith(.jpg) || uri.equals(request.getContextPath() /) || uri.endsWith(/login) || uri.endsWith(/login.jsp)) { chain.doFilter(req, resp); return; } // 核心判断session 里没有用户就踢回登录页 Object loginUser request.getSession().getAttribute(loginUser); if (loginUser null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); } }这段过滤器的逻辑是两个分支都放行但语义完全不同。第一个分支是“白名单放行”静态资源没有业务数据不能拦第二个分支是“已登录放行”有合法身份才能进业务页面。写在最前面的这段静态资源判断非常关键如果漏掉你会发现页面打开只有 HTML没有 CSS按钮没法点整个界面像被扒了一层皮。登录信息的存取还有一个细节用户对象放进 session 用的是 loginUser 这个 key后续 JSP 里判断是否登录、页面顶部显示“欢迎 xxx”都靠这个 key 取值。如果你改成 user记得过滤器、页面、Servlet 三处全部同步改漏一处就会出现“登录成功了但页面还让我登录”的灵异体验。管理员和普通用户的区别在 t_user 表的 role 字段。过滤器只管“有没有登录”不管“是不是管理员”更细粒度的权限可以在 HouseServlet 里判断 role 是否为 admin再决定能不能进入后台管理页面。大作业做到这层就够了不需要再引入 Shiro 这类权限框架。4.2 房源信息管理从表单到数据库的增删改查房源管理的闭环是新增、列表、修改、删除。核心入口是 HouseServlet 的 doPost它接收表单数据并封装成 House 对象protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 这个必须放在第一个 getParameter 之前否则 POST 中文乱码 request.setCharacterEncoding(UTF-8); House house new House(); house.setTitle(request.getParameter(title)); house.setArea(Double.parseDouble(request.getParameter(area))); house.setPrice(new BigDecimal(request.getParameter(price))); house.setStatus(request.getParameter(status)); house.setOwnerName(request.getParameter(ownerName)); boolean ok houseService.addHouse(house); if (ok) { // 新增成功用重定向刷新页面不会把同一条房源插两遍 response.sendRedirect(request.getContextPath() /house/list); } else { request.setAttribute(error, 新增失败); request.getRequestDispatcher(/house_add.jsp).forward(request, response); } }两个关键点。第一个是 request.setCharacterEncoding(UTF-8) 必须放在第一个 getParameter 之前否则 POST 提交的中文标题直接变乱码。第二个是新增成功用 sendRedirect 而不是 forward虽然二者都能跳转但 forward 是服务端转发浏览器地址栏不变用户按一次 F5 就把同一条房源再插一遍sendRedirect 会发新的请求地址栏变成列表地址刷新也不再触发新增逻辑。删除和修改本质是同一个路径。修改时先根据 id 查出原数据回填到表单提交后走 update 方法删除时表单只传一个 idDAO 执行 delete。这里最容易忽略的是参数校验比如 area 传了空字符串Double.parseDouble 直接抛 NumberFormatException页面白屏。常见做法是在 Servlet 里先做非空判断或者在前端 JS 里用正则限制输入两道校验才能覆盖正常用户和手滑用户。4.3 数据统计与信息查询答辩加分的小功能房地产系统通常要展示统计数字大作业里不用做复杂报表两个指标就够房源总数、按状态分类统计。SQL 长这样SELECT COUNT(*) AS total_count FROM t_house; SELECT status, COUNT(*) AS cnt FROM t_house GROUP BY status;Java 端把统计结果放进 Map 返回给页面public MapString, Integer countByStatus() { String sql SELECT status, COUNT(*) AS cnt FROM t_house GROUP BY status; MapString, Integer map new HashMap(); try (Connection conn DbUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { // 别名 cnt 必须和 SQL 里 AS 后面的名字完全一致 map.put(rs.getString(status), rs.getInt(cnt)); } } catch (Exception e) { e.printStackTrace(); } return map; }rs.getInt(cnt) 里的别名必须和 SQL 里 AS 后面的别名完全一致这是新手最容易踩的坑写错就报 Column cnt not found。另外 GROUP BY 的结果如果存在 NULL 状态HashMap 的 key 允许一个 null但统计口径会变得混乱常见做法是 SELECT COALESCE(status,unknown) AS status把空值归入 unknown 类别再放进 Map。信息查询功能也很重要通常是在列表页加一个搜索框DAO 里拼 LIKE 条件String sql SELECT * FROM t_house WHERE title LIKE ? ORDER BY id DESC; // 模糊匹配的 % 拼在参数里而不是拼进 SQL 字符串避免注入 ps.setString(1, % keyword %);占位符里的 % 要拼在参数里而不是拼在 SQL 字符串里这样能避免直接拼接用户输入造成注入。查询的坑往往不在 SQL而在分页链接列表页点击第二页时keyword 参数必须一起带到下一个请求即 href 要写成 ?pageNo2keyword花园否则你会看到“明明搜索了翻页后却变成全量数据”的反常表现。我一般会把搜索框放在表单里用 GET 方式提交让参数自然出现在地址栏翻页时直接拼接 request.getParameter(keyword)。5. 避坑指南项目移植和答辩前常翻车的五个问题5.1 环境部署阶段的坑404、端口冲突与 JDK 版本坑 1Tomcat 能启动页面却全部 404 现象日志不报错浏览器访问 /house/list 和根路径都是 404。 原因IDEA 的 Tomcat 配置里没有添加 Deployment Artifact项目根本没有发布到 webapps 目录。IDEA 不会根据导入自动帮你部署这是新手最常踩的隐形坑。 解决打开 Run → Edit Configurations切到 Deployment 标签页点加号添加 Artifact选择项目的 war exploded把 Application context 设为 /重启 Tomcat。这个操作的本质是告诉 IDEA要把这个项目打包后放进 Tomcat 的发布目录。坑 2Tomcat 启动报端口被占用 现象点击运行后日志出现 Port 8080 required by Tomcat v8.5 Server或者直接抛 java.net.BindException浏览器访问不了。 原因8080 端口被其他进程占用最常见的来源是之前没关干净的 Tomcat 实例或者是其他开发工具占用了 8080。 解决在命令行执行 netstat -ano | findstr :8080找到占用进程的 PID到任务管理器结束它或者直接把 Tomcat 的 HTTP 端口改成 8088。改动位置在 Run Configuration 里的 HTTP port或者 conf/server.xml 里 Connector 节点的 port 属性。坑 3JSP 页面编译报错提示 Java 版本过低或过高 现象项目启动正常一访问 JSP 页面就报 Unable to compile class for JSP后台日志堆栈指向 Jasper 编译器。 原因Project SDK 和 Tomcat 的 Java 版本不一致比如用了 JDK 17Tomcat 8.5 的 Jasper 还在按 Java 8 的字节码规格编译兼容性处理不上。 解决把 Project Structure 里的 SDK 统一成 JDK 1.8检查 Settings → Build → Java Compiler 里的 Target bytecode version 设为 8最后 Rebuild Project。很多人只改了 SDK没改 compiler 的 target一样会报错。5.2 数据与编码阶段的坑乱码、连接失败和中文参数坑 4页面中文全部变成问号 现象登录后界面中文正常新增房源提交的中文标题列表页显示成 ???或者从开始就整体乱码。 原因三层里至少有一层编码不对。数据库连接 URL 没加 characterEncodingServlet 没设置 request.setCharacterEncodingJSP 页面没声明 pageEncoding都可能各乱各的。 解决把这三处一次性对齐。连接 URL 加 useUnicodetruecharacterEncodingutf-8每个接收 POST 的 Servlet 第一行加 request.setCharacterEncoding(UTF-8)JSP 顶部确认 pageEncodingUTF-8。三处统一后再测如果还乱码就去检查 MySQL 表的 COLLATE 是不是 utf8_general_ci以及数据库连接工具类里有没有把编码写死成 ISO-8859-1。坑 5数据库连接报 Access denied for user 现象点登录或访问列表页页面报 SQLException 堆栈里面写着 Access denied for user rootlocalhost (using password: YES)。 原因jdbc.properties 或 DbUtil 类里写的账号密码和你本机 MySQL 不一致。源码默认的账号密码通常不是你的最常见是默认 password 为 123456而你的 root 密码是别的。 解决打开 jdbc.properties把 jdbc.username 和 jdbc.password 改成你自己 MySQL 的账号密码保存后 Rebuild Project 再重启。注意改完一定要重新编译否则 Tomcat 运行时读的还是 classes 目录里的旧文件。最后一个提醒数据库连接类里如果把密码写死在 Java 字符串里比如 DriverManager.getConnection(url, root, 123456)那改完 properties 文件也别指望生效必须改 Java 代码里的字面量。这个细节在答辩时很容易被临时环境坑到提前把账号密码调成一致的比现场改代码省心得多。6. 交作业前的最后一遍自查功能验证清单与答辩演示逻辑离交付还有半天时间别急着打包把下面五个检查项过一遍能挡掉九成低级翻车。这套表格是我实践下来的习惯每次交 JavaWeb 大作业都按它收尾。检查项操作方式通过标准登录链路用预置账号登录再退出重登能进入首页退出后 session 失效、跳回登录页房源列表查询不带关键词查一次带关键词再查一次列表正常渲染关键词过滤后条数减少新增房源提交一条中文标题的房源回显无乱码刷新页面没有重复插入权限控制不登录直接访问 /house/list跳转到登录页而不是直接暴露页面数据库还原导出 house_estate.sql换一台机导入能重建全部表和数据答辩演示我不建议照着代码讲而是按“需求 → 功能 → 技术”三段走。第一句话说清楚项目是什么、解决谁的什么需求比如“我做了一个基于 JavaWeb 的房地产信息管理系统支持房源管理、用户登录、数据统计三类核心功能”这句话已经把项目的定位和范围钉死。第二段现场操作最关键的两条链路新增一条房源再到列表页搜出来用看得见的结果证明功能是真的。第三段只挑一个自己改过的细节讲比如过滤器放行规则、分页偏移量讲清楚“为什么这么写”比背三十页 PPT 管用得多。从那以后我每次交作业或帮人看项目都会强制走一遍这张表的五个检查项尤其是“不登录直接访问列表”这一条几乎每次都筛出几个忘了放行静态资源的页面。数据库账号密码也一定在打包前和源码对齐不然上台演示时数据库连不上之前做的功能全部白搭。这份基于 JavaWeb 的房地产项目源码建议你先跑通、再改动、最后对照清单验收把每一步都踩实了你会发现大作业这东西其实没那么玄。希望帮到你。本文还有配套的精品资源点击获取