
简介基于Java与SQL Server开发完成的房屋中介公司管理系统属于完整课程设计项目源码包适合正在学习Java桌面应用开发、数据库编程或准备课程设计答辩的学生使用。系统运行在Windows10与JDK1.8环境中采用Eclipse作为开发工具通过JDBC操作SQL Server数据库界面部分使用Swing组件搭建业务上涵盖房源信息管理、订单查询统计、客户反馈以及基础数据设置等模块。压缩包内有88个文件包括43个Java源代码文件、19张界面截图PNG、9个Eclipse项目配置XML、5个依赖JAR包等内容整体体积仅3.22MB代码结构划分了src源码目录、jdbc数据库访问目录以及img图片资源目录便于导入项目后直接运行和学习。目前已有156人学习浏览下载后可以得到完整的源码、项目配置文件与运行所需的第三方库能够帮助理解Java连接SQL Server的完整流程、Swing窗体界面设计方法以及小型管理系统的分层组织思路是一份可运行、可扩展的课程设计参考资料。1. 基于 Java SQL Server 的房屋中介管理系统课设源码拆解与复现一套 Java SQL Server 的房屋中介公司管理系统放在硬盘里跑不起来的人远比跑起来的多。这不是夸张而是我在帮人看这套课设源码时的真实感受——乱掉的 JDBC 驱动、没配的 TCP/IP 协议、写错的排序规则任何一个都能让项目卡在启动第一屏。这套系统的正身是一个标准的中介业务闭环房源管理、客户管理、合同登记、租售跟进界面用 Swing 搭建数据层走 JDBC 操作 SQL Server。它适合两类人一类是拿它当 Java 课设交作业的在校生另一类是想快速看一遍“Swing JDBC SQL Server”三者怎么配合的从业者。这篇文章把你从“这是什么”带到“怎么改、坑在哪”照着做半小时内能把系统跑起来。2. 系统设计与数据库建模先看表结构再动手2.1 模块划分与 ER 关系这套房屋中介管理系统的功能边界很清楚打开源码包之后你会看到整个项目可以拆成六个业务模块房源信息管理、客户信息管理、租赁合同管理、销售合同管理、员工登录与管理、数据统计与报表。这六个模块不是各干各的它们的核心关系是先有房源和客户再生成合同最后由合同产生业绩提成。表设计上最关键的几张表是HouseInfo房源表、CustomerInfo客户表、ContractInfo合同表、EmployeeInfo员工表。其中房源表和客户表是主数据的来源合同表同时引用了房源编号、客户编号和业务员编号形成了一条完整的中介业务链。如果你拿到源码后想改功能第一步不是打开 Java 代码而是先打开数据库脚本看清楚这几张表的主外键关系。我在拆这套系统时注意到一个值得说的地方它把租赁合同和销售合同放在同一张合同表里通过合同类型字段区分。这种设计在小规模课设里是合理的减少了表数量也让统计查询少做一次 join。真实商业系统大概率会把两者拆开因为租赁有押金、周期、续约逻辑销售有产权过户、贷款节点字段差异太大。但对于这个项目的定位来说合表设计更贴合课程设计的体量。2.2 建库脚本与表结构参数源码包里带了建库脚本文件名为house_manage_db.sql。打开之后你会发现它不只是一个建表脚本还包含初始数据。这里建议你先别急着在 SSMS 里直接执行先看一眼脚本里用了哪些数据类型和约束。-- 房源信息表 CREATE TABLE HouseInfo ( houseId INT IDENTITY(1,1) PRIMARY KEY, -- 房源编号自增主键 houseNo VARCHAR(20) NOT NULL, -- 房源编号业务编号区别于主键 houseType VARCHAR(10) NOT NULL, -- 户型如 2室1厅 area DECIMAL(8,2) NOT NULL, -- 建筑面积单位平方米 floorNum INT, -- 所在楼层 totalFloor INT, -- 总楼层 price DECIMAL(12,2) NOT NULL, -- 租金或售价 isSell BIT DEFAULT 0, -- 0出租1出售 status VARCHAR(10) DEFAULT 未成交, -- 房源状态 address NVARCHAR(100) NOT NULL -- 地址用NVARCHAR存中文 );houseId用IDENTITY(1,1)做主键这意味着插入数据时不需要显式传值SQL Server 会自动生成。这个设计在后续 JDBC 操作里会牵扯到一个关键点怎样在插入后拿到自增的主键值。我在后面第 5 章的避坑里会专门讲这里先记住用Statement.RETURN_GENERATED_KEYS这个方向。price用DECIMAL(12,2)既支持大额价格又避免FLOAT带来的精度误差。很多人做课设喜欢用FLOAT在房产这种以万为单位计算的场景下浮点误差会在统计报表里被放大所以这套源码的类型选择是规范的。2.3 初始数据与登录账号脚本末尾有一段INSERT INTO EmployeeInfo的语句默认管理员账号是admin密码是admin123。客户表里也插了两条测试数据房 source 表插了三套房源。这些初始数据是为了让系统跑起来后界面不至于空空荡荡。这里有个容易忽略的点EmployeeInfo表的密码列是VARCHAR(32)存的是 MD5 加密后的值。登录时 Java 端先用MD5工具类把输入密码转成摘要再和数据库里的值比较。如果你把脚本里的初始密码直接改成明文登录逻辑就会失效。我在复现时见过有人把数据库里的密码改成123456然后 Java 端一直登录失败原因就是登录代码里强制走了 MD5。进 SSMS 执行脚本时注意选中正确的数据库实例和库名默认库名是HouseManageDB。如果 SSMS 报“对象名无效”多半是脚本开头有USE [HouseManageDB]但你的库名不一致改一下即可。3. 打通 JDBC连接配置与驱动选择3.1 驱动版本与连接串参数这个项目用的是 SQL Server 2008 以上的兼容写法驱动类名是com.microsoft.sqlserver.jdbc.SQLServerDriver。这个类名在 sqljdbc4.jar 和更高版本的 mssql-jdbc 驱动里都存在区别在于旧驱动支持的 JDK 版本比较老。如果你本机是 JDK 8建议直接用 mssql-jdbc 6.2 以上的驱动兼容性更好。连接串的核心格式如下String url jdbc:sqlserver://localhost:1433;databaseNameHouseManageDB;encrypttrue;trustServerCertificatetrue; String user sa; String password 123456;参数说明localhost:1433是 SQL Server 默认端口如果你安装时改过端口务必同步修改databaseName是库名必须和建库脚本里一致encrypttrue和trustServerCertificatetrue是应对新版驱动默认加密行为的关键如果缺失JDK 8 下可能报 SSL 握手失败。3.2 连接工具类与 DAO 层写法源码里有一个DBUtil类作用是统一管理连接的创建与关闭。我拆过很多课设这个类的写法大同小异核心是把驱动加载放静态块里用DriverManager.getConnection()获取连接关闭资源时遵循“后开先关”的顺序。public class DBUtil { private static final String DRIVER com.microsoft.sqlserver.jdbc.SQLServerDriver; private static final String URL jdbc:sqlserver://localhost:1433;databaseNameHouseManageDB;encrypttrue;trustServerCertificatetrue; 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, PreparedStatement ps, Connection conn) { if (rs ! null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (ps ! null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn ! null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }逻辑说明这段代码先把驱动注册动作封装到静态块里保证类加载时驱动只注册一次避免每条连接重复加载关闭资源的三段式写法虽然笨但保证在连接用完后的释放顺序是 ResultSet → PreparedStatement → Connection防止连接泄漏。在实际运行时Class.forName(DRIVER)在 JDBC 4.0 之后已经不是必须的驱动 jar 包含META-INF/services会自动注册。保留这行代码能让代码兼容性更好也让初学者更容易理解驱动加载的过程。如果你的项目用了连接池这个类可以不用直接交给池管理即可。3.3 登录校验与防止 SQL 注入登录模块是这套系统里少数需要安全意识的代码。我拆源码时发现它用了PreparedStatement而不是Statement这个细节值得说。使用PreparedStatement的setString传参SQL 会预编译后再填充参数注入语句只被当成普通字符串处理从根本上杜绝了or11这类密码绕过问题。public boolean checkLogin(String username, String md5Password) { String sql SELECT COUNT(*) FROM EmployeeInfo WHERE empNo ? AND password ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, md5Password); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { return rs.getInt(1) 0; } } } catch (SQLException e) { e.printStackTrace(); } return false; }这里的try-with-resources写法是 Java 7 以后的推荐风格资源自动关闭不用手写close()。参数说明第一个?对应员工编号不是员工姓名第二个?必须传 MD5 后的密码。如果你改了登录逻辑改成明文比对一定要同时调整这条 SQL否则两边对不上。4. 核心业务实现从房源登记录入到合同生成4.1 房源模块CRUD 与组合条件查询房源管理是本系统的核心模块也是一个 Java 课设必须做完整的部分。源码里HouseDao承担了房源表的增删改查其中最有参考价值的是它的组合查询——支持按户型、区域、价格区间三个条件同时过滤。public ListHouse searchHouses(String houseType, String area, double minPrice, double maxPrice) { StringBuilder sql new StringBuilder(SELECT * FROM HouseInfo WHERE 11); if (houseType ! null !houseType.isEmpty()) { sql.append( AND houseType ?); } if (area ! null !area.isEmpty()) { sql.append( AND address LIKE ?); } if (minPrice 0) { sql.append( AND price ?); } if (maxPrice 0) { sql.append( AND price ?); } // 执行查询依次设置参数 }逻辑说明这里的拼 SQL 技巧是典型的“动态查询模板”——先写WHERE 11后面每个条件都用AND开头。这样任何一个条件缺失都不会破坏语法结构省去判断“是否第一个条件”的麻烦。参数设置顺序必须和拼接顺序一致先户型、再区域、后价格区间错一个就查不出数据。在 UI 层这套系统的搜索事件通过addActionListener绑定到查询按钮用户选完条件后点击查询结果刷新到JTable里。注意address LIKE ?的参数值需要在 Java 端手动拼%和_SQL Server 的 LIKE 和 MySQL 一样支持通配符。4.2 客户模块意向状态与跟进记录客户模块对应CustomerDao它比房源模块多了一层业务逻辑客户意向状态管理。状态字段是VARCHAR(10)值域包括“未看房”“已看房”“已成交”“已流失”。在 DAO 层状态流转的控制不是简单 UPDATE而是带条件的位置比较public boolean updateCustomerStatus(int customerId, String oldStatus, String newStatus) { String sql UPDATE CustomerInfo SET status ? WHERE customerId ? AND status ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, newStatus); ps.setInt(2, customerId); ps.setString(3, oldStatus); return ps.executeUpdate() 1; } catch (SQLException e) { e.printStackTrace(); return false; } }这里的核心是 WHERE 里带上了oldStatus作为乐观锁条件。它的意义在于假如两个窗口同时处理同一个客户一个把状态从“已看房”改成“已成交”另一个还想从“已看房”改成“已流失”第二个 UPDATE 的执行结果是 0因为它查询到的旧状态已经不存在。这种写法在课设里算超纲但能很好地解释什么叫“并发安全”。执行结果的判定用executeUpdate() 1而不是 0原因是更新一条记录只可能返回 0 或 1。如果返回 1说明状态确实发生迁移返回 0说明旧状态不匹配前端就要给出提示。4.3 合同模块事务控制与自增主键获取合同生成是整套系统业务链的终点。一个合同对象包含房源编号、客户编号、员工编号、合同类型租赁/销售、成交金额、签订日期等字段。这里的代码最能看出作者功力的是插入操作因为它在单条INSERT里同时完成了两件事写入合同记录并返回自增主键同时更新房源状态为“已成交”。public int addContract(Contract contract) throws SQLException { String sql INSERT INTO ContractInfo (houseId, customerId, empId, contractType, dealPrice, signDate) VALUES (?, ?, ?, ?, ?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { ps.setInt(1, contract.getHouseId()); ps.setInt(2, contract.getCustomerId()); ps.setInt(3, contract.getEmpId()); ps.setString(4, contract.getContractType()); ps.setBigDecimal(5, contract.getDealPrice()); ps.setDate(6, new java.sql.Date(contract.getSignDate().getTime())); ps.executeUpdate(); try (ResultSet keys ps.getGeneratedKeys()) { if (keys.next()) { return keys.getInt(1); } } } return 0; }关键参数是conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)。默认的prepareStatement(sql)执行后不返回数据库生成的主键必须显式声明这个标志然后通过getGeneratedKeys()拿到结果集。我见过不少人写完 INSERT 后再用SELECT MAX(contractId)去查主键这在单用户课设里能跑通但并发场景下可能拿到别人的主键值。合同插入后房源状态更新不能用自动提交模式必须和合同插入放在同一个事务里。源码里这部分在 Service 层控制核心三步开启事务 → 插入合同 → 更新房源状态 → 提交任一步失败则回滚。代价是这个业务逻辑的完整代码较长但它是整套系统里最值得模仿的一段。4.4 数据报表SQL Server 聚合查询与 Java 端展示统计报表模块用的是 SQL Server 的聚合函数GROUP BY。源码里做了一个“按合同类型统计成交额”的报表在ReportDao里对应一条命令SELECT contractType, COUNT(*) AS cnt, SUM(dealPrice) AS totalAmount FROM ContractInfo GROUP BY contractType这段 SQL 在 SSMS 里执行没任何问题但放到 Java 端要注意结果集的取数类型。SUM(dealPrice)返回的类型在 JDBC 里可以映射为BigDecimal如果强转成double或float金额精度会立刻丢失。源码里用的是getBigDecimal(totalAmount)然后在表格模型里用setValueAt显示为字符串。这个细节让报表的数字和 SQL Server 里查询结果完全一致不出现小尾巴。如果你需要导出 Excel这套源码里没有整合 POI但可以在报表结果集上自行封装。常见做法是先把数据填到Vector或二维数组再通过JFileChooser弹窗选保存路径最后逐行写入 CSV 文件。CSV 可以用 Excel 直接打开做到这一步课设答辩时加分明显。5. 避坑记录SQL Server 版 Java 项目的八个典型问题5.1 驱动类名报 ClassNotFoundException现象运行项目时控制台抛出ClassNotFoundException: com.microsoft.sqlserver.jdbc.SQLServerDriver。原因项目里没有引入驱动 jar或引用的 jar 位置不对。很多人下载了源码包却忘了看lib目录是否完整直接把源码导入 IDE 就跑。解决检查项目lib文件夹下是否有sqljdbc4.jar或mssql-jdbc-x.x.jar如果没有手动下载对应版本并添加到 Build Path。注意旧驱动不支持 JDK 9JDK 11 环境下用 mssql-jdbc 8.2 以上版本更稳。5.2 端口连不上SQL Server TCP/IP 协议未启用现象报Cannot connect to localhost:1433但 SSMS 能正常连接。原因SQL Server 默认安装后 TCP/IP 协议是禁用的JDBC 走不了 1433 端口SSMS 能连是因为它走的是 Shared Memory 或 Named Pipes。解决打开“SQL Server 配置管理器”展开“SQL Server 网络配置”选中实例名右侧把 TCP/IP 启用然后重启 SQL Server 服务。如果这时还连不上再检查 Windows 防火墙是否放行 1433。这个优先级最高的数据库连接翻车点。5.3 中文乱码或中文查询不出来现象界面上房源地址显示乱码或者按中文关键字搜索无结果。原因数据库表字段用了VARCHAR来存中文而 Java 端字符串是 UTF-16 编码两边转换时发生编码不一致。另外 SQL Server 的默认排序规则是Chinese_PRC_CI_AS如果建表时用了非中文字段类型存储中文就会出现问号或方框。解决建表时地址等中文字段用NVARCHAR而不是VARCHAR连接串里加characterEncodingUTF-8虽然 JDBC 驱动不太吃这个参数但规范上写着没坏处如果是已经建好的表用ALTER TABLE HouseInfo ALTER COLUMN address NVARCHAR(100)调整类型。5.4 密码带特殊字符导致连接失败现象数据库连接串里的password123abc能测试连通但 Java 程序连接时报连接属性异常。原因等特殊字符在 JDBC URL 里需要转义直接裸写会被解析成连接串的一部分导致认证信息错位。解决用DriverManager连接时把用户名和密码作为参数传入getConnection(String url, String user, String password)而不是拼在 URL 字符串里。这个写法本身就是最佳实践不用做转义。5.5 SQL Server 安装失败无法找到数据库引擎启动句柄现象安装 SQL Server 2016/2019 时安装界面报“无法找到数据库引擎启动句柄”安装进程直接回滚。原因这个报错通常与系统已安装过 SQL Server 但服务残留有关或者系统账户权限不足导致数据库引擎服务无法启动。解决先用管理员身份运行命令提示符执行net start查看是否有残留的MSSQLSERVER服务如果有用net stop停掉后删除对应注册表服务项再重新安装。安装时建议在“服务器配置”页指定NT AUTHORITY\SYSTEM账户启动服务避开权限问题。5.6 日期字段转换报错现象合同表的signDate字段在 Java 端用Date类型取值时抛ClassCastException。原因JDBC 从 SQL Server 的datetime类型取出的对象是java.sql.Timestamp它和java.util.Date不是继承关系直接强转会失败。解决统一用java.sql.Date接值或者用Timestamp取值后手动转java.util.Date。插入时也注意方向用setDate传参时先转java.sql.Date。这套源码里已经做对但如果你自己写新模块很容易踩。5.7 表和字段名撞上 SQL Server 保留字现象执行建表脚本时某条 CREATE 语句报语法错误或“xxx 附近有语法错误”。原因表名或字段名和 SQL Server 保留字冲突比如identity、order、number这类单词。解决改表结构时避开保留字确实要用则给表名和字段名加方括号[]。检查源码里的 SQL 语句凡是加[]的字段名千万别去掉去掉就是语法错误。5.8 JTable 刷新不显示新数据现象新增一条房源后列表界面没有立即刷新必须重启程序才看到。原因DAO 插入数据后没有重新执行查询JTable的DefaultTableModel还是旧的数据集。解决在新增成功后的回调里重新调用searchHouses并setModel刷新表格。注意要把清空旧行的setRowCount(0)放在填充新数据之前否则旧数据会在表格里叠加显示。6. 验证与扩展把课设做成经得起答辩的完整作品一个课设源码如果你只是让它跑起来那它和班里同学的作品没有本质区别。拉高完成度的方法是建立自己的验证体系把每个模块的输入、执行、输出都过一遍并记录下与默认行为的差异。我的习惯是每次拿到这类源码先走三组验证登录组、业务流转组、异常组。登录组验证的内容是三个账号的行为差异admin能看到全部菜单普通员工只能看到房源和客户验证完成后要确认数据库里的 MD5 密码不能被替换成明文。业务流转组则模拟一条完整链路录入房源 → 新增客户 → 客户登记看房意向 → 生成合同 → 房源状态自动变为“已成交”——这五步在界面上全部走通回到 SSMS 查合同表确认外键字段和金额正确。异常组更直接输入不存在的员工编号登录、提交价格为零的合同、删除被合同引用的房源系统是否给出友好错误提示而不是抛堆栈异常。扩展方向建议做一个二选一给客户模块加一个跟进记录表FollowRecord或者给报表模块加一个整体导出 CS V 的功能。前者涉及表设计变更后者更轻量约三十行代码。如果你选导出 CSV核心逻辑是先查出报表结果集再用BufferedWriter按行写入表头用英文或中文都可try (BufferedWriter writer Files.newBufferedWriter(Paths.get(report.csv))) { writer.write(合同类型,合同数量,成交总额); writer.newLine(); while (rs.next()) { writer.write(rs.getString(contractType) , rs.getInt(cnt) , rs.getBigDecimal(totalAmount)); writer.newLine(); } }注意金额字段用BigDecimal写入避免浮点尾巴。导出成功后检查文件大小和首行内容确保打开是规整的表格而不是乱码。这套系统最值得带走的东西不是它的代码量而是“Java SQL Server 如何在一个课设尺度上合作”的完整示范。从那以后我每次拿到课设源码都强制走一遍“看脚本 → 建库 → 配驱动 → 验证登录 → 走核心业务”这条路不去猜测哪里可能出错而是直接让错误暴露出来。希望帮到你。本文还有配套的精品资源点击获取