☰
医院住院管理系统Java源码:从class反编译到Servlet+JSP+JDBC实战
2026/10/9 10:56:52 网站建设 项目流程

简介:医院住院管理系统是一套面向Java Web开发者、高校学生及毕业设计人群的实战项目源码,业务覆盖患者信息管理、入院/出院办理、病床实时调度、诊疗记录跟踪、医疗费用核算与线上预约挂号等环节,功能链条完整。后端以Java为主要语言,采用Spring Boot构建RESTful API,支持高并发与微服务扩展;前端涉及HTML、CSS、JavaScript、JSP以及Less/Scss预处理器,包含完整页面资源与交互逻辑;数据库端还提供SQL脚本,便于一键初始化表结构。资源包共738个文件,压缩后约13.6MB,文件类型以less、js、css、html、jsp、java、class等为主,同时含ttf/eot/woff字体、png/svg图标、jar依赖、properties配置及项目描述文件,覆盖Web项目从静态资源到后端逻辑、再到部署配置的完整目录结构。目前已有1579人学习下载。通过阅读源码,可同时体会经典Servlet/JSP与Spring Boot结合的实现方式,练习面向对象建模、数据库ER设计、SQL性能优化、前端组件协作以及SQL注入、XSS等Web安全防护;对准备课程设计、毕业设计或想系统性梳理Java Web开发流程的读者,是一份结构完整、便于上手的参考资料。

1. 医院住院管理系统 Java 项目源码:先看 class 文件再谈业务

如果你是冲着「医院住院管理系统 Java项目源码」这个词来的,大概率是两种情况:一是课程设计要交差了,二是想在面试前找一个业务闭环完整的 Java Web 项目啃一啃。这套源码我拆完之后的第一印象是——它比那些堆了十几个 Maven 依赖的「伪完整项目」实在得多。项目核心是经典的 Servlet + JSP + JDBC 架构,覆盖患者入院、出院、病床调度、费用计算、预约挂号、医生与管理员双角色登录这些住院管理主链路。但注意一个关键事实:下载包里大概率只给了编译后的 .class 文件,不是 .java 源文件,这意味着你要么用 IDE 反编译看逻辑,要么把它当成「阅读 + 重构」的练习素材。别慌,这反而是好事,下文我会告诉你为什么会这么判断,以及怎么把它在本地跑起来。

2. 从 class 文件反推架构:Servlet + JSP + JDBC 的老三层

2.1 先数一数 class 清单:控制层、业务层、持久层各就各位

拿到压缩包先别急着解压跑,先看清单。项目正文里列出来的这些 class 文件其实已经把架构说得很明白了:

class 文件职责推断所属层次
AdminLoginServlet / DocLoginServlet管理员登录、医生登录的请求入口控制层
AdminInfoService / DocInfoService管理员、医生的业务逻辑处理业务层
PatiInfoService患者信息增删改查业务层
NewRInfoService新入院记录、预约记录处理业务层
WardInfoService病床信息与状态流转业务层
BaseDaoJDBC 连接、通用增删改查封装持久层
GenerateSQLDao动态生成 SQL 语句持久层
ConvertUtil类型转换、日期格式化等工具工具层

这个类命名习惯是非常典型的「Java Web 课程设计 + 小型医院管理项目」风格——类名直白,一个类管一件事,没有花哨的设计模式。你如果看过一些培训机构的项目就会发现,它们几乎长一个样:XxxServlet 做请求分发,XxxInfoService 处理业务,BaseDao 统一管数据库操作。好处是新手容易看懂,坏处是层与层之间的边界没那么严格,比如 Service 里可能会直接写 JDBC。

这里要纠正一个常见的认知偏差:摘要描述里提到 Spring Boot、RESTful API、Vue.js 这些技术栈,但实际情况是——从 class 文件就能看出来,这套项目压根不是微服务架构,也没有前端框架。它是传统的 JSP 渲染页面 + Servlet 做控制器 + JDBC 直连数据库。Spring Boot 那套描述是通用的项目介绍模板,跟这套源码的实际构成对不上。我拆过不少这样的包,十有八九摘要都是机器写的,真正判断架构还得看 class 文件和 lib 目录。

2.2 老三层架构的运作链路

这套项目的请求流转链路,用一句话概括就是:浏览器请求 → Servlet 接收参数 → Service 处理业务 → Dao 访问数据库 → 结果回填到 JSP 页面。以管理员登录为例,AdminLoginServlet 接收用户名密码,调用 AdminInfoService 去 BaseDao 里查 Admin 表,比对通过后把管理员信息塞进 Session,再 forward 到管理首页。

为什么当年这类项目普遍选 Servlet + JSP + JDBC 而不是 Spring MVC?最现实的原因是:零框架依赖,部署简单。把项目打包成 war 丢进 Tomcat 的 webapps 目录就能跑,不需要 Maven 拉依赖、不需要配 Spring 容器。对教学场景和课程设计来说,这反而是优点——你能看到每一个环节的原始样子,不像 Spring Boot 项目那样被自动配置遮得严严实实。

我一般拿到这种项目会先做一件事:把 class 文件用反编译工具(IDEA 自带 FernFlower,或者 JD-GUI)还原成 .java,然后重点看 BaseDao 和 GenerateSQLDao。因为这两个类决定了这套代码能跑得多顺。BaseDao 里通常维护着一个 Connection 的获取逻辑,可能是 DriverManager.getConnection,也可能是 C3P0 连接池;GenerateSQLDao 更关键——从类名推断,它大概率是根据实体对象的字段动态拼接 INSERT 或 UPDATE 语句的「半自动」工具。如果它确实是用反射遍历实体类字段来生成 SQL,那这套代码在设计上比纯手写 SQL 要进阶一档。

2.3 一个完整的业务请求长什么样

拿「新患者办理入院」这条链路举例。前端页面上填完入院信息提交,NewRInfoService 接手,处理两件事:往患者表插入基本信息,同时往病床表更新床位状态——把某个空闲床位改成占用,并关联上患者 ID。这中间如果第二步失败,第一步的插入也要回滚。所以这个 Service 里应该有事务控制,要么是手动 connection.commit()/rollback(),要么是 BaseDao 里封装了事务模板。

而 WardInfoService 的床位状态设计,一般是一个枚举或状态字段:空闲(0)、占用(1)、待清洁(2)。患者出院时,床位从占用转成待清洁,而不是直接转回空闲,这和真实医院管理流程对得上。这就是我刚才说「比伪完整项目实在」的原因——不少课程设计把床位状态只做成空闲/占用两态,压根不考虑保洁环节。

如果你要拿这套项目去面试,我建议把这三条链路背熟:入院(患者表插入 + 床位占用)、出院(床位转待清洁 + 费用结算)、预约挂号(预约表插入 + 号源扣减)。面试官问业务流程时,直接说你读过这套源码的 class 结构,能讲出数据流转,比背八股文有说服力得多。

3. JDK 8 + MySQL 8 + Tomcat 9:把这套源码在本地跑起来

3.1 环境选型:别一上来就 JDK 17

这套项目是典型的 Java 8 时代产物,class 文件版本大概率是 52(Java 8 对应版本号)或更低。直接用 JDK 17 跑不是不行,但会遇到不少麻烦——比如高版本 JDK 移除了某些老库、模块化系统对反射的限制更严格。最省事的方式是装 JDK 8。如果你电脑上已经有高版本 JDK,也可以下个 jdk8 zip 绿色版解压到单独目录,不碰系统 PATH,只在 IDE 里给这个项目单独指定 JDK 8。

Windows 下配置 JDK 8 的常见做法是这样:下载 jdk8 的 zip 包(不用 exe 安装版),解压到 D:\Java\jdk1.8.0_202,然后配 JAVA_HOME 和 PATH。配完之后命令行验证:

java -version javac -version

输出里能看到 1.8.0_202 或者 java version "1.8.0_202" 就说明没问题。注意一个小坑:如果你装了多个 JDK,cmd 里执行 java -version 时可能命中高版本,因为 PATH 里前面那个生效。这时要么调整 PATH 顺序,要么在 IDEA 的 Project Structure 里直接指定 JDK 路径,后者更省心。

Tomcat 版本选择上,Tomcat 9 配 JDK 8 是稳的,Tomcat 8.5 也行。别上 Tomcat 10——Tomcat 10 把 javax.servlet 包名换成了 jakarta.servlet,这套老代码的 Servlet 全是 javax 开头,丢进去直接 500。

3.2 MySQL 8 的两个硬坑:认证插件和时区

数据库方面,这套项目当年的开发环境基本是 MySQL 5.x,但你现在装的大概率是 MySQL 8.0。MySQL 8 默认的认证插件是 caching_sha2_password,老版本的 MySQL Connector/J 驱动不认识它,连接时会报 Unable to load authentication plugin。解决办法两个:把驱动换成 8.x 的 mysql-connector-java,或者把 MySQL 用户认证改回 mysql_native_password。

我的建议是直接换驱动。根目录 lib 或 WEB-INF/lib 里如果躺着 mysql-connector-java-5.1.x.jar,把它换成 8.0.x 版本,连接 URL 也要改:

driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/hospital?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false username=root password=你的密码

注意 URL 里必须带上 serverTimezone=Asia/Shanghai,否则 MySQL 8 会报 CST 时区识别错误。这就是搜索引擎里常说的 mysql80 zip 配置教程里反复强调的那几个参数,其实核心就是这个。

数据库本身如果是 zip 安装的 MySQL 8,先初始化:

mysqld --initialize-insecure --basedir=D:/mysql-8.0 --datadir=D:/mysql-8.0/data net start mysql mysql -u root -p

initialize-insecure 会生成一个空密码的 root 账号,登录后立刻改密码:

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456'; FLUSH PRIVILEGES;

3.3 IDEA 导入 + 部署运行

用 IDEA 打开这个项目(不是新建),选 Open 然后定位到解压目录。IDEA 会识别为一个普通 Java 项目,这时候要手动做三件事:把项目结构里的 Source 标记为 src(如果反编译后有源码)、把 lib 下的 jar 加入 Libraries、配置 Artifacts 打成 war exploded 模式。

部署的核心配置在 Run/Debug Configurations 里选 Tomcat Server → Local,Application server 指向你解压的 Tomcat 目录,Deployment 选项卡里把 war exploded 加进去,Application context 填 /hospital。然后启动:

# IDEA 里直接点运行,或者手动拷贝 war 到 Tomcat cp target/hospital.war /path/to/tomcat/webapps/ /path/to/tomcat/bin/startup.bat

启动后浏览器访问 http://localhost:8080/hospital/ ,能看到登录页就说明环境通了。如果你看到 404 或者 500,先去看 Tomcat 的 logs/catalina.out,重点搜 ClassNotFoundException 和 SQLException,这两个异常覆盖了八成启动失败的原因,具体排查我放在第 5 章。

有一点要提醒:如果压缩包里的 class 文件没有对应 .java 源文件,IDEA 里是直接看不了源码的。我一般会先把 class 文件所在的整个目录复制一份,然后在 IDEA 里选中这些 class 按快捷键反编译(IDEA 内置 FernFlower 插件会自动反编译 class 文件并显示为可读源码)。反编译出来的代码没有注释、泛型可能丢失,但跑通流程不受影响。

4. 核心业务与数据库:床位、费用、预约是怎么落表的

4.1 表结构设计参考

这套项目里我推测核心表不少于七张:管理员表、医生表、患者表、病床表、入院记录表、费用明细表、预约挂号表。表结构设计是这类项目的灵魂,面试也常被追问。按这套项目的业务范围,合理的建表脚本长这样:

CREATE TABLE patient ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, gender CHAR(1), age INT, phone VARCHAR(20), id_card VARCHAR(18), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE ward ( id INT PRIMARY KEY AUTO_INCREMENT, ward_no VARCHAR(20) UNIQUE, status TINYINT DEFAULT 0 COMMENT '0-空闲 1-占用 2-待清洁', patient_id INT, ward_type VARCHAR(10) COMMENT '普通/ICU', fee_per_day DECIMAL(8,2) ); CREATE TABLE admission_record ( id INT PRIMARY KEY AUTO_INCREMENT, patient_id INT, ward_id INT, admit_date DATETIME, discharge_date DATETIME, diagnosis VARCHAR(255), status TINYINT COMMENT '0-在院 1-已出院' ); CREATE TABLE fee_record ( id INT PRIMARY KEY AUTO_INCREMENT, admission_id INT, fee_type VARCHAR(20) COMMENT '药品/检查/床位', amount DECIMAL(10,2), create_time DATETIME ); CREATE TABLE appointment ( id INT PRIMARY KEY AUTO_INCREMENT, patient_name VARCHAR(50), doctor_id INT, appoint_date DATE, appoint_time VARCHAR(20), status TINYINT DEFAULT 0 COMMENT '0-待就诊 1-已完成 2-取消' );

这套表结构的关键设计点在 ward 表的 status 字段——我在 2.3 里提到的三态流转,就是靠这个 TINYINT 字段 + WardInfoService 里的状态机逻辑实现的。另外一个值得留意的设计是 fee_record 把费用类型拆成药品、检查、床位三类,这样结算时只要按 admission_id 汇总就能打印费用清单。如果你要扩展,我最建议先加一张 drug 表和 prescription 中间表,让药品费用从手填数字变成按药品单价自动计算——这属于性价比最高的功能升级。

4.2 GenerateSQLDao:不用手写 SQL 的土办法

这个类的存在是这套源码里最让我感兴趣的部分。GenerateSQLDao 这类工具,本质上是在模仿 ORM 框架的基础能力——通过反射读取实体类的字段和值,拼出 INSERT、UPDATE、DELETE 语句。核心思路大概是:

public class GenerateSQLDao { public static String generateInsert(Object obj) { Class<?> clazz = obj.getClass(); Field[] fields = clazz.getDeclaredFields(); StringBuilder columns = new StringBuilder(); StringBuilder values = new StringBuilder(); for (Field field : fields) { field.setAccessible(true); try { Object value = field.get(obj); if (value != null) { columns.append(field.getName()).append(","); values.append("'").append(value).append("',"); } } catch (IllegalAccessException e) { e.printStackTrace(); } } // 去掉末尾逗号,拼成 INSERT INTO return String.format("INSERT INTO %s (%s) VALUES (%s)", clazz.getSimpleName().toLowerCase(), columns.substring(0, columns.length() - 1), values.substring(0, values.length() - 1)); } }

逻辑不复杂:拿到实体对象的 Class,遍历所有字段,字段名就是列名,字段值反射读出来塞进 SQL。这套做法在今天看确实简陋——SQL 注入风险取决于外层有没有用 PreparedStatement 参数化;字段名和表名靠类名映射,一旦表名和类名对不上就废。但作为教学项目,它很直观地演示了「反射能做什么」,而且你现在去看 MyBatis-Plus 的 insert 方法,思路本质上和它一模一样,只是加了注解映射、日志、缓存这些工程化能力。学习的时候把这个对比记在心里,面试被问到「ORM 底层原理」时,拿 GenerateSQLDao 当起点讲比干背源码强。

4.3 BaseDao 里应该有什么

BaseDao 的角色是统一数据访问入口。一个合格的 BaseDao 通常会封装这几个方法:getConnection()(负责拿连接)、executeUpdate(String sql, Object... params)(执行增删改)、executeQuery(String sql, ResultSetMapper mapper)(执行查询并做结果映射)、closeAll()(关闭连接和语句)。这套项目如果走的是连接池方案,getConnection 里可能是从 com.mchange.v2.c3p0.ComboPooledDataSource 取连接;如果没引第三方库,那就还是 DriverManager,每次请求新建连接——性能不行但思路简单。

这里有个容易翻车的地方:手工拼接 SQL 时,字符串里的单引号、日期格式、中文编码三件事最容易出错。ConvertUtil 这个类就是用来收拾这些杂活的——除了类型转换,它大概率还封装了日期格式化、字符串判空、HTML 转义之类的工具方法。你在重构这套代码时,优先把这个类的方法补全,能让后面所有 Service 层的代码减少不少重复。

5. 常见问题排查:启动失败、乱码、驱动不兼容的五个现场

5.1 Tomcat 启动报 ClassNotFoundException

现象:Tomcat 启动后访问项目地址直接 500,日志里出现 java.lang.ClassNotFoundException: com.mysql.jdbc.Driver 或者找不到某个 Servlet 类。 原因:编译后的 class 文件和依赖 jar 没进到 WEB-INF/classes 和 WEB-INF/lib 里。IDEA 部署模式选成了 war 而不是 war exploded,导致 lib 目录的 jar 没被拷贝到 Tomcat 的运行目录。 解决:在 IDEA 的 Artifacts 设置里检查 Output Layout,确认 lib 下的所有 jar 都在 WEB-INF/lib 节点下;部署模式改选 war exploded,它会把整个项目目录直接映射到 Tomcat,启动后改动 JSP 也不用重启。

5.2 MySQL 8 连接报 Communications link failure

现象:打开登录页面后点登录,页面卡顿几秒然后报 com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure。 原因:两个高频因素——驱动版本太老(很多这套源码自带 5.1.x 驱动,不认 MySQL 8 的认证方式),或者 URL 没带 serverTimezone。如果你在运行日志里看到 The server time zone value '�й���ʱ��' 这类乱码,就是时区问题。 解决:把驱动替换成 mysql-connector-java-8.0.x,URL 完整写法参考 3.2 里那段配置。如果数据库里已经用 caching_sha2_password 插件,顺手执行一遍 ALTER USER 改回 mysql_native_password,双保险。

5.3 JSP 页面中文全是问号

现象:页面标题、患者姓名、诊断信息全是 ???,或者插入数据库的中文变成乱码。 原因:三层乱码叠加——JSP 文件本身的 pageEncoding 不是 UTF-8、请求参数到后端时没按 UTF-8 解码、数据库连接 URL 没带 characterEncoding 参数、表字段字符集是 latin1。四个环节断一个,中文就坏。 解决:先统一 JSP 头部的 pageEncoding="UTF-8",再给 MySQL 连接 URL 加 characterEncoding=utf8,最后检查表字符集:ALTER TABLE patient CONVERT TO CHARACTER SET utf8mb4。还有一个治本方案是写一个 CharacterEncodingFilter,在 doFilter 里强制 request.setCharacterEncoding("UTF-8"),这是老 Java 项目里标准的过滤器写法,比在每一个 Servlet 里手动设置干净得多。

5.4 IDEA 导入项目全是红叉但不报错

现象:IDEA 能打开项目,所有文件都没有编译报错,但项目目录上有个大红叉,点运行时提示 Create Process failed。 原因:项目没有绑定正确的 SDK 和运行环境——很常见的是 Project SDK 选成了 JDK 11,但项目依赖的 Tomcat 版本是 8.5(基于 JDK 8 编译);或者 Project Structure 里没有指定 Web 模块的 Web Facet 和对应的 Tomcat Server Runtime。 解决:File → Project Structure → Project 里把 SDK 换成 1.8;Modules → Dependencies 里确认 Tomcat 的 library 勾上了;Modules → Web 里检查 Web Facet 已启用,同时把 facet 关联的 Server Runtime 指向你本地 Tomcat。

5.5 端口被占用或 Tomcat 关闭后 java 进程还在

现象:第二次启动 IDEA 里 Tomcat 时报 8080 端口被占用,或者 Tomcat 提示 Address already in use: JVM_Bind。 原因:上次运行 Tomcat 时 IDEA 没把进程杀掉——常见于上一次是手动点击「停止」但线程卡住,或者是用 startup.bat 启动的 Tomcat 一直没关,残留了一个 java 进程占着端口。 解决:命令行里查端口占用并清掉残留进程:

netstat -ano | findstr 8080 taskkill /PID 上一步输出的PID号 /F

如果是 Linux 环境,就是 lsof -i:8080 然后 kill -9 PID。清完之后再启动。这套操作也适用于排查「Tomcat 一直起不来」的通用场景——先确认端口干净,再谈其他。

6. 进阶改造:把 BaseDao 升级成泛型 DAO,这项目还能再战五年

老项目最大的痛点是重复代码集中在 Service 层:每新增一张表的业务,就要在 Dao 里写一遍近乎相同的增删改查。但你已经看懂了 GenerateSQLDao 的反射思路,完全可以用泛型把它收编成一个真正可复用的 BaseDao:

public class BaseDao<T> { protected Class<T> entityClass; @SuppressWarnings("unchecked") public BaseDao() { this.entityClass = (Class<T>) ((ParameterizedType) getClass() .getGenericSuperclass()).getActualTypeArguments()[0]; } public T findById(int id) { String sql = "SELECT * FROM " + tableName() + " WHERE id = ?"; // 用反射读取字段,把 ResultSet 转成 T } public int insert(T entity) { String sql = GenerateSQLDao.generateInsert(entity); // 改用 PreparedStatement 执行,避免 SQL 注入 } }

改造的核心就两步:第一步,构造函数里利用泛型反射拿到实体类的 Class;第二步,把 GenerateSQLDao 生成的裸 SQL 从 Statement 换成 PreparedStatement 参数化执行,堵住注入风险。改完以后,PatientDao 只需继承 BaseDao<Patient> 并敲一个空类,就自动拥有了 findById、insert、update、deleteById 全家桶。这套写法放在面试里也经得住问——从老代码发现问题、用泛型和反射做抽象、顺手解决安全漏洞,是完整的重构叙事线。

还有 ConvertUtil 也值得深挖。老项目里日期格式、BigDecimal 计算、null 处理散落在各处,你把 ConvertUtil 补成一个静态工具集后,费用计算那里能明显瘦身——费用按天累加的逻辑,用 BigDecimal 而不是 double 算,能避免精度丢失。如果你时间充裕,再给它加一层导出 CSV 或 Excel 的方法,报告打印功能就有了真正的落点。

从那以后,我每次拿到一套老 Java 项目,都会先干三件事:翻 lib 目录确认依赖版本,看 BaseDao 判断数据访问方式,跑一个最小流程确认环境。这套住院管理系统的价值恰恰在于它足够老——老到你还能一眼看清 Servlet、JSP、JDBC 各自在干什么,又足够完整到能练业务拆解和代码重构。如果你正卡在「看过很多教程但没完整跑通过一个项目」的状态,花一个周末把它跑起来再顺手做一次 DAO 层的泛型改造,收获会比刷十篇 Java 基础笔记都大。希望帮到你。

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

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

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

立即咨询