Java医院管理系统课设:Swing+MySQL实战与事务设计
发布时间:2026/10/1 4:57:16来源:尧图网络
简介这是一套面向高校Java课程设计的医院信息管理系统实战项目采用Swing构建图形界面、MySQL实现数据持久化专为Java初学者及课设学生打造满足教学验收与基础工程实践需求。资源包共82个文件含16个核心Java源码如Login、PatientMain、DoctorMain、JDBCUtils等、25个编译后class文件、28张界面截图展示登录、患者挂号、医生接诊、记录查询等全流程操作效果、10个XML配置文件含IDEA项目结构与UI布局、1个可直接执行的yyglxt.sql数据库脚本整体压缩包仅580KB轻量易部署。已有86人学习下载所有模块均经真实客户交付验证可稳定运行界面保持简洁原始风格符合课设评审对代码原创性与学生能力匹配度的要求。下载即得完整可运行工程含源码、数据库脚本、运行效果图及清晰目录结构无需额外配置即可导入IDEA或Eclipse快速启动调试。1. 医院管理系统课设不是炫技的 Swing MySQL 组合而是帮你把 Java 面向对象、JDBC、GUI 三座大山一次性踩实的实战沙盒你写完一个「学生信息管理系统」老师点头说“结构完整”但当你把挂号、医生排班、药房库存、门诊结算全串进一个 Swing 窗口里再让 MySQL 真正撑住并发插入和多表关联查询——那一刻你才第一次摸到 Java 工程落地的边。这不是玩具项目它强制你用SwingUtilities.invokeLater()处理线程安全用PreparedStatement防 SQL 注入用ResultSetMetaData动态生成表格列用ConnectionPool哪怕只是简易版扛住连续 5 个窗口同时查患者记录的压力。我带过 17 届课设90% 的翻车点不在功能缺失而在——没真正理解 Swing 事件分发线程EDT和 JDBC 连接生命周期的耦合关系。这份资源不是“能跑就行”的模板它是按真实课设答辩标准打磨过的含完整 ER 图、MySQL 建库脚本含外键约束与索引、Swing 界面分层源码Controller/View/Model 分离、以及最关键的——3 个典型业务场景的事务边界标注比如“退号退费”必须原子性。适合正在啃《Java 核心技术卷 I》第 12 章、刚配好 JDK 17 和 MySQL 8.0、但对着空白 IDE 发呆的你。2. 从零建库到数据就位MySQL 8.0 安装、建库、初始化数据的硬核闭环2.1 Windows 下 MySQL 8.0 安装避坑指南绕开 error 2002 和 SSL 连接错误别急着下载官网 zip 包——Windows 用户直接用 MSI 安装器最稳。关键步骤安装时取消勾选 “Configure MySQL Router” 和 “Start MySQL Server at System Startup”课设本地单机用自启反而干扰调试设置 root 密码时务必记牢且不要用特殊字符如、!否则 JDBC URL 里要 URL 编码新手极易漏安装完成后打开命令行执行# 检查服务是否启动非端口监听 sc query mysql # 若状态为 STOPPED手动启动 net start mysql提示error 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock是 Linux 错误提示Windows 不会出现。Windows 常见报错是Cant connect to MySQL server on localhost (10061)本质是服务未启动或端口被占。用netstat -ano | findstr :3306查端口占用杀掉 PID 即可。2.2 创建医院专用数据库与 7 张核心表含外键约束与索引建库脚本hospital_db.sql必须包含以下要素字符集统一、引擎指定、外键显式声明。以下是doctor表的完整定义其他表逻辑同理CREATE DATABASE IF NOT EXISTS hospital_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE hospital_db; CREATE TABLE doctor ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, department_id INT NOT NULL, title VARCHAR(20) DEFAULT 住院医师, -- MySQL 设置默认值为0不这里是字符串默认值 phone VARCHAR(15), status ENUM(在职, 休假, 离职) DEFAULT 在职, created_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_dept_id (department_id), -- 关键为外键字段建索引否则 JOIN 性能暴跌 FOREIGN KEY (department_id) REFERENCES department(id) ON DELETE RESTRICT ON UPDATE CASCADE );注意ON DELETE RESTRICT是课设安全底线——删科室前必须清空该科室医生避免数据孤儿ON UPDATE CASCADE保证科室 ID 变更时医生表自动同步。ENUM类型比VARCHAR更省空间且防脏数据但需在 Java 代码中严格映射见 4.3 节。2.3 初始化测试数据用 INSERT SELECT 批量造 200 条真实感数据别手敲 INSERT用INSERT INTO ... SELECT结合UNION ALL生成批量数据-- 一次性插入 50 个模拟医生含不同职称、科室 INSERT INTO doctor (name, department_id, title, phone, status) SELECT CONCAT(张, LPAD(seq.n, 2, 0)) AS name, FLOOR(1 RAND() * 5) AS department_id, -- 随机分配到 1~5 号科室 ELT(FLOOR(1 RAND() * 4), 主任医师, 副主任医师, 主治医师, 住院医师) AS title, CONCAT(138, LPAD(FLOOR(RAND() * 100000000), 8, 0)) AS phone, 在职 AS status FROM ( SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 -- 扩展至 50 行实际脚本含 50 个 SELECT ) seq;逻辑说明LPAD补零保证手机号格式统一ELT函数按随机数选择职称避免全是一个职称FLOOR(1 RAND() * 5)生成 1~5 的整数对应科室 ID。这样生成的数据有分布、有规律、可复现比 faker 库更可控。2.4 验证数据库连通性用 MySQL Workbench 或命令行做三步检测连通性检测mysql -u root -p -h 127.0.0.1 -P 3306必须用127.0.0.1不用localhost避免 socket 连接干扰权限检测执行SHOW GRANTS FOR rootlocalhost;确认有ALL PRIVILEGES ON hospital_db.*数据检测SELECT COUNT(*) FROM patient;确保初始化数据已入库课设要求 patient 表至少 100 条。提示若用 Navicat 连接连接属性中取消勾选 “Use SSL”——课设无需加密勾选后易报SSL connection error密码输入框右下角点“Test Connection”成功才继续。3. Swing 界面工程化拆解从“拖控件”到 MVC 分层的质变3.1 主窗口设计原则用 JFrame CardLayout 实现多视图切换别用JDialog堆弹窗主框架必须是JFrame内部用CardLayout管理挂号、医生、药房等子面板public class HospitalMainFrame extends JFrame { private JPanel cardPanel; private CardLayout cardLayout; public HospitalMainFrame() { cardPanel new JPanel(); cardLayout new CardLayout(); cardPanel.setLayout(cardLayout); // 添加各业务面板实例化时传入共享的 DBManager cardPanel.add(new RegistrationPanel(dbManager), registration); cardPanel.add(new DoctorPanel(dbManager), doctor); cardPanel.add(new PharmacyPanel(dbManager), pharmacy); this.add(cardPanel, BorderLayout.CENTER); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setSize(1200, 700); this.setLocationRelativeTo(null); // 居中显示 } public void showPanel(String panelName) { cardLayout.show(cardPanel, panelName); } }参数说明CardLayout是 Swing 原生布局管理器比JTabbedPane更轻量、更易控制导航逻辑setLocationRelativeTo(null)比setLocation(200, 100)更可靠适配不同分辨率所有子面板构造时注入DBManager实例避免重复创建连接。3.2 表格组件实战JTable AbstractTableModel 动态绑定医生列表硬编码DefaultTableModel会锁死扩展性。必须继承AbstractTableModelpublic class DoctorTableModel extends AbstractTableModel { private ListDoctor doctors new ArrayList(); private final String[] columnNames {ID, 姓名, 科室, 职称, 电话, 状态}; Override public int getRowCount() { return doctors.size(); } Override public int getColumnCount() { return columnNames.length; } Override public Object getValueAt(int rowIndex, int columnIndex) { Doctor d doctors.get(rowIndex); switch (columnIndex) { case 0: return d.getId(); case 1: return d.getName(); case 2: return DepartmentDAO.getInstance().getDepartmentNameById(d.getDepartmentId()); // 关联查询科室名 case 3: return d.getTitle(); case 4: return d.getPhone(); case 5: return d.getStatus(); default: return ; } } Override public String getColumnName(int column) { return columnNames[column]; } public void setDoctors(ListDoctor doctors) { this.doctors doctors; fireTableDataChanged(); // 关键通知视图刷新 } }逻辑说明fireTableDataChanged()是刷新表格的唯一正确方式repaint()无效DepartmentDAO.getInstance().getDepartmentNameById()将科室 ID 转为名称避免在 Doctor 对象中冗余存储科室名符合范式ListDoctor作为数据源便于后续排序、过滤扩展。3.3 事件处理规范用 ActionListener Lambda 表达式解耦业务逻辑按钮点击事件必须分离 UI 和业务// 在 RegistrationPanel 构造方法中 btnRegister.addActionListener(e - { try { // 1. 获取表单数据 String name txtName.getText().trim(); String idCard txtIdCard.getText().trim(); int deptId (int) cmbDepartment.getSelectedItem(); // 2. 校验课设基础要求 if (name.isEmpty() || idCard.length() ! 18) { JOptionPane.showMessageDialog(this, 姓名不能为空身份证号必须为18位, 输入错误, JOptionPane.ERROR_MESSAGE); return; } // 3. 调用业务层非 DAO 层 RegistrationService.registerPatient(name, idCard, deptId); // 4. 刷新表格 refreshPatientTable(); JOptionPane.showMessageDialog(this, 挂号成功); } catch (SQLException ex) { JOptionPane.showMessageDialog(this, 数据库操作失败 ex.getMessage(), 错误, JOptionPane.ERROR_MESSAGE); ex.printStackTrace(); // 课设调试必需 } });注意JOptionPane是课设级 UI 反馈比控制台System.out.println更直观ex.printStackTrace()必须保留答辩时老师会要求现场 debugrefreshPatientTable()是封装好的方法内部调用tableModel.setPatients(...)再fireTableDataChanged()。3.4 线程安全铁律Swing 的 EDT 规则与耗时操作隔离Swing 组件只能在事件分发线程EDT中创建和修改。网络/数据库操作必须异步// 错误示范在按钮点击中直接查数据库阻塞 EDT界面冻结 // ListPatient patients PatientDAO.findAll(); // 正确做法用 SwingWorker SwingWorkerListPatient, Void worker new SwingWorker() { Override protected ListPatient doInBackground() throws Exception { return PatientDAO.findAll(); // 耗时操作放这里 } Override protected void done() { try { ListPatient patients get(); // get() 返回 doInBackground 结果 patientTableModel.setPatients(patients); patientTable.repaint(); // EDT 中安全刷新 } catch (Exception e) { JOptionPane.showMessageDialog(HospitalMainFrame.this, 加载患者列表失败 e.getMessage()); } } }; worker.execute(); // 启动异步任务逻辑说明SwingWorker是 Swing 官方推荐的异步方案doInBackground()在后台线程执行done()回到 EDT 执行 UI 更新get()方法会阻塞直到后台完成但done()是回调不会卡主线程课设中所有SELECT查询都必须套SwingWorker这是答辩加分项。4. JDBC 连接池与事务控制让 MySQL 真正扛住课设并发压力4.1 简易连接池实现C3P0 vs HikariCP课设选 C3P0 的三个理由课设不需生产级性能C3P0 更易配置、文档更全、报错更友好依赖极简仅需c3p0-0.9.5.5.jarmysql-connector-java-8.0.33.jar配置文件友好支持c3p0-config.xml比 HikariCP 的代码配置更直观异常堆栈清晰com.mchange.v2.c3p0.ComboPooledDataSource报错直接指向连接参数方便调试。c3p0-config.xml关键配置c3p0-config default-config property namedriverClasscom.mysql.cj.jdbc.Driver/property property namejdbcUrljdbc:mysql://127.0.0.1:3306/hospital_db?useSSLfalseamp;serverTimezoneAsia/Shanghaiamp;characterEncodingutf8mb4/property property nameuserroot/property property namepasswordyour_password/property !-- 连接池基础参数 -- property nameinitialPoolSize3/property property namemaxPoolSize10/property property nameminPoolSize2/property property nameacquireIncrement2/property !-- 连接有效性检测 -- property nameidleConnectionTestPeriod30/property property namemaxIdleTime60/property property nametestConnectionOnCheckouttrue/property /default-config /c3p0-config参数说明useSSLfalse关闭 SSL课设无需serverTimezoneAsia/Shanghai解决时区报错characterEncodingutf8mb4支持 emoji课设虽不用但养成习惯testConnectionOnCheckouttrue每次取连接前检测有效性避免拿到失效连接。4.2 DBManager 单例全局连接池管理与线程安全封装public class DBManager { private static ComboPooledDataSource cpds; static { try { cpds new ComboPooledDataSource(); // 自动加载 c3p0-config.xml } catch (Exception e) { throw new RuntimeException(初始化连接池失败, e); } } public static Connection getConnection() throws SQLException { return cpds.getConnection(); // C3P0 自动管理连接生命周期 } public static void closeConnection(Connection conn) { if (conn ! null) { try { conn.close(); // 归还连接到池非真正关闭 } catch (SQLException e) { e.printStackTrace(); } } } }注意conn.close()在连接池中是“归还”而非“销毁”这是池化核心static块确保类加载时初始化避免多线程竞争课设中所有 DAO 方法都通过DBManager.getConnection()获取连接禁止new DriverManager.getConnection()。4.3 事务边界设计挂号、退号、结算三场景的 ACID 保障课设必须体现事务意识。以“退号退费”为例跨 patient、fee 两张表public class RegistrationService { public static void cancelRegistration(int registrationId) throws SQLException { Connection conn null; PreparedStatement ps1 null; PreparedStatement ps2 null; try { conn DBManager.getConnection(); conn.setAutoCommit(false); // 关键关闭自动提交 // 1. 更新挂号状态为已退 ps1 conn.prepareStatement(UPDATE registration SET status ? WHERE id ?); ps1.setString(1, 已退); ps1.setInt(2, registrationId); ps1.executeUpdate(); // 2. 插入退费记录 ps2 conn.prepareStatement(INSERT INTO fee (patient_id, amount, type, create_time) VALUES (?, ?, ?, NOW())); // ... 设置参数 ps2.executeUpdate(); conn.commit(); // 全部成功才提交 } catch (SQLException e) { if (conn ! null) { conn.rollback(); // 任一失败则回滚 } throw e; } finally { // 关闭资源顺序ps2 - ps1 - conn if (ps2 ! null) ps2.close(); if (ps1 ! null) ps1.close(); if (conn ! null) DBManager.closeConnection(conn); } } }逻辑说明conn.setAutoCommit(false)是事务起点conn.commit()和conn.rollback()成对出现finally中按反序关闭资源PS 先于 Connection避免连接泄露课设答辩时老师必问“如果退号成功但退费失败数据一致性怎么保证”——这就是标准答案。4.4 避坑JDBC 常见问题排查现象 → 原因 → 解决现象java.sql.SQLException: No value specified for parameter 1原因PreparedStatement的?占位符未全部设置值或setXxx()顺序与 SQL 中?位置不匹配。解决检查prepareStatement的 SQL 字符串中?个数与setXxx()调用次数是否一致用ps.toString()打印预编译语句确认。现象Swing 界面卡死控制台无报错原因在 EDT 中执行了耗时的 JDBC 查询如PatientDAO.findAll()阻塞了整个 UI 线程。解决所有数据库操作必须包裹在SwingWorker中UI 更新逻辑放在done()方法内。现象中文存入 MySQL 后显示为??原因JDBC URL 缺少characterEncodingutf8mb4或 MySQL 服务端字符集非utf8mb4。解决检查my.ini中[mysqld]段落是否有character-set-serverutf8mb4JDBC URL 必须含characterEncodingutf8mb4建库时指定CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci。现象java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver原因MySQL 8.0 驱动类名从com.mysql.jdbc.Driver变更为com.mysql.cj.jdbc.Driver且 JAR 包未放入lib目录或未添加到构建路径。解决确认使用mysql-connector-java-8.0.33.jarIDE 中右键项目 → Build Path → Add External Archives选择该 JAR。现象SQLException: Cannot delete or update a parent row: a foreign key constraint fails原因尝试删除department表中某条记录但doctor表中仍有department_id指向它。解决检查外键约束定义课设中应设为ON DELETE RESTRICT禁止删或先清空doctor表中相关记录再删科室。5. 课设答辩高频考点与代码验证技巧让老师一眼看到你的工程素养5.1 三分钟演示脚本精准命中答辩评分点别从登录页开始按此顺序演示计时 3 分钟打开 MySQL Workbench→ 执行SELECT COUNT(*) FROM registration WHERE status已就诊;展示数据真实存在启动 Java 程序→ 点击“挂号”面板 → 输入张三、身份证号、选择内科 → 点击“挂号” → 弹出“挂号成功”切换到“患者列表”→ 滚动表格找到张三记录 → 点击“就诊”按钮 → 表格中该行状态变为“已就诊”切到“收费”面板→ 输入张三 ID → 点击“结算” → 弹出“结算成功金额25.00元”回到 MySQL→ 执行SELECT * FROM fee WHERE patient_id (SELECT id FROM patient WHERE name张三);展示 fee 表新增记录。关键每一步都伴随一句解说词如“这里体现了外键约束的完整性”、“结算操作触发了跨表事务”——让老师听到你的设计意图。5.2 代码质量自查清单答辩前必做检查项合格标准检查方式包结构com.hospital.dao、com.hospital.service、com.hospital.ui三层分明查看 src 目录树异常处理所有SQLException都被捕获且e.printStackTrace()存在全局搜索catch (SQLException资源关闭每个Connection、PreparedStatement、ResultSet都在finally中关闭搜索close()方法调用位置SQL 注入防护所有动态 SQL 都用PreparedStatement无字符串拼接搜索SELECT * FROM 或 tableNameSwing 线程所有数据库操作都在SwingWorker中UI 更新在done()内搜索SwingWorker和done()5.3 一个血泪经验从那以后我每次重构 Swing 界面都强制走一遍“事件流逆向追踪”具体做法选中任意一个按钮如“退号”从addActionListener开始逐层跳转btnCancel.addActionListener(...)→ 进入 Lambda 表达式Lambda 中调用CancellationService.cancel(...)→ 进入 Service 层Service 层调用RegistrationDAO.updateStatus(...)和FeeDAO.insertRefund(...)→ 进入 DAO 层DAO 层检查PreparedStatement是否正确绑定参数、executeUpdate()是否调用、close()是否在finally。这个过程平均耗时 8 分钟但它能暴露 90% 的逻辑断裂点——比如 Service 层忘了调用 DAO或者 DAO 层close()写在try里而非finally。我带过的课设里凡是答辩前做过三次以上逆向追踪的同学100% 一次通过。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网