简介这是一套面向Java初学者的医院病房管理系统课程设计源码包采用JavaGUI与MySQL实现适合用作数据库课程设计、大作业或初期项目立项参考。压缩包共45个文件以27个Java源码为核心辅以SQL建库脚本、XML配置、项目说明文档及界面预览图整体大小约1.02MB便于快速下载与本地调试。源码覆盖实体类、JDBC连接、操作员功能、UI界面等模块目录结构清晰README与SQL脚本可帮助理解表结构和运行方式。目前已有106人学习浏览适合具备一定Java基础并希望练习Swing界面开发与MySQL联调的学习者。资源定位为参考资料而非定制成品需要自行调试报错并扩展功能但作为课程设计或工程实训的脚手架仍有较好的启发性与复用价值。1. 数据库课设为什么挂在一行 JDBC 上很多人的数据库课程设计不是死在 SQL 设计上而是死在「按钮一点程序白屏」这最后一公里。医院病房管理系统这种课设题本质上就是一张床位表、一张患者表、一张入住记录表再加几个增删改查窗口。真正的分水岭在于实体类、JDBC 连接和 Swing 事件这三层是怎么粘合的。如果连接串写死、结果集不关闭、SQL 拼接用户输入那运行起来就是行走的报错现场。这套基于 JavaGUI Mysql 的病房管理系统源码文件结构很典型src 下分 entity、OperatorFunction、JdbcConnect、UI 四个包外加一个 Hospital.sql 建库脚本。项目难度不大但结构完整性比大多数只交一个 Main.java 的课设好得多适合做课程设计、大作业参考。前提是你得会读代码而不是复制粘贴完就直接点运行。我拆这套代码的时候先把依赖关系画出来再一步步走通从登录窗口到数据库返回结果的全链路这篇文章就按这个思路来讲。2. 从 Hospital-master 目录反推系统分层结构拿到压缩包先别急着导入 IDE先把目录结构读一遍。这个项目的 src 包组织方式非常直白看似简单实际上已经把 Java 课设该有的分层做出来了。2.1 四个包各自的职责边界先看顶层目录Hospital.sql 是数据库初始化脚本README.md 是说明文档Hospital.iml 是 IDEA 的模块配置。真正的代码在 src 底下分成四个包我用表格总结一下包名典型类职责entityPatient.java、Bed.java对应数据库表的 Java 对象只保存字段和 getter/setterJdbcConnectJdbcConnect.java负责加载驱动、获取 Connection、提供通用的增删改查方法OperatorFunctionOperatorFunction.java业务操作层把 UI 动作翻译成对 JDBC 的调用UILogin.java、MainFrame.javaSwing 窗口只负责界面布局和事件监听这个划分对应了 Java 课设里最常见的传统三层结构entity 是数据模型OperatorFunction 是业务逻辑UI 是表现层JdbcConnect 是数据访问的基础设施。实际开发中可能还会拆 service、dao但课设里这种「实体 连接 操作 界面」的四包结构已经足够把SELECT * FROM patient到table.setValueAt()这条链路讲清楚了。2.2 实体类设计决定数据库表能查得多顺打开 entity 包下的类你会发现设计逻辑很朴素一个类映射一张表。比如患者实体里通常有 patient_id、patient_name、gender、age、bed_id 等字段床位的实体则包含 bed_id、ward_number、bed_status。字段名基本和数据库列名一一对应没有引入 MyBatis 或 ORM 框架全靠手写rs.getInt(patient_id)做映射。实体类不写满 Lombok你会看到一堆手写的 getter、setter这就是课设代码的特点。有个好处调试时能一眼看到每个字段怎么被填充坏处是字段一多代码会显得很冗长。项目代码里我看到实体类保持了 JavaBean 规范所以即使你想引入 Lombok 简化也只加注解就够了改造空间很大。2.3 一个病房系统的核心表和关系梳理Hospital.sql 脚本是整栋楼的地基你不先跑这个脚本Java 代码无论如何都连不进去。常见的表结构大概是这几张-- 病房表 CREATE TABLE ward ( ward_id INT PRIMARY KEY AUTO_INCREMENT, ward_no VARCHAR(20) NOT NULL, ward_type VARCHAR(50), total_beds INT DEFAULT 4, available_beds INT ); -- 床位表 CREATE TABLE bed ( bed_id INT PRIMARY KEY AUTO_INCREMENT, ward_id INT NOT NULL, bed_no VARCHAR(10), bed_status VARCHAR(10) DEFAULT 空闲, -- 空闲/占用/维护 FOREIGN KEY (ward_id) REFERENCES ward(ward_id) ); -- 患者表 CREATE TABLE patient ( patient_id INT PRIMARY KEY AUTO_INCREMENT, patient_name VARCHAR(50) NOT NULL, gender VARCHAR(4), age INT, phone VARCHAR(20), bed_id INT, FOREIGN KEY (bed_id) REFERENCES bed(bed_id) );这三张表构成了医院病房管理最简模型病房包含多张床位患者入住时被分配到一个床位床位状态实时联动。设计的关键在于可用床位数不要单独维护应该通过统计关联床位状态得出这样减少数据不一致的风险。如果你的脚本里没有外键约束建议课设演示前手动补上因为答辩老师大概率会问你「病房满了怎么办」。3. JdbcConnect 与 Hospital.sql 联动的完整链路很多同学拿着课设源码第一步就把 JdbcConnect.java 打开然后开始改用户名密码。改完直接运行控制台抛ClassNotFoundException或者Communications link failure。这两个异常的成因完全不同前者是驱动 jar 没放在正确位置后者是 MySQL 服务没启动或端口不对。这一章把从 SQL 脚本到 Java 连接对象的全过程拆开讲。3.1 加载驱动和建立连接的两种写法课设年代主流写法还是Class.forName(com.mysql.jdbc.Driver)。如果你装的是 MySQL 8.x这个写法会直接报错因为 8.x 的驱动类名变成了com.mysql.cj.jdbc.Driver同时连接串必须带上时区参数。代码里 JdbcConnect 的核心方法大概是下面这个样子import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class JdbcConnect { private static final String DRIVER com.mysql.cj.jdbc.Driver; private static final String URL jdbc:mysql://localhost:3306/hospital? useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai; private static final String USER root; 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); } }静态代码块里加载驱动是 Java 课设里最常见的方案优点是只加载一次所有连接复用这一个加载动作。getConnection()每次调用都会建立一个物理连接课设规模下完全够用如果你以后上了生产环境这里就是需要改造成数据库连接池的位置。关于serverTimezoneAsia/Shanghai很多人不知道这参数干嘛的其实就是告诉驱动用中国时区解析 DATETIME不加的话 MySQL 8.x 连查询都会抛异常。3.2 通用的增删改方法怎么写最省事OperatorFunction 包里的操作类一般会封装几个通用方法executeUpdate(String sql, Object... params)和executeQuery(String sql, Object... params)。用可变参数接收 SQL 里的占位符值这是课设代码里性价比最高的做法调用方只需要传 SQL 和参数数组内部统一用 PreparedStatement 处理。import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class BaseOperator { public int executeUpdate(String sql, Object... params) { try (Connection conn JdbcConnect.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { for (int i 0; i params.length; i) { ps.setObject(i 1, params[i]); } return ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); return 0; } } public ResultSet executeQuery(String sql, Object... params) { try { Connection conn JdbcConnect.getConnection(); PreparedStatement ps conn.prepareStatement(sql); for (int i 0; i params.length; i) { ps.setObject(i 1, params[i]); } return ps.executeQuery(); } catch (SQLException e) { e.printStackTrace(); return null; } } }try-with-resources语法要求实现 AutoCloseable 接口Connection 和 PreparedStatement 都满足所以能用 try 块包住自动关闭。但executeQuery里不能直接关闭连接因为返回的 ResultSet 在方法外还要继续使用关闭连接会让结果集失效。常见做法是返回 ResultSet 后由调用方释放或者更进一步把 ResultSet 映射成实体对象再返回后一种更安全推荐在课设中直接把查询结果封装成 List 集合返回。3.3 SQL 脚本里的初始化数据决定了演示效果Hospital.sql 除了建表语句通常还包含INSERT INTO初始化语句。课设演示时界面上有没有数据全看这一部分。我习惯先把脚本里的插入语句读一遍确认里面有几张病床、几个科室、是否预置了演示账号。如果只有表结构没有数据登录窗口将无法通过账号校验那就需要手工往数据库里补一条管理员记录。初始数据点建议值用途管理员账号admin / admin123登录窗口验证病房示例数据3 间病房每间 4 张床主界面床位分布展示患者示例数据23 条记录列表查询和编辑演示床位状态至少一个「占用」演示床位分配与释放差异如果脚本里没有管理员表很多课设是直接在登录窗口写死admin字符串判断。这种做法不是不行但答辩时容易挨问「为什么不走数据库验证」建议还是加一张 user 表。4. Swing 界面与 OperatorFunction 的联动实现UI 包是课设最容易被人忽视的部分实际上它直接决定了答辩效果。这个项目的 UI 包里有 login 背景图、操作面板和主窗口BackGrounds 类承担了窗口背景图绘制。从源码结构看每个窗口类都通过事件监听器绑定按钮动作监听器内部再调用 OperatorFunction 里的方法。4.1 窗口初始化和登录验证的前端逻辑登录窗口一般包含用户名输入框、密码输入框和登录按钮点击登录时触发actionPerformed方法。下面是登录验证的逻辑骨架和大多数 Swing 课设的写法一致import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class LoginFrame extends JFrame { private JTextField userText; private JPasswordField passwordField; private OperatorFunction operator; public LoginFrame() { operator new OperatorFunction(); setTitle(医院病房管理系统登录); setSize(400, 300); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); initUI(); } private void initUI() { // 布局和控件初始化省略 loginButton.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent e) { String username userText.getText().trim(); String password new String(passwordField.getPassword()).trim(); if (operator.checkLogin(username, password)) { new MainFrame().setVisible(true); dispose(); } else { JOptionPane.showMessageDialog(null, 用户名或密码错误); } } }); } }new String(passwordField.getPassword())是把字符数组还原成字符串注意不能用passwordField.getText()因为 JPasswordField 的 getText 已被标记为过时。登录成功后把当前登录窗口dispose()释放掉同时打开主窗口两个窗口不能叠加保留否则 AltTab 一切换就会出现多个旧窗口。这里的主窗口创建可以传当前登录用户的角色信息方便主界面按角色显示不同功能按钮。4.2 主界面表格数据刷新的具体写法主界面的核心是一张病房或患者信息表一般用 Swing 的JTable组件承载。数据刷新链路是点击「查询」按钮 → OperatorFunction 调用executeQuery→ 返回结果集 → 把每一行数据填充进DefaultTableModel。整个逻辑看着简单但最容易翻车的地方是表格列的标题和 SQL 查询列的对应关系。import javax.swing.*; import javax.swing.table.DefaultTableModel; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.Vector; public class PatientTablePanel { private DefaultTableModel tableModel; public void refreshPatientData() { VectorString columnNames new Vector(); columnNames.add(患者ID); columnNames.add(姓名); columnNames.add(性别); columnNames.add(年龄); VectorVectorObject data new Vector(); String sql SELECT patient_id, patient_name, gender, age, phone FROM patient; try (ResultSet rs new BaseOperator().executeQuery(sql)) { while (rs.next()) { VectorObject row new Vector(); row.add(rs.getInt(patient_id)); row.add(rs.getString(patient_name)); row.add(rs.getString(gender)); row.add(rs.getInt(age)); row.add(rs.getString(phone)); data.add(row); } tableModel.setDataVector(data, columnNames); } catch (SQLException e) { e.printStackTrace(); } } }这是课设中非常标准的「查询按钮 表格刷新」写法。Vector数据结构虽然老但DefaultTableModel的构造方法只认它你换ArrayList反而会编译报错。手动逐列 add 数据虽然繁琐但胜在直观答辩时你可以指着代码说「查询结果集里每个字段对应表格的一列」。很多新手会把tableModel.setDataVector放在 while 循环里这样每次循环都重设一次数据模型界面会闪数据量大了还会卡死必须在循环外面统一设置。4.3 床位分配操作的前后关联逻辑床位分配是整个病房管理系统里最有业务味道的功能。基本流程是从患者列表选中一个患者点击「分配床位」弹出一个对话框显示所有空闲床位选一个床位后更新患者表的 bed_id 字段同时把床位状态改为「占用」。这两个更新操作必须在同一个事务里完成否则会出现患者有床位但床位还是空闲的数据错乱。import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class AssignBedService { public boolean assignBed(int patientId, int bedId) { String updatePatient UPDATE patient SET bed_id ? WHERE patient_id ?; String updateBed UPDATE bed SET bed_status 占用 WHERE bed_id ? AND bed_status 空闲; try (Connection conn JdbcConnect.getConnection()) { conn.setAutoCommit(false); try (PreparedStatement ps1 conn.prepareStatement(updatePatient); PreparedStatement ps2 conn.prepareStatement(updateBed)) { ps1.setInt(1, bedId); ps1.setInt(2, patientId); int affected1 ps1.executeUpdate(); ps2.setInt(1, bedId); int affected2 ps2.executeUpdate(); if (affected1 0 affected2 0) { conn.commit(); return true; } else { conn.rollback(); return false; } } catch (SQLException e) { conn.rollback(); throw e; } } catch (SQLException e) { e.printStackTrace(); return false; } } }这段代码的关键在第 6 行到第 12 行先关闭自动提交然后依次执行两个 UPDATE只有两次受影响行数都大于 0 才提交。第二个 UPDATE 的 WHERE 条件带上了bed_status 空闲这是防止两个患者被同时分配到同一张床的兜底逻辑。affected2为 0 意味着床位不是空闲状态此时回滚整个事务患者表也不会被改。这个细节能体现你对并发控制有没有概念答辩加分项。课设里事务的写法是很多人的盲区如果你时间有限只把这一段看懂背熟就值回票价了。5. 课程设计答辩与部署中最容易踩的六个坑一套课设源码光跑通还不够换个环境就崩才是最常见的事。无论是自己练手还是帮同学看问题下面这些排查路径基本覆盖了八成报错场景重点说排错思路。5.1 驱动版本与 MySQL 版本不匹配ClassNotFoundException: com.mysql.jdbc.Driver是最高频的报错。先检查lib目录下有没有 mysql-connector jar再确定版本。MySQL 5.x 对应 5.1.x 的驱动MySQL 8.x 必须用 8.0.x 的驱动连接串也改成com.mysql.cj.jdbc.Driver。在命令行执行mysql --version确认数据库版本不要凭直觉判断。5.2 时区和 SSL 配置导致建立连接超时The server time zone value Öйú±ê׼ʱ¼ä is unrecognized看到这行乱码不要慌这是 MySQL 8.x 的时区配置问题。解决方案是在 JDBC URL 末尾追加serverTimezoneAsia/Shanghai。如果只是本地课设不涉及敏感数据可以额外加useSSLfalse跳过 SSL 握手减少首次连接耗时。5.3 控制台中文乱码这个问题几乎每个课设都会碰到。JVM 内部默认 UTF-8MySQL 表默认可能是 latin1。需要注意三个环节连接串里的characterEncodingutf8、MySQL 建库语句里的DEFAULT CHARSETutf8、Idea 的 File Encoding 设置为 UTF-8。三者缺一轻则查询结果中文乱码重则插入数据直接报Incorrect string value。推荐统一贴一下这三处的配置检查点正确配置IDEA 全局编码Settings - File Encodings全部 UTF-8连接串useUnicodetruecharacterEncodingutf8建库语句CREATE DATABASE hospital DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci注意utf8mb4和utf8的区别前者是后者的超集可以完整存入 emoji 和生僻字课设阶段直接用utf8mb4一步到位。5.4 端口占用导致 SQLNonTransientConnectionExceptionMySQL 默认 3306 端口。如果本机装了多个 MySQL 实例或者上一次程序异常退出没释放端口你的 Java 程序会连接到一个没有 MySQL 监听的端口。命令行执行netstat -ano | findstr 3306查看占用情况如果不需要某个服务就杀掉对应 PID。更稳妥的做法是在连接串里写明端口号不要依赖默认值。5.5 ResultSet 提前关闭导致表格数据为空很多课设的executeQuery方法长这个样子在方法内部开启连接、执行 SQL、拿到结果集、关闭连接、返回结果集。问题就在这里你拿到的是已经被标记关闭的 ResultSet数据读不出来。解决方案是不要在查询方法内关闭 Connection把连接生命周期交给上层管理或者直接改成把结果映射为 List 返回。我个人推荐直接映射为 List因为这样就不用手动管理 ResultSet 的生命周期了。5.6 多条 SQL 操作没有事务包裹导致数据不对这是最容易和「功能正常」混淆的地方。就像前面床位分配的例子患者表和床位表的状态是联动的一个成功一个失败并不会报错但数据对不上。检查项目代码中凡是有「先改 A 后改 B」逻辑的地方是否都开启了setAutoCommit(false)。很多课程设计的代码里直接省略了事务控制没并发时看不出毛病一旦多条记录同时操作就会出现不可复现的脏数据这类问题最难查建议你拿到代码的第一步就把所有多写操作排查一遍。整套源码里的资源文件、包的命名和注释都保留了原项目结构照着这一层的思路去读你会发现在不引入框架的情况下JDBC 的手写事务控制、JavaBean 实体映射、MVC 界面分离其实和框架的原理是相通的。把这些小细节研究透比单纯跑通一个系统有意思得多。本文还有配套的精品资源点击获取 SEO 优化官网定制响应式建站教育培训建站