
简介这份JavaSwingMySQL房屋租赁管理系统项目完整包含前端界面、后端逻辑、数据库脚本和课程设计文档主要面向Java初学者、数据库课程设计或毕业设计需要完整案例的读者。系统功能覆盖前台浏览与用户注册登录、后台管理员登录、房源信息录入修改删除查询并支持按分类或多条件搜索房源方便租客快速查看房型、价格和房主等详细内容。压缩包共29个文件以11个Java源码和11个class编译文件为核心数据库方面附有house_rental.sql项目配置含.project、.classpath和prefs等Eclipse环境文件另提供jar运行库及docx格式的《Java高级程序设计》课程设计文档整体约3.89MB导入IDE后即可对照学习。源码、SQL和文档配套完整读者既能运行系统了解Swing控件与MySQL增删改查的关联过程也能参考其用户管理、房源管理和查找模块来改造扩展自己的课程作业或毕业设计。目前已吸引342人学习下载适合作为课设起步、功能完善和文档撰写的直接参考。1. 房屋租赁管理系统一套能跑的 Swing MySQL 工程值得下做课程设计或者给小型中介做内部工具时最尴尬的不是需求复杂而是资料搜了一堆打开全是半成品——数据库脚本缺失、Swing 界面和 MVC 分层糊在一起、跑起来就报错。这套 Java Swing MySQL 实现的房屋租赁管理系统属于少见的下载即闭环类型源码能编译、SQL 能建库、文档能对上号。它解决的是房源、租客、合同、收租这四条业务线在单机桌面环境下的数据管理问题适合正在做课程设计的学生、想练手完整 CRUD 的 Java 新人也适合需要快速搭一套内部管理工具的小团队。2. 系统架构与数据库设计先把表结构摸清再改代码资源下载下来别急着点运行。先花半小时把工程目录和数据库脚本过一遍你后面改代码能少踩一半的坑。Swing 工程的结构通常很直白view包放窗口和面板model包放实体类dao或util包放 JDBC 操作入口一般是MainFrame或LoginFrame。2.1 为什么是这个组合Swing 桌面端 MySQL 存储先说选型逻辑。同样是房屋租赁管理系统你可以做成 Spring Boot Vue 的 Web 项目也可以做成 Swing MySQL 的桌面项目但两者的适用场景完全不同。Swing 方案的核心优势是单机部署、零前后端联调成本一个进程里完成界面渲染和数据访问用户装好 JDK 和 MySQL 就能直接跑不需要配置 Tomcat不需要处理跨域不需要考虑浏览器兼容性。MySQL 在这套组合里承担的是数据持久化职责。相比直接用文件存储或者 Derby 这类内嵌数据库MySQL 的好处在于SQL 语法通用表结构设计能直接迁移到其他技术栈支持多表关联查询合同和账单这类业务刚好需要 JOIN课程设计答辩时面试官或老师一定会问表结构设计用 MySQL 设计过的同学能说清楚外键和索引这在 Java 面试里属于高频考点。另外这套系统没有引入 MyBatis 或 Hibernate而是直接用 JDBC 操作 SQL这不是偷懒反而对新手更友好。框架会把 SQL 细节藏起来但 JDBC 会让你看清楚每一次查询是怎么发出、怎么取回的排查问题时思路更直接。2.2 五张核心表业务字段怎么落到关系模型房屋租赁系统的核心业务本质上是三个实体加两个关联过程房源、租客是基础档案合同把房源和租客绑到一起账单记录每次收租。所以数据库里最合理的方案是五张表用户表、房屋表、租客表、合同表、账单表。用户表负责登录认证字段不需要多但用户名必须唯一。房屋表要记录房号、地址、面积、月租金、押金和当前状态其中状态字段是业务流转的关键。租客表存姓名、电话、身份证号、紧急联系人。合同表是系统的枢纽它同时持有房屋 ID 和租客 ID外加起止日期、合同价、押金。账单表记录每一期收租情况按合同 ID 关联。表名核心字段作用t_userusername, password, role登录与权限控制t_househouse_no, address, rent_price, status房源档案status 控制空置/已租/预订t_tenantname, phone, id_card租客档案t_contracthouse_id, tenant_id, start_date, end_date房源与租客的关联绑定t_billcontract_id, period, amount, status收租台账可按合同追溯这里最值得关注的是status字段。房源表里用0-空置 1-已租 2-预订表示状态合同表里用1-执行中 2-已到期表示合同状态这两个状态在签约、退租时会联动修改。新手容易犯的错误是把状态做成字符串直接存比如空置已租看似直观但后续写 SQL 统计还有几套房可租时字符串比较效率低且容易写错整数枚举才是常见做法。2.3 初始化 SQL 脚本建库、建表、造测试数据下载包里的 SQL 脚本一般会包含建库语句、建表语句和测试数据三个部分。我建议你把脚本从头到尾执行一遍执行成功后立刻用 Navicat 或命令行查看表结构确认字段类型与你预期一致再往下走。CREATE DATABASE IF NOT EXISTS house_rental DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE house_rental; -- 用户表登录账号密码字段建议存 MD5不要存明文 CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, real_name VARCHAR(50), role TINYINT DEFAULT 1 COMMENT 1-管理员 2-操作员, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 房屋表状态字段用整数枚举方便统计 CREATE TABLE t_house ( id INT PRIMARY KEY AUTO_INCREMENT, house_no VARCHAR(20) NOT NULL, address VARCHAR(200) NOT NULL, area DECIMAL(8,2), rent_price DECIMAL(10,2) NOT NULL COMMENT 月租金, deposit DECIMAL(10,2), status TINYINT DEFAULT 0 COMMENT 0-空置 1-已租 2-预订, remark VARCHAR(200) );这段脚本里有几个参数值得注意。utf8mb4是 MySQL 8.0 下推荐使用的字符集相比老旧的utf8它能完整支持中文和生僻字避免后续插入特殊字符时出现Incorrect string value报错。DECIMAL(10,2)表示最高 8 位整数加 2 位小数金额字段必须用定点数而不是FLOAT或DOUBLE否则计算累计租金时会出现 0.1 0.2 不等于 0.3 的精度问题。执行脚本后检查三个点第一.sql文件是否用 UTF-8 编码保存用记事本打开另存为时容易变成 ANSI导致中文注释乱码第二是否有USE house_rental;语句很多初学者把建表语句直接粘贴到默认库执行表建到了mysql库里第三测试数据是否覆盖了多种状态比如房屋表里最好同时有空置和已租的记录这样登录后界面不会一片空白。3. 核心功能模块拆解登录、签合同、收租是怎么串起来的表结构只是骨架真正让系统活起来的是业务代码。这部分我们按登录、签约、收租三个关键动作拆开看每个动作都对应一组具体的类和方法。源码的view包和dao包之间通常还有一个service层别跳过它直接写 SQL——分层清晰是这类工程能不能改成自己系统的分水岭。3.1 登录验证PreparedStatement 查询与界面跳转登录功能是所有管理系统的入口逻辑本身不复杂界面获取用户名密码调用 DAO 层执行查询返回结果后决定是否跳转到主界面。但实现细节上有两个点必须讲究SQL 注入防护和密码存储。public User login(String username, String password) { // 参数校验放在最前面空值直接返回避免SQL拼接 if (username null || password null || username.trim().isEmpty()) { return null; } String sql SELECT id, username, real_name, role FROM t_user WHERE username ? AND password ?; // 使用PreparedStatement的占位符传参而不是字符串拼接 // 字符串拼接SQL是注入漏洞的根源这个习惯要从第一个项目养成 try (PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); // 常见做法是存MD5值登录时对输入做MD5后再比对 ps.setString(2, MD5Util.md5(password)); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { User user new User(); user.setId(rs.getInt(id)); user.setUsername(rs.getString(username)); user.setRealName(rs.getString(real_name)); user.setRole(rs.getInt(role)); return user; } } } catch (SQLException e) { e.printStackTrace(); } return null; }这段代码的关键在PreparedStatement。?占位符会把传入值当作数据而不是 SQL 语句的一部分即使密码里包含 OR 11这类内容也只会被当作普通字符串处理。关于密码资源里如果直接用明文比对你可以自行升级为 MD5 或加盐哈希改动的范围很小只涉及登录方法和初始化 SQL 里的密码字段。登录成功后的界面跳转通常在LoginFrame的按钮事件里完成。拿到非空的User对象后new MainFrame(user).setVisible(true)同时dispose()当前登录窗口。这里有一个细节主框架需要当前登录人的role字段用来控制菜单项的可用性比如只有管理员才能看到数据初始化或用户管理按钮。3.2 房源状态流转合同签订时的事务边界签合同是租赁系统里最容易出数据不一致的操作。它同时涉及两张表合同表要插入一条新记录房屋表要把状态从空置改成已租。如果先插合同再改房屋时程序崩溃就会出现合同存在但房屋仍显示空置的脏数据。public void signContract(Contract contract) throws SQLException { String insertContract INSERT INTO t_contract (contract_no, house_id, tenant_id, start_date, end_date, rent_price, deposit) VALUES (?, ?, ?, ?, ?, ?, ?); String updateHouse UPDATE t_house SET status 1 WHERE id ?; // 关闭自动提交手动控制事务边界 conn.setAutoCommit(false); try { try (PreparedStatement ps1 conn.prepareStatement(insertContract)) { // 合同编号使用自定义工具类生成规则如HT-2025-001 ps1.setString(1, contract.getContractNo()); ps1.setInt(2, contract.getHouseId()); ps1.setInt(3, contract.getTenantId()); ps1.setDate(4, contract.getStartDate()); ps1.setDate(5, contract.getEndDate()); ps1.setBigDecimal(6, contract.getRentPrice()); ps1.setBigDecimal(7, contract.getDeposit()); ps1.executeUpdate(); } try (PreparedStatement ps2 conn.prepareStatement(updateHouse)) { ps2.setInt(1, contract.getHouseId()); ps2.executeUpdate(); } // 全部成功才提交 conn.commit(); } catch (SQLException e) { // 任意一步失败都回滚避免合同和房屋状态不一致 conn.rollback(); throw e; } finally { conn.setAutoCommit(true); } }这段代码的核心是事务的 ACID 属性在 JDBC 层面的体现。setAutoCommit(false)之后两条 SQL 处于同一个事务内只有都执行成功才会commit()否则rollback()撤销全部改动。这也是面试官常问的JDBC 如何控制事务的标准答案。配套的操作是退租。退租时状态流转方向相反需要把房屋状态改回0-空置同时把合同状态改成2-已到期还要计算押金退还金额。资源里如果只实现了签约没实现退租你可以按同样的模式在ContractService里补一个terminateContract方法。状态流转的全部边界情况整理如下操作合同表变化房屋表变化事务必要性新签合同插入 status1 记录status 改为 1必须到期退租status 改为 2status 改为 0必须提前解约status 改为 3status 改为 0必须预订房源不操作合同status 改为 2可选3.3 收租与到期账单表怎么支撑业务统计收租是租赁系统里最频繁的日常操作。账单表按合同 账期记录每一笔应收比如合同编号HT-2025-001的账期2025-04金额 2800 元。收租时做的事情很朴素把账单的status从待收改成已收并填写实收日期和支付方式。这里有一个常见的需求争议账单要不要提前生成两种方案都有项目在用。按月提前生成的好处是待收账单列表查询简单坏处是要跑定时任务或手动触发生成按需创建的好处是省事坏处是统计逾期时要用「合同起止日期 月租金」现场计算。课程设计级别的资源通常选按需创建我在自己改项目时也更倾向按需创建减少一张流水表的状态维护成本。账单表对统计功能的支撑体现在一个典型 SQL 上查询某个月份的实收总金额SELECT SUM(amount) FROM t_bill WHERE period 2025-04 AND status 1。如果你后续想加本月应收逾期未收两个指标也是在这个表上做条件聚合。这部分逻辑在BillService里配合JTable展示在收租面板中。4. 避坑与常见问题排查5 个最容易翻车的细节这个项目的坑集中分布在运行环境和数据两个层面。我把个人调试过程中遇到的高频问题按现象、原因、解决三段式整理出来对照排查能省下不少时间。4.1 连接 MySQL 失败驱动版本和连接串是重灾区现象程序启动后立刻报java.sql.SQLException: Access denied for user rootlocalhost或者ClassNotFoundException: com.mysql.cj.jdbc.Driver。原因分两类。第一类是账号密码确实不对MySQL 安装时设置的 root 密码被遗忘第二类是驱动与 MySQL 版本不匹配MySQL 5.x 使用com.mysql.jdbc.Driver MySQL 8.x 使用com.mysql.cj.jdbc.Driver连接串差异也很大。解决方式先确认你本地 MySQL 的版本SELECT VERSION()。如果是 8.x驱动 JAR 用mysql-connector-java-8.0.33.jar这类 8.x 版本连接串写成private static final String URL jdbc:mysql://localhost:3306/house_rental ?useUnicodetruecharacterEncodingutf8 serverTimezoneAsia/Shanghai;serverTimezoneAsia/Shanghai是 MySQL 8.x 必须的5.x 也要加否则报The server time zone value ??? 乱码 is unrecognized。如果确认密码没错但依然拒绝访问去 MySQL 命令行执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码;兼容 8.x 下旧驱动的认证方式。4.2 Windows 上 MySQL 安装后登不进去现象MySQL 安装过程没问题但用命令行mysql -u root -p输入安装时设的密码一直报错或者安装时根本就没设置密码环节。原因MySQL 8.0 的 Windows 安装包在 Installer 界面里设置密码的页面有默认不填密码直接下一步的选项很多人跳过了这一步导致 root 密码为空或未知。另一种情况是服务启动后与 Navicat 连接时的 host 不匹配。解决方式以管理员身份打开命令行停掉 MySQL 服务后用mysqld --skip-grant-tables模式启动服务然后直接在命令行UPDATE mysql.user SET authentication_string...重置密码。这个操作课程设计里一定会遇到建议在文档里单独写一节。日常开发我一般建议装 MySQL 时记录好 root 密码并在系统 Excel 或笔记里留存这个习惯能省掉很多基础调试时间。4.3 中文乱码连接串与 IDE 编码双重作用现象界面按钮显示中文正常但JTable里从数据库查出来的中文全是???或者反过来——数据库存的中文没问题Swing 界面输入的中文存进去就是乱码。原因JDBC 连接串没有指定字符集客户端和服务器端编码不一致。?characterEncodingutf8这个参数缺失时连接使用 MySQL 服务端的默认字符集Windows 下通常是 GBKJava 侧是 UTF-8两边一对就乱。解决方式连接串必须带上useUnicodetruecharacterEncodingutf8。同时检查 IDE 和源码文件的编码设置IDEA 里统一设置File Encoding为 UTF-8.java文件用UTF-8重新编码一次。数据库表已经建好的情况下修改表字符集的语句是ALTER TABLE t_house CONVERT TO CHARACTER SET utf8mb4;。4.4 日期与金额类型字段类型选错的连锁反应现象合同起止日期传参时报Cannot convert value 2025-04-05 from String金额计算出现 19.999999 这类结果或者日期在界面显示成2025-04-05 08:00:00.0。原因日期字段在实体类里用了String在数据库里用了DATETIME类型转换全靠 JDBC 驱动兜底性能差而且格式不可控金额用了double或FLOAT二进制浮点无法精确表示十进制小数。解决方式实体类的日期字段统一用java.util.DateDAO 层用java.sql.Date做转换数据库用DATE类型。金额一律使用BigDecimal数据库用DECIMAL(10,2)。Swing 的JTable渲染金额时getCellValue返回的BigDecimal直接toString()会显示纯数字建议加一个TableModel层的格式化方法保留两位小数显示。4.5 Swing 表格不刷新TableModel 与事件线程现象在主界面新增了一条房源切到列表页JTable里看不到关闭窗口重新打开才出现。有时候点击按钮后界面卡死几秒后才恢复。原因JTable的数据来自TableModel你不通知模型数据变化界面是不会主动重新查库的卡死问题一般是查询操作跑在了 Swing 的事件分发线程EDT上数据库查询耗时就阻塞了界面。解决方式新增数据后重新执行查询并把结果集放入DefaultTableModel调用model.setDataVector(data, columnNames)或fireTableDataChanged()通知界面刷新。耗时超过 200 毫秒的操作放到SwingUtilities.invokeLater或新线程里执行防止界面假死。这是一个很容易被忽略的 Swing 特性我在第一次做这类项目时在这个坑上浪费了一整天。5. 验证与进阶六步跑通闭环再加一个合同到期提醒拿到资源后我建议你按下面六步做一次完整验证。这个过程能同时确认源码、数据库、文档三者是否对齐也能让你在答辩或面试时清楚说出系统的完整业务流程。第一步执行house_rental.sql确认五张表全部创建成功第二步修改DBUtil.java里的USER和PASSWORD为本地 MySQL 账号第三步运行MainFrame或LoginFrame用初始化脚本里的测试账号登录第四步新增一套房源录入地址和月租金第五步新增一位租客录入姓名和电话第六步为这套房源和租客签订合同回到房源列表中确认状态变为已租。如果你能不加修改地走完这六步说明整体环境完全没问题。走完基础闭环后可以尝试一个很有价值的扩展合同到期提醒。改动点只有三处正好能训练你理解界面层 / 业务层 / 数据层的分工。数据层新增一条 SQLSELECT c.contract_no, h.address, t.name AS tenant_name, c.end_date FROM t_contract c JOIN t_house h ON c.house_id h.id JOIN t_tenant t ON c.tenant_id t.id WHERE c.status 1 AND c.end_date BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 30 DAY) ORDER BY c.end_date;业务层在ContractService里加一个对应方法返回ListContract界面层在主面板加一个到期提醒按钮点击后弹窗展示结果。通过这个过程你能真实地感受到在分层结构里加功能是平推而不是打乱战。我在自己的出租房管理工具里就沿用了这套逻辑只是把JTable换成了定时弹窗。从那以后我每次定位乱码问题都强制先检查连接串再检查数据库字符集最后才去动实体类——这个排查顺序帮我省了无数冤枉时间。希望这本书里拆过的每一个坑都能让你少走一步弯路拿到源码后跑得比预期更顺。本文还有配套的精品资源点击获取