ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Servlet+JSP+JDBC房屋租赁管理系统开发实战:从设计到部署

Servlet+JSP+JDBC房屋租赁管理系统开发实战:从设计到部署 简介这是一套基于ServletJspJDBC的房屋租赁管理系统完整毕业设计资料包面向计算机相关专业正在准备毕设的学生以及需要JavaWeb实战练习的开发者。系统功能覆盖前台会员登录、免费注册、忘记密码、信息检索、留言板和公告浏览以及后台出租求租、出售求购、交易记录、统计报表和房屋租金报表等模块技术栈涉及JavaWeb、Servlet、JSP、jQuery、CSS运行于JDK8支持IDEA或Eclipse配合Navicat连接SQL Server数据库。包体包含项目源码、数据库脚本、毕业论文、系统详细配置指导以及PPT压缩包约13.69MB。其中源码经调试可导入开发工具直接运行数据库脚本用于初始化房屋与交易数据配置指导帮助快速搭建环境论文与PPT便于完成毕业设计文档与答辩展示整体结构清晰可直接作为毕业设计基础或项目实战练手。目前已有250人学习/下载对于需要一套可运行、易扩展的房屋租赁管理系统参考方案的读者而言具有很高的实用价值。1. 基于 ServletJSPJDBC 的房屋租赁管理系统这套老技术栈为什么还有人在做房屋租赁管理系统是 JavaWeb 方向最常见的毕业设计题之一也是新手能真正跑通“浏览器 → Servlet → JDBC → MySQL 数据库”全链路的最小完整项目。这套系统要管的不只是“把房源信息存进数据库”而是围绕「房东、租客、房屋、合同」四个核心对象完成信息发布、租赁登记、合同生成和统计查询。对正在选毕设题或者刚学完 JavaWeb 的读者来说它最大的价值在于不需要接触 Spring 全家桶只要弄懂 HTTP 请求、Servlet 生命周期、JSP 标签、JDBC 增删改查这四件事就能把系统完整落地。本文按一个能交付验收的标准来讲这套系统的选型理由、数据库设计、核心代码写法、环境配置和上线打包的完整路径。2. 为什么还在用 ServletJSPJDBC技术选型与开发环境搭建2.1 这套技术栈的真实边界在哪里很多人一听到 ServletJSP 就觉得过时但实际做这套租赁系统它反而是最能讲清楚原理的选择。Spring Boot 能帮你省掉配置但也会把请求怎么进 Servlet、参数怎么绑定、连接怎么管理这些关键环节全部封装成黑匣子。毕业设计答辩时老师问“登录状态怎么保持的”“查询条件怎么拼接的”如果你从 Spring Security 和 MyBatis-Plus 写起大概率只能背术语用 ServletJSPJDBC你自己写的每一行代码都能成为答辩素材。这套技术栈的边界也很明确适合并发量不大、业务逻辑清晰、表数量在十张以内的管理系统。房屋租赁系统恰好符合——没有高并发秒杀没有复杂支付没有分布式需求。最重的操作也就是房东发布房源、租客检索房源、生成租赁合同、管理员统计这几个动作。如果未来要扩展业务层可以换成 Spring但 Servlet 层和数据库层的设计仍然可以复用。2.2 开发环境版本搭配与下载清单做这套系统前先把版本对齐否则后面排查环境问题的时间比写代码还长。我建议的版本组合是组件推荐版本说明JDK1.8最稳妥兼容 Tomcat 8.5 和 9.xTomcat8.5.x 或 9.0.x支持 Servlet 3.1/4.0MySQL5.7 或 8.05.7 对新手友好8.0 需要配置驱动参数开发工具IDEA 或 EclipseIDEA 社区版足够MySQL 驱动mysql-connector-java 5.1.49对应 MySQL 5.7/ 8.0.x对应 MySQL 8.0经典坑驱动版本与数据库版本不匹配常见做法是直接用 IDEA 新建一个普通的 Java Web 工程而不是 Maven 工程。原因很实际Maven 会自动联网下载依赖如果网络不稳或者私服配置有问题经常出现 IDEA 自动下载 Maven 依赖失败。传统方式只要手动把 mysql-connector-java 的 jar 包拷进 WEB-INF/lib就不会有这类问题。提示开发环境不是越新越好。JavaWeb 老项目用 JDK 8 Tomcat 8.5 MySQL 5.7 是能少踩很多坑的组合尤其是做毕业设计时间紧张的同学别在环境上折腾太久。2.3 创建工程的标准目录结构一个规范的 JavaWeb 工程应该这样组织后面扩展时不会乱house-rental/ ├── src/ │ ├── com/house/rental/ │ │ ├── servlet/ // 控制层LoginServlet、HouseServlet... │ │ ├── service/ // 业务层HouseService、ContractService │ │ ├── dao/ // 数据访问层HouseDao、UserDao │ │ ├── entity/ // 实体类House、User、Contract │ │ └── util/ // 工具类DBUtil、StringUtil │ └── com/house/rental/filter/ // 编码、登录拦截过滤器 ├── web/ │ ├── static/ // CSS、JS、图片 │ ├── jsp/ // house-list.jsp、house-add.jsp... │ ├── WEB-INF/ │ │ ├── web.xml │ │ └── lib/ // mysql-connector-java.jar 等 └── sql/ └── house_rental.sql // 建库建表脚本这个分层方式对应 MVC 思想Servlet 接收请求并控制页面跳转Service 写业务规则比如发布房源前校验房东身份DAO 用 JDBC 操作数据库JSP 只负责展示。把分层做好后面答辩时“为什么这么设计”这一问就稳了。3. 核心功能实现从用户登录到房屋检索与合同生成3.1 登录功能与 Session 状态管理servlet 生命周期的一次完整演示登录功能是所有管理系统的入口。这里你会发现 servlet 生命周期不是纯理论——用户点“登录”按钮之后一个请求先经过 TomcatTomcat 判断没有对应实例就调用构造器和 init()然后执行 service() 分发到 doPost()你在这里验证用户名密码最后用完进入 service() 返回响应。整个流程走一遍比背十遍“init—service—destroy”都管用。登录逻辑代码示例WebServlet(/login) public class LoginServlet extends HttpServlet { private UserDao userDao new UserDao(); Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String username request.getParameter(username); String password request.getParameter(password); // 调用 DAO 层验证用户注意密码比对要在 Service 层做 MD5 加密后比对 User user userDao.findByUsernameAndPassword(username, MD5Util.md5(password)); if (user null) { request.setAttribute(errorMsg, 用户名或密码错误); request.getRequestDispatcher(/jsp/login.jsp).forward(request, response); return; } // 登录成功将用户信息存入 Session供 JSP 页面用 HttpSession session request.getSession(); session.setAttribute(loginUser, user); // 重定向到首页而不是转发避免刷新页面重复提交表单 response.sendRedirect(request.getContextPath() /house/list); } }这段代码里有三个细节值得记住。第一密码不能明文比对Service 层里统一用 MD5再加盐更好处理后再查库。第二request.getParameter 取到的全是字符串涉及金额、日期时要注意类型转换。第三登录成功后用 sendRedirect 而不是 forward——因为重定向是让浏览器重新发起一次 GET 请求到 /house/list地址栏变化了F5 刷新时不会把登录表单重新提交一遍这是新手很容易踩的“刷新页面导致重复登录/重复下单”坑。3.2 房屋发布与多表关联查询JDBC 增删改查的完整业务载体房屋发布是租赁系统的核心动作。房源上架时要同时写入房屋基本信息和房东信息状态还要设置为“待审核”。这一步背后是 service 层事务控制——房屋表和房东表要同时成功要么都失败。来看一个带事务的 DAO 写法public boolean addHouseAndLandlord(House house, Landlord landlord) { Connection conn null; PreparedStatement pstmtHouse null; PreparedStatement pstmtLandlord null; try { conn DBUtil.getConnection(); // 手动开启事务JDBC 默认自动提交多条 SQL 必须关掉这个默认值 conn.setAutoCommit(false); String sqlLandlord INSERT INTO landlord(name, phone, id_card) VALUES(?, ?, ?); pstmtLandlord conn.prepareStatement(sqlLandlord, Statement.RETURN_GENERATED_KEYS); pstmtLandlord.setString(1, landlord.getName()); pstmtLandlord.setString(2, landlord.getPhone()); pstmtLandlord.setString(3, landlord.getIdCard()); pstmtLandlord.executeUpdate(); // 取出自增主键作为房屋的外键 landlord_id ResultSet rs pstmtLandlord.getGeneratedKeys(); int landlordId 0; if (rs.next()) { landlordId rs.getInt(1); } rs.close(); String sqlHouse INSERT INTO house(address, area, price, landlord_id, status) VALUES(?, ?, ?, ?, PENDING); pstmtHouse conn.prepareStatement(sqlHouse); pstmtHouse.setString(1, house.getAddress()); pstmtHouse.setBigDecimal(2, house.getArea()); pstmtHouse.setBigDecimal(3, house.getPrice()); pstmtHouse.setInt(4, landlordId); pstmtHouse.executeUpdate(); conn.commit(); // 两条 SQL 都成功才提交 return true; } catch (SQLException e) { if (conn ! null) { try { conn.rollback(); // 任何一条出错回滚全部 } catch (SQLException ex) { ex.printStackTrace(); } } e.printStackTrace(); return false; } finally { // 资源释放顺序ResultSet → Statement → Connection // Connection 如果是连接池的这里不是真正关闭而是归还给池子 DBUtil.close(pstmtHouse, pstmtLandlord, conn); } }这段代码的要点在于一setAutoCommit(false) 是事务开始标志很多人漏掉这一行导致第一条 SQL 执行完自动提交第二条失败时无法回滚二用 getGeneratedKeys 回填自增主键避免查两次数据库三finally 块里资源释放顺序不能乱如果先把 Connection 释放了再关 Statement连接池中的连接会残留未关闭的语句对象数据库连接池一段时间后就被“占满”。多表关联查询是租赁系统检索功能的基础。前台检索“某区域可租房源房东联系方式”时一条 SQL 就能带出全部信息SELECT h.id, h.address, h.area, h.price, h.status, l.name AS landlord_name, l.phone AS landlord_phone FROM house h INNER JOIN landlord l ON h.landlord_id l.id WHERE h.status AVAILABLE AND h.address LIKE CONCAT(%, ?, %) AND h.price BETWEEN ? AND ? ORDER BY h.create_time DESC LIMIT ?, ?;这条 SQL 里的 LIMIT 是分页的实现核心。传入三个参数关键词、价格下限、价格上限。JDBC 层用 PreparedStatement 的 setString/setBigDecimal 填充这三个问号注意 LIKE 语句在 PreparedStatement 里不能用 %?% 这样写必须用 CONCAT(%, ?, %)这是很多新手直接拼字符串后碰到的第一个 SQL 注入漏洞点。3.3 JSP 页面与分页组件让租客能按条件筛选房源JSP 页面负责展示和收集数据。一个合格的房源列表页要完成三件事展示分页数据、保留查询条件、管理按钮按角色显示。用 JSTL 拼接 HTML 是传统 JSP 项目打包 war 后最常用的做法。%-- house-list.jsp 核心部分 --% % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % % taglib prefixfmt urihttp://java.sun.com/jsp/jstl/fmt % div classfilter-bar form action${pageContext.request.contextPath}/house/list methodget input typetext namekeyword value${param.keyword} placeholder输入区域关键字/ input typenumber nameminPrice value${param.minPrice} placeholder最低价/ input typenumber namemaxPrice value${param.maxPrice} placeholder最高价/ button typesubmit搜索/button /form /div table classhouse-table tr th地址/th th面积/th th月租/th th状态/th th房东/th th操作/th /tr c:forEach items${page.list} varhouse tr td${house.address}/td td${house.area}㎡/td tdfmt:formatNumber value${house.price} pattern¥#,##0.0//td td c:choose c:when test${house.status AVAILABLE}可租/c:when c:when test${house.status RENTED}已租/c:when c:otherwise待审核/c:otherwise /c:choose /td td${house.landlordName} / ${house.landlordPhone}/td td c:if test${sessionScope.loginUser.role ADMIN} a hrefhouse/edit?id${house.id}编辑/a a hrefhouse/delete?id${house.id} onclickreturn confirm(确定删除该房源)下架/a /c:if a hrefcontract/create?houseId${house.id}签约/a /td /tr /c:forEach /table %-- 分页组件 --% div classpagination c:if test${page.currentPage 1} a hrefhouse/list?page${page.currentPage - 1}keyword${param.keyword}上一页/a /c:if span第 ${page.currentPage} / ${page.totalPages} 页/span c:if test${page.currentPage page.totalPages} a hrefhouse/list?page${page.currentPage 1}keyword${param.keyword}下一页/a /c:if /div分页参数是这套系统的必调参数。页码 page 从前端传入后必须做边界校验——小于 1 就置为 1大于总页数就置为总页数否则用户手动改 URL 参数 ?page999 会得到一个空白页甚至 SQL 异常。每页条数我一般设为 10如果房源数据量大到上千条要靠索引和延迟加载配合而不是单纯调大每页条数。JSP 里用 param.keyword 回显搜索条件才能保证翻页时条件不丢这个细节做不好就会让用户点第二页时搜索条件重置。4. 数据库设计与 JDBC 优化表结构、连接池与多数据源注意点4.1 五张核心表的字段设计与外键约束房屋租赁系统的数据模型要围绕“租赁合同”这个核心业务来设计。最少需要五张表表结构直接决定 Servlet 和 DAO 的编写量。表名关键字段说明userid, username, password, role系统登录用户role 区分 ADMIN/AGENTlandlordid, name, phone, id_card房东信息一个房东可有多套房houseid, address, area, price, status, landlord_id, create_time核心房源表status 用枚举字符串tenantid, name, phone, id_card, create_time租客信息签约前先建档contractid, house_id, tenant_id, landlord_id, start_date, end_date, monthly_rent, deposit, status租赁合同表外键最多设计时要注意三个点。第一金额字段用 DECIMAL(10,2) 而不是 FLOAT避免租金计算出现 0.0000001 的精度问题。第二外键约束要建但要分清楚逻辑外键和物理外键——正式项目里所有关联都建物理外键会影响性能和迁移毕设项目建物理外键反而能在答辩时展示你懂数据完整性。第三status 字段用字符串 VARCHAR 加 CHECK 约束比如 AVAILABLE、RENTED、PENDING不要用 0/1 数字可读性差而且扩展第三种状态时不直观。初始化建库脚本这样写CREATE DATABASE IF NOT EXISTS house_rental DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; USE house_rental; CREATE TABLE landlord ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, phone VARCHAR(20) NOT NULL, id_card VARCHAR(18), create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE house ( id INT AUTO_INCREMENT PRIMARY KEY, address VARCHAR(200) NOT NULL, area DECIMAL(6,2) NOT NULL, price DECIMAL(10,2) NOT NULL, status VARCHAR(20) NOT NULL DEFAULT PENDING, landlord_id INT NOT NULL, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_house_landlord FOREIGN KEY (landlord_id) REFERENCES landlord(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- tenant、contract 表结构按相同规范继续加这里 ENGINEInnoDB 必须写清楚。InnoDB 支持外键和事务而 MyISAM 不支持——如果建表时没写MySQL 5.5 之前默认是 MyISAM事务代码就废了。utf8mb4 用四字节存储租房合同里如果出现生僻字或 emoji 才会正常显示这是 jdbc 连接项里的 characterEncoding 写入 SQL 脚本时最容易出现的字符集口径不一致问题。4.2 数据库连接池参数与 JDBC API 封装传统 JDBC 的 Connection 每次创建和销毁开销很大。在 Tomcat 里用连接池只需要配置 context.xml不需要额外引入 jar 包。下面是一套适配 MySQL 8.0 的最小 Database 配置?xml version1.0 encodingUTF-8? Context Resource namejdbc/houseRentalDB authContainer typejavax.sql.DataSource driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/house_rental?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltruecharacterEncodingUTF-8 usernameroot passwordyour_password maxTotal20 maxIdle10 minIdle5 maxWaitMillis10000 initialSize5 validationQuerySELECT 1 / /Context关键参数里maxTotal20 是最大连接数超过后请求会排队如果线上并发超过这个数就要关注慢 SQL不能盲目加大。maxWaitMillis10000 是拿到连接的等待超时超过 10 秒抛异常配太短高峰期会被误杀配太长四五个慢查询就能占满线程。validationQuery 配置成 SELECT 1作用是每次从池里取出连接时验证可用性防止 MySQL 默认的 8 小时超时断开导致拿到的 Connection 变成“死连接”。配套的工具类封装核心是“把连接归还池子而不是关闭”这个语义。DBUtil 的 close 方法要注意这个细节public class DBUtil { private static DataSource dataSource; static { try { Context ctx new InitialContext(); dataSource (DataSource) ctx.lookup(java:comp/env/jdbc/houseRentalDB); } catch (NamingException e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { // 从连接池借出连接不是 new 一个新连接 return dataSource.getConnection(); } public static void close(ResultSet rs, Statement stmt, Connection conn) { if (rs ! null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt ! null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn ! null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }解释一下为什么 close 方法里 Connection 的关闭是安全的归还连接池返回的 Connection 是代理对象它的 close() 不是真的断开 TCP而是把这条连接标记为空闲并放回池中。新手最容易犯的错是在 Service 层里把 conn 暴露给两个 DAO 方法顺序使用第一个方法调用 close 后第二个方法操作一个已经“归还”的连接就会报 Connection is closed。4.3 多数据源场景下的“数据库同步”问题一个系统同时连接多个库比如日志库和业务库分离或者用数据库同步工具做主从读写分离尤其是毕设答辩时老师极可能追问的一套接口。这里只提跟 jdbc 最相关的一点多数据源时一个 Connection 对应一个库不要让 DAO 自己决定用哪条数据源——事务应该控制在 Service 方法内部。具体做法是在 Service 里传入数据源名称参数DAO 方法签名必须接受 Connection 作为参数而不是内部隐藏数据源切换这样同一个事务方法里两条 SQL 才能保证用的是同一个 Connection主从切换不会造成“空事务”。5. 常见问题排查驱动加载失败、中文乱码、事务与连接池踩坑记录5.1 现象Class.forName(com.mysql.jdbc.Driver) 报 ClassNotFoundException这是用 MySQL 8.0 数据库但驱动 jar 还停留在 5.x 时的经典报错。MySQL 8.0 的驱动类名变成了 com.mysql.cj.jdbc.Driver旧名称不再生效。另一种情况是 jar 包放在了 /lib 目录而不是 WEB-INF/lib——Tomcat 的类加载器只认自己环境下的 lib 目录。解决方法是去 mysql-connector-java 8.0.x 版本里找 mysql-connector-java-8.0.26.jar 这类文件拷进 WEB-INF/lib 并重新构建 Artifact。如果用的 IDEA还要记得在项目结构里把 jar 包“添加到库”否则编译能过、运行时报错。5.2 现象页面中文全部显示为“???”字符集有三道关口JSP 文件本身的编码、Servlet 中 request 与 response 的编码、MySQL 表结构和 JDBC URL 的编码。四者只要有一个不一致中文就乱。我现在的习惯是建立文件时全部选 UTF-8web.xml 里再加一个强制过滤器不给漏网之鱼。!-- web.xml 中配置字符集过滤器位置要在所有 Servlet 之前 -- filter filter-nameencodingFilter/filter-name filter-classcom.house.rental.filter.EncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping对应的 Filter 类实现里必须同时设置 request 和 response 的编码。很多教程只写了 request.setCharacterEncoding结果从数据库读出来中文正常但 through response 写回浏览器的 JSON 或页面文字乱码。另外 GET 请求的 query string 编码是 Tomcat 在 URI 层面处理的要在 Tomcat 的 server.xml 里给 Connector 加 URIEncodingUTF-8 才能真正解决 URL 参数乱码很多人改了 filter 没用就是因为漏了这一步。5.3 现象更新或删除房源后数据没有变化这类“修改不生效”的问题十有八九是事务没提交。排查顺序三条第一确认代码里有没有 conn.setAutoCommit(false) 和 conn.commit() 成对出现第二看是否在方法中途 try-catch 吞掉了 SQLException一旦 catch 住没往外抛rollback 不会执行事务也不会提交第三如果是连接池环境确认 close 方法是不是放在 finally 里如果提前 close事务被自动回滚。一个容易被忽略的原因PreparedStatement 复用了同一个 Connection 但没用同一个 transaction 边界。比如一个 Service 方法调用了两个 DAO 方法每个 DAO 方法内部都调用了 DBUtil.getConnection()这实际上是两条连接事务失效。要解决必须把 Connection 从 Service 层传入 DAO 方法内部所有操作共用一段事务。5.4 现象IDEA 自动下载 Maven 依赖失败plugin 报 download from maven failed如果用了 Maven 管理依赖这是最常见的“环境怪现象”。原因是中央仓库地址网络慢或证书问题。解决方法是换阿里云镜像在 settings.xml 里加镜像或者在项目 pom.xml 里拉下来的依赖缺少时先手动安装了 .m2 缓存。但我的习惯是毕设项目不用 Maven手动管理 jar。将 Tomcat 的 lib 目录下自带的一些 jar比如 servlet-api.jar、jsp-api.jar复制到项目的 WEB-INF/lib再在 IDE 里“添加为库”编译运行时完全不用去网上下东西这是最不受环境影响的做法。5.5 现象intellij idea 里用 SQL Server 数据源的 JDBC 自动下载同样报错换了一个库就报相同的 Failure 现象根本原因是 Maven 布鲁克坐标解析出意外版本导致下载失败时 IDEA 会多次自动重试网络不给力就卡死。我们的项目是 MySQL如果在写作业时交叉用了 SQLServer正确做法是手动下载对应版本的 mssql-jdbc 包通过项目结构导入而非让 IDE 网络解析。不管什么数据库jdbc targetServertype 这类新驱动参数在旧项目里根本不存在不要照抄别人 Oracle 的配置——不同数据库驱动 URL 写法完全不同这属于“看帖不认版本”造成的翻车。6. 打包上线与维护从 War 部署到性能参数的进阶技巧6.1 传统 JSP 项目打包 War 并部署到 Tomcat开发完成后最常见的交付方式是打包成 War 文件直接丢到 Tomcat 的 webapps 目录。步骤分四步第一步在 IDEA 里选择 Build → Build Artifacts → 选择你的项目 Artifact如果没配置在 Project Structure → Artifacts 里新建一个 Web Application Exploded再点 Fix 把所有依赖 jar 打包进去。第二步点击 Build Artifact 后选择 Build生成的 War 文件在 out/artifacts/ 目录下。第三步把这个 War 拷到 Tomcat 的 webapps/ 下启动 Tomcat 后它会自动解压部署。第四步浏览器访问 http://localhost:8080/项目名/jsp/login.jsp。部署前的三件检查事项第一确认 lib 下的 mysql 驱动 jar 没有和 Tomcat 自带的 servlet-api.jar 冲突——把 servlet-api 从 lib 中删掉只留 Tomcat 的第二检查 context.xml 里的数据库密码是不是明文且写对的第三确认 web.xml 中欢迎页列表配置了 login.jsp。War 包名尽量用短英文不要带版本号和中文。如果 Tomcat 启动后页面 404在 logs 目录下的 catalina.out 里搜索 Exception大多数情况要么是连接数据库失败要么是 jar 冲突。6.2 上线后的三个必调参数阶段目标是把“能跑起来的项目”变成“能稳定跑的项目”三个关键调整项值得先做。第一JVM 内存参数。Tomcat 的 bin/setenv.shWindows 是 setenv.bat里添加 CATALINA_OPTS-Xms256m -Xmx102mXms 等于初始堆大小Xmx 是最大堆毕设系统 256m 起步。第二数据库连接池连接数上限。maxTotal 默认是 8对房屋租赁这种并发不高的系统20 就够了调大意味着数据库要预留相应连接数MySQL 默认 max_connections 151别盲目调大。第三定期清理临时文件。Tomcat 的 webapps 下每次启动都会创建临时缓存目录长期运行会产生大量 session 文件和日志用 crontab 定期把 work/Catalina 目录下超过 7 天的缓存文件做清理。6.3 从“能跑”到“可用”的验证思路与维护习惯完成基本功能后验证系统是否达到交付标准可以从用户体验和代码健康度两个维度自检验证维度检查项预期结果功能完整性房东发布→管理员审核→租客签约→合同到期退租全链路数据一致数据正确性合同结束日期等于退租日期押金冻结状态正常释放字段更新正确安全基础未登录直接访问 /house/list 会被拦截到登录页重定向生效并发基础5 个用户同时浏览房源页面不卡连接池不爆安全的拦截器很少有人写。写一个 LoginFilter对所有 /jsp/ 下的页面和 /house/、/contract/这些 servlet 路径做校验session 里没有 loginUser 就重定向回 login.jsp。这一步代码量很少但答辩加分极大因为业务系统最忌讳未登录操作后台接口。维护层面我的习惯是每次改数据库结构都在 sql/ 目录下留一份增量脚本命名为 db_2024_v2.sql不直接改原表。租房合同这类核心数据每月导出一次备份 SQL放在本地。这样即使 Tomcat 崩溃重装半小时内能恢复全部数据。整套系统里最值得反复看的不是彩色页面而是 Servlet 中每个 try-catch 的 rollback 和 finally 的资源释放代码。我把“事务要么全成功要么全失败、连接只借不永不关闭”这两条规则刻进了自己的编码习惯里。这个道理在 Spring 的 Transactional 里也适用只是框架帮你隐藏了细节。希望帮到你下次处理这类管理系统时先把数据库表结构和连接池参数定下来再动手写第一行 Servlet 代码你会发现整个开发流程顺很多。本文还有配套的精品资源点击获取
返回列表