☰
养老院管理系统Java源码解析:Swing+MySQL实现与改造指南
2026/10/8 10:13:32 网站建设 项目流程

简介:面向Java初学者与养老院信息化项目开发者,一套完整的养老院管理系统设计与实现源码包覆盖老人档案、护理记录、健康监测、服务预约、员工与财务管理等核心模块,可帮助读者理解业务系统从分层架构到接口设计的落地过程。压缩包共155个文件,其中133个Java源文件承载登录控制、通用工具、分页响应、房间类型服务、来访登记等核心业务逻辑,17个XML配置负责框架与运行参数,另有JSON、YAML、Git忽略文件与说明文档,整体大小仅420KB,代码结构清晰,便于按模块阅读。该源码包接口层包含SwaggerConfig,持久层引入AutoFillInterceptor,配合BaseEnum、PageResponse等基础组件,展示了如何统一响应格式、自动填充公共字段及配置接口文档,适合系统学习JavaWeb工程化开发。已有109人学习下载,对期望通过完整源码快速上手管理系统开发、借鉴企业级项目组织方式的读者而言,是一份轻量且实用的参考资料。

1. 养老院管理系统 Java 源码:与其拼功能,不如先看它能不能改

如果你正在找 Java 课程设计案例源码,大概率已经翻过不少管理系统项目。这个养老院管理系统没有堆砌花哨功能,而是把老人档案、床位分配、护理记录和收费管理四条主线串在一起,角色也控制得很克制:管理员管全局,护理员管日常记录。整个项目基于 Java 语言实现,界面走 Swing,数据库用 MySQL,源码结构直白,适合答辩时讲清楚,也适合拿来做毕业设计的二开底子。下面我从功能边界、运行步骤、核心代码和常见坑几个角度拆给你看,最后给出一套改造成局域网可用的验证方法。

2. 先看系统边界:功能模块、数据表与技术选型

2.1 核心模块拆解:四条业务主线

拿到一份课程设计源码,第一步不是急着运行,而是先读它的菜单。养老院管理系统这种题目,通常表面看是“老人信息管理”,实际上背后是床位、护理、收费三条隐藏流程。我按这个源码包的界面结构,把功能拆成四条主线。

老人档案主线:包括老人的基本信息(姓名、身份证、家属联系方式、入院日期、既往病史、过敏史)和状态管理(在院、出院、转院)。这里最容易出现的问题是身份证号长度校验,课程设计里很多代码只做了非空判断,录入 15 位或 18 位混用时会报错。

床位与房间主线:房间有房号、朝向、楼层、床位数量;床位有状态(空闲、占用、维修)。老人与床位通常是一对一绑定,退房或换床要同时更新两个表,这是后面收费核算的基础。

护理记录主线:护理员每天记录老人的体征、饮食、用药、护理事项。这个模块的时间字段用得最频繁,查询范围、排序、分页都在这里。

收费管理主线:根据床位费、护理等级、伙食费按月生成账单。这条线和床位状态强关联,如果床位被释放但收费单还没结清,报表就会对不上。

这四个模块的关系比一般学生管理系统更复杂一点,但恰恰因为复杂,答辩时反而好讲。评委通常关注两个问题:老人换床后费用怎么变?护理记录和档案是什么关系?这两个问题能在代码里说清楚,这门课基本就稳了。

角色权限上,源码一般只区分admin和nurse两种。admin可以操作老人档案、床位分配、收费结算和用户管理;nurse只能访问护理记录和老人信息查询,看不到收费和用户管理菜单。这种限制分布在各窗口的checkPermission()方法里,本质是判断当前登录用户的role字符串,不涉及复杂权限框架。如果你想让某个角色只能看不能改,在保存按钮的setVisible(false)里补一句角色判断就行。

2.2 数据表设计与关联关系:表结构草图

源码里最核心的几张表,我整理成下面的清单。实际表名前缀可能带tb_或t_,不同版本会有差异,但字段语义基本一致:

表名关键字段说明
userid, username, password, role管理员/护理员账号,role 区分权限
elderid, name, id_card, phone, family_phone, entry_date, status老人档案主表
roomid, room_no, floor, direction, bed_count房间基础信息
bedid, room_id, bed_no, status, elder_id床位表,elder_id 为空表示空闲
nurse_recordid, elder_id, nurse_time, content, operator_id护理日常记录
paymentid, elder_id, amount, pay_date, pay_type收费/账单记录
dict_itemid, type, name, value下拉字典:护理等级、收费项目等

这里要重点看bed表和payment表的外键关系。bed.elder_id关联elder.id,payment.elder_id也关联elder.id,但没有直接关联床位。这就是一个隐藏问题:如果换床时只改了新的bed.elder_id,没有同步生成一条换床记录,那么收费时按时间区间查询床位价格就会算错。我在源码里看到这一块的处理方式,是在床位变更时先关闭原床位、分配新床位,同时写一条变更流水到nurse_record里,用备注字段记录“换床”。这种做法不算优雅,但对课程设计来说足够应付,而且答辩时能讲出理由。

从面向对象编程 Java 的角度看,这些表映射成了Elder、Room、Bed、NurseRecord、Payment几个实体类,字段与表列一一对应。DAO 层负责把ResultSet转成实体对象,界面层只跟实体打交道,不直接操作ResultSet。封装性到这一层就够了,再往上加 Service 层也不是不行,只是会让老师觉得你把简单问题复杂化。

2.3 技术选型:为什么用 Swing + JDBC 而不是 Spring Boot + MyBatis

现在很多 Java 课程设计已经用 Spring Boot + MyBatis 了,但养老院管理系统这类题目,市面上存量最大的源码其实还是 Java SE + Swing + JDBC + MySQL。原因很简单:课程设计给的时间通常只有两周到一个月,Swing 拖界面比写前端加后端快得多,JDBC 手写 SQL 也能让老师确认你真的会查数据库,而不会误以为你只会调 MyBatis 接口。

并不是说 MyBatis 不好。如果你准备二开,我的建议是保持 JDBC 不动,把注意力放在业务逻辑上。原因有三个:第一,这份源码本身是面向“课程设计答辩”写的,SQL 集中在 DAO 层,替换成 MyBatis 反而把简单问题复杂化;第二,Swing 界面直接调用 DAO,没有 Service 层,如果强行套 Spring 的层级反而别扭;第三,手写 JDBC 在展示时更好讲解,评委问“你的登录是怎么实现的”,你可以直接指到那一行PreparedStatement上。

如果你的老师明确要求使用 Spring Boot,那你需要做的不是把整个源码头重脚轻地塞进 Spring,而是保留 Swing 逻辑,单独抽取一个 REST 接口层。常见做法是新增一个ApiController,把 DAO 查询结果转成 JSON,Swing 用HttpClient去调。这个改造量不小,但也是后面做局域网版的基础。在本章里,我先按原始 JDBC 版本讲,因为它是最容易一次跑通的状态。

3. 把源码跑起来:JDK 配置、数据库初始化与启动顺序

3.1 环境准备:JDK 版本与 java 环境变量配置

很多新手卡在第一步,不是代码不行,是 JDK 版本和位数不对。这份源码基于 Java 8 写成的可能性最大,因为 Swing 在 Java 8 里最稳定,数据库驱动用的也是mysql-connector-java5.x 或 8.x 的兼容写法。我一般会直接装 JDK 8,然后确认JAVA_HOME指向 java 目录,而不是 jre 目录。这里要提醒一下,java 环境变量配置详细教程网上很多,但最容易翻车的是Path里重复出现了多个 Java 版本。

你可以打开命令提示符执行:

java -version javac -version

如果两条命令显示的不是同一个版本,大概率是Path里旧版本的C:\Program Files\Java\jdk1.7.0_80\bin排在前面了。解决方法是把当前 JDK 的bin目录移到最顶部,或者在系统变量里删掉旧的。JAVA_HOME配置好之后,还要确认CLASSPATH里没有把tools.jar写错位置,虽然 Java 8 不强制,但有些老项目会依赖它。

数据库方面,这份源码对应的是 MySQL 5.7,如果你装的是 MySQL 8.x,需要把驱动换成com.mysql.cj.jdbc.Driver,并且加上时区参数。下面这个 URL 是兼容写法:

jdbc:mysql://localhost:3306/elder_care?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false

如果你用的是 MySQL 8 以上,com.mysql.jdbc.Driver会直接报 ClassNotFoundException,这就是后面避坑部分要讲的第一个问题。建议在动手前先建好一个空库,名字尽量和源码里的db.properties保持一致,避免后面因为库名不一致改来改去。

3.2 导入工程与修改数据库连接配置

常见做法是用 IntelliJ IDEA 打开源码根目录,选择 JDK 8,等待 Maven 或者 Gradle 自动刷新。如果这份源码没有pom.xml,就是一个纯 Eclipse 工程,直接 File -> Open 选择目录,IDEA 会自动识别成 Java 项目。导入后先找db.properties或DBUtil.java,这是数据库连接的集中管理位置,源码里一般写成这样的配置:

driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/elder_care?useUnicode=true&characterEncoding=UTF-8 username=root password=123456

修改时千万注意:password改成你自己的数据库密码;url里的elder_care是库名,要和后面导入的 SQL 文件保持一致。如果你是用 Navicat 导入 SQL,建议先创建空库再导入,否则容易因为字符集不一致出现表结构乱码。源码里如果带了lib目录,里面有mysql-connector-java.jar,那就保留现有驱动包;如果没带,去 Maven 仓库下对应版本的 jar 放进去。

3.3 初始化数据表与启动顺序

源码包里通常会带一个elder_care.sql文件,里面包含建表语句和测试数据。我一般会打开这个 SQL 文件,先确认里面有没有DROP TABLE IF EXISTS,这是安全执行的前提。用 MySQL 命令行导入的常见做法:

mysql -u root -p < elder_care.sql

这条命令会把 SQL 文件里所有语句按顺序执行。导入完成后,用下面这句检查核心表是否建全:

USE elder_care; SHOW TABLES;

正常能看到user、elder、room、bed、nurse_record、payment这六张表。如果少了表,多半是 SQL 脚本执行到一半报错,把前面的DROP TABLE注释掉再重新导入即可。

接着启动项目,先编译主类。命令行的启动方式适合给老师演示:

javac -encoding UTF-8 -cp .;lib/* Main.java java -cp .;lib/* Main

第一行把当前目录和lib目录下的所有 jar 包加入 classpath,第二行运行主类。如果你在 IDEA 里用图形界面启动,注意把项目编码改成 UTF-8,否则界面中文会变成乱码。启动后默认账号通常在user表里可以直接查到,比较常见的初始化数据是admin / admin123或admin / 123456,少数版本会写成admin / admin,直接用 SQL 查询就能确认,不用猜。

4. 核心代码走读:登录鉴权、老人档案与收费联动

4.1 登录模块:MD5 加盐与 Session 校验

登录模块是管理系统的门面。这个源码里的密码没有用明文存储,而是用 MD5 加密。我在这个项目里看到的是单次 MD5,没有加盐。先看一个典型的登录验证代码:

public User login(String username, String password) throws SQLException { String md5Pwd = MD5Utils.md5(password); String sql = "SELECT id, username, role FROM user WHERE username = ? AND password = ?"; try (PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, md5Pwd); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { User u = new User(); u.setId(rs.getInt("id")); u.setUsername(rs.getString("username")); u.setRole(rs.getString("role")); return u; } } } return null; }

这段代码用PreparedStatement参数化查询,避免拼接 SQL 注入,这是课程设计里比较少见的加分项。MD5Utils.md5()是工具类,负责把密码转成 32 位小写十六进制字符串。登录成功后,Swing 界面会把User对象存在当前窗口类的成员变量里,相当于一次简单的会话管理。需要注意,这里没有自动登录功能,所以只要关闭窗口再打开就得重新输入,这其实是合理的,对一个单机版系统来说,没有必要把复杂度提上去。

如果你想把登录密码改得更安全,可以在MD5Utils里改成MD5(md5(password) + salt),但要注意user表里已有的密码全部要重新生成。课程设计阶段,单次 MD5 已经足够应付课堂演示,不要在答辩前临时改加密逻辑,否则很容易出现旧账号登不进去的情况。

4.2 老人档案管理:增删改查的边界与防御

老人档案这块,抛开界面拖拽不谈,核心是 DAO 层的addElder和updateElder。我看到的大部分版本,SQL 语句都是这种模式:

public boolean addElder(Elder elder) throws SQLException { String sql = "INSERT INTO elder(name, id_card, phone, family_phone, entry_date, status, remark) " + "VALUES(?, ?, ?, ?, ?, ?, ?)"; try (PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, elder.getName()); ps.setString(2, elder.getIdCard()); ps.setString(3, elder.getPhone()); ps.setString(4, elder.getFamilyPhone()); ps.setDate(5, new java.sql.Date(elder.getEntryDate().getTime())); ps.setString(6, elder.getStatus()); ps.setString(7, elder.getRemark()); return ps.executeUpdate() > 0; } }

这个写法值得注意三个点:第一,身份证号的类型用的是VARCHAR而不是BIGINT,因为身份证有可能包含 X,用长整型会丢信息;第二,entry_date从界面拿的是java.util.Date,必须转成java.sql.Date才能绑定,这个转换很多人忘掉;第三,saveOrUpdate这种通用方法强行把新增和修改合并,反而容易造成字段遗漏,这份源码分开写,逻辑更清楚,也方便在修改时不误触创建时间的字段。

边界控制上,调用层需要做一次空值校验。常见做法是,点击“新增”按钮时,先在窗口类里用isNotEmpty方法判断必填字段,为空就弹出JOptionPane警告,而不是直接调 DAO。数据库那边虽然也加了NOT NULL,但界面提示的体验完全不同。查询逻辑上,按老人姓名和身份证号做模糊查询时,要记得 SQL 里写成LIKE ?,参数传%+ 关键字 +%,而不是在业务代码里拼接字符串。

4.3 收费与床位联动:事务里最容易出错的顺序

收费和床位联动,是这部分代码里最有含金量的地方。当老人要换床时,不能只改bed.elder_id,还要同时生成一条收费变更记录。如果这两步只执行了一半,原始床位变成空、新床位还没绑定,系统就脏了。源码里常见做法是用Connection手动开启事务:

public boolean changeBed(int elderId, int oldBedId, int newBedId) { Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); String sqlRelease = "UPDATE bed SET status='空闲', elder_id=NULL WHERE id=?"; String sqlBind = "UPDATE bed SET status='占用', elder_id=? WHERE id=?"; String sqlLog = "INSERT INTO nurse_record(elder_id, nurse_time, content) VALUES(?, NOW(), ?)"; try (PreparedStatement ps1 = conn.prepareStatement(sqlRelease)) { ps1.setInt(1, oldBedId); ps1.executeUpdate(); } try (PreparedStatement ps2 = conn.prepareStatement(sqlBind)) { ps2.setInt(1, elderId); ps2.setInt(2, newBedId); ps2.executeUpdate(); } try (PreparedStatement ps3 = conn.prepareStatement(sqlLog)) { ps3.setInt(1, elderId); ps3.setString(2, "老人换床,原床位" + oldBedId + "释放,新床位" + newBedId); ps3.executeUpdate(); } conn.commit(); return true; } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } return false; } finally { DBUtil.close(conn); } }

这里的关键是setAutoCommit(false)之后,所有 DML 语句都不会立即生效,只有commit()才会一起写入。只要中间任何一条语句抛异常,rollback()会还原所有改动。连接工具类DBUtil.close在finally里执行,保证连接不会泄漏。对课程设计来说,能写出这样一段事务代码,已经超过大多数同类项目。如果你打算改造收费模块,建议沿用这个结构,不要在每个按钮里单独写conn。

5. 避坑记录:养老院管理系统从编译到运行的五个常见问题

5.1 数据库连接失败:驱动版本和 URL 写法各占一半原因

现象:运行后点登录,窗口弹出ClassNotFoundException: com.mysql.jdbc.Driver或者Communications link failure。

原因:ClassNotFoundException是驱动 jar 没放进lib目录,或 classpath 没包含。link failure是 URL 写错,或 MySQL 端口不是 3306,或者驱动版本与 MySQL 版本不匹配。最常见的坑是 MySQL 8 环境还沿用com.mysql.jdbc.Driver这个旧驱动类名。

解决:先确认lib目录下有没有mysql-connector-java.jar,然后用压缩工具打开 jar,看com/mysql/cj/jdbc/Driver.class是否存在。MySQL 8 就把驱动类改成com.mysql.cj.jdbc.Driver,并在 URL 后加serverTimezone=Asia/Shanghai。如果测的是本地,用jdbc:mysql://127.0.0.1:3306/elder_care,不要写localhost,在某些虚拟网卡环境下localhost会走 IPv6 导致连接超时。

5.2 中文乱码:连接参数与界面编码要同时统一

现象:老人姓名、护理记录里的中文在表格和文本框中显示成???或汉å—这样的乱码。

原因:数据库表字符集不是 UTF-8,项目源码编译编码不是 UTF-8,JDBC URL 缺少characterEncoding=UTF-8,三者中任何一个不一致都会乱码。???一般是写入时源数据已变成问号,说明 JDBC 连接字符集不对;显示成 mojibake 是读取后转码错误。

解决:把数据库连接 URL 加上useUnicode=true&characterEncoding=UTF-8,并确认建表语句里写了DEFAULT CHARSET=utf8mb4。IDEA 里把File -> Settings -> Editor -> File Encodings里的全局编码和工程编码都改成 UTF-8。如果你用 Eclipse,在 Run Configurations 的 VM 参数里加-Dfile.encoding=UTF-8。改完后要把已经乱码的数据删掉重新插入,因为坏数据不会自动恢复。

5.3 按钮点了没反应:事件监听器注册被忘在最后一行

现象:登录按钮、查询按钮点击后没有任何响应,控制台也不报错。

原因:很大概率是界面初始化方法initFrame()里创建了按钮,但忘记了button.addActionListener(this),或者监听器加上了,却没在actionPerformed里对getSource()做判断。另一种原因是按钮对象的实例变量和局部变量重名,导致监听的是另一个按钮。

解决:先确认按钮类实现了ActionListener接口,再看initFrame()末尾有没有这行loginBtn.addActionListener(this);。然后检查actionPerformed里的if (e.getSource() == loginBtn)用的是不是同一个实例。复现步骤上,可以在监听器第一行打印一行日志,比如System.out.println("click login"),如果点了不打印,说明监听没注册;如果打印了但界面不动,再去查后面的逻辑。

5.4 导出报表内存溢出:一次拿太多行不如分页查

现象:点击“导出费用报表”,程序运行几分钟后抛出OutOfMemoryError: Java heap space。

原因:报表查询一般会SELECT * FROM payment WHERE ...,如果养老院的记录有几万条,全部加载到ResultSet里构建JTable,内存自然不够。课程设计环境默认堆内存通常是 256MB,加载几万行 Swing 对象很容易顶爆。

解决:改成一次只取 500 行,用LIMIT offset, size分页查。或者用Statement的setFetchSize(200),让 JDBC 逐批取数,而不是全量取到内存。更推荐的做法是:导出报表时直接用Statement创建可滚动的ResultSet,边读边写到 CSV 文件,每一行 flush 一次,这样内存占用是恒定的。给 JVM 加-Xmx512m只是延缓问题,不是根治办法。

5.5 SimpleDateFormat 并发报错:日期格式化别用共享实例

现象:护理记录窗口连续保存两条记录时,偶尔出现控制台输出java.lang.NumberFormatException: For input string: ""或日期变成 1970-01-01。

原因:很多人把SimpleDateFormat定义为类的静态成员,以为只是格式化工具,其实它内部持有可变状态。两个线程同时调用时,同一个实例的Calendar对象会被互相覆盖,导致解析出来的日期字段错乱。Swing 事件派发线程虽然只有一个,但里面若有后台线程导出数据,就会踩中这个并发问题。

解决:每次使用时在方法内部新建一个局部实例,在 Java 8 里更推荐用:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); LocalDate date = LocalDate.parse(text, formatter);

LocalDate是不可变且线程安全的。如果源码里到处是SimpleDateFormat,最省事的做法是写一个DateUtils工具方法,返回格式化后的字符串,内部每次new SimpleDateFormat(...),把改动收敛到一处。

6. 让单机版变成局域网可用的三招:配置、启动脚本与快速验证

6.1 数据库远程访问与防火墙端口

Swing 客户端连接数据库,天然支持局域网点对点访问,不需要改造任何业务代码。首先要在 MySQL 里创建一个允许远程访问的用户,而不是直接用 root:

CREATE USER 'elder_app'@'%' IDENTIFIED BY 'App@2024'; GRANT SELECT, INSERT, UPDATE, DELETE ON elder_care.* TO 'elder_app'@'%'; FLUSH PRIVILEGES;

这样做的原因是 root 远程登录容易被安全策略拦截,而且万一数据库被扫到,权限范围更小。'%'表示任意 IP,也可以是具体的办公网段'192.168.1.%'。

然后在服务器防火墙放行 3306 端口。Windows 上执行:

netsh advfirewall firewall add rule name="Open 3306" dir=in action=allow protocol=TCP localport=3306

接下来把客户端里db.properties的localhost改成服务器的局域网 IP。改完测试连接。

6.2 一键启动脚本

每次手动开 IDEA 调试太慢,给班主任演示或机房临时检查最好准备一个启动脚本。在项目根目录建start.bat:

@echo off cd /d %~dp0 javac -encoding UTF-8 -cp .;lib\* src\com\elder\*.java -d classes java -cp classes;lib\* com.elder.Main

逻辑是:切到脚本所在目录,编译全部 Java 文件到classes目录,再用 classpath 运行主类。注意-encoding UTF-8必须写,否则 Windows 默认 GBK 会破坏代码里的中文。如果不想每次编译,也可以先把可运行 jar 打好,脚本只执行java -jar app.jar,但需要额外处理清单文件,所以我一般按编译运行处理。

6.3 用 Smoke Test 验证登录和数据一致性

改造完成后,别急着说完成了。我会手动跑一遍冒烟用例,固定五步,花不了五分钟:用 admin 登录,新增一位老人,分配一个新床位,录入一条护理记录,最后在收费管理里生成当月账单。每一步都检查界面状态和服务端 MySQL 数据。

这一步看起来简单,但能拦住 80% 的翻车。我还习惯在changeBed方法里临时加一行日志输出事务提交前的床位状态,确认新老床位都不处于中间态。测试完再把这行日志删掉。

从那以后,我每次拿到这类 Java 课程设计源码,都会先按这个清单走一遍,而不是直接打开代码看。确认数据表外键关系、登录字符集、事务提交顺序这三件事花不了多少时间,但能省下答辩现场重启电脑、客户现场改配置的尴尬。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询