ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JSP+MySQL 5.7医院住院管理系统开发实践详解

JSP+MySQL 5.7医院住院管理系统开发实践详解 简介以Java语言和MySQL数据库实现的医院住院管理系统毕业设计论文面向计算机相关专业毕业生与Java开发者针对医院住院管理信息化需求提供从选题到系统实现的完整参考。资源共1个doc文档约1.6MB内容覆盖绪论、相关技术MySQL5.7、Java、B/S架构、navicat工具、需求分析与用例、数据库概念结构与E-R图、系统详细设计与关键代码、系统测试等模块结构清晰。已有169人学习浏览。文档不仅阐述选题背景、国内外现状与可行性研究还具体给出医生信息、病人、床位、收费等核心模块的设计思路并展示用户登录验证、入院登记等关键代码既可作毕业论文写作模板也能帮助读者快速理解医院住院管理系统的业务流程与开发要点。1. 为什么一个 JSP MySQL 5.7 的住院系统反而更适合入门很多小规模医疗机构到现在还在用 Excel 表维护病床占用和住院收费护士站和收费处各记各的账床位状态经常对不上。这套用 Java JSP MySQL 5.7 构建的医院住院管理系统把医生信息、病人入院、病床分配、收费明细和统计分析收拢到一个 B/S 应用里正好解决这个真实痛点。技术选型上它没有引入微服务和前后端分离而是用 JSP 动态拼页面、Servlet 做控制层、Service DAO 分层访问 MySQL——这种结构对初学者友好能清晰地看到一次请求从浏览器到数据库再返回的全过程也适合作为毕业设计和中小型医院信息化的参考底座。下面会按数据建模、登录权限、入院流程、收费统计、部署验证这条线逐层拆开。2. 从 E-R 图到建表 SQLMySQL 5.7 的住院系统数据模型设计2.1 实体划分与关系医生、病人、病床、收费如何关联先明确边界。这套系统里的核心实体有五个医生、病人、病床、收费信息、管理员。但落在关系模型里病人与病床是一对一的关系——一个病人住院期间占用一张病床医生与病人是一对多——一个主治医生名下可以有多个住院患者收费信息则挂在病人下一次住院可以产生多条收费记录。这里有一个容易踩的坑病床表里的使用状态和病人表里的病床号实际上是两个方向的数据冗余。病人表通过病床号字段指向病床表病床表又用 use_status 字段标记是否被占用。这种设计在查询时很直观但更新时容易产生不一致——比如出院流程只删了病人记录忘了把病床状态改回空闲。后面第 4 章会专门说事务怎么兜住这一步。2.2 表结构对照字段类型、长度与空值约束原论文给出的字段表里长度大多写了 32 字节实际建表时我建议重新梳理一遍。varchar(32) 对于中文姓名没问题但病症这一列往往超过 32 个字符收费项目的描述、医生科别也需要放宽。表名字段类型约束说明doctoridvarchar(32)primary key医生编号doctornamevarchar(32)not null姓名doctorzhichengvarchar(32)-职称doctorkebievarchar(32)not null科别patientidvarchar(32)primary key病人编号关联收费patientbed_novarchar(32)not null病床号patientyisivarchar(32)-主治医师bedbed_novarchar(32)primary key病床号bedchuangwei_feedecimal(10,2)not null default 0床位费用chargeidintprimary key auto_increment收费流水号chargepatient_idvarchar(32)not null关联病人adminnamevarchar(32)primary key管理员账号adminpasswordvarchar(64)not null密码哈希这个表把原文字段按实际用途重新归并了age、riyuan、chuyuan 这类字段继续放在 patient 表里。设计时尽量把金额列从 varchar 改成 decimal字符串存金额会导致排序混乱、汇总溢出后面第 5 章还会提到。2.3 建表 SQL把 E-R 图落成 MySQL 5.7 可执行的 DDLCREATE TABLE bed ( bed_no VARCHAR(32) PRIMARY KEY COMMENT 病床号, kebie VARCHAR(32) NOT NULL COMMENT 科别, chuangwei_fee DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT 床位费用, use_status TINYINT NOT NULL DEFAULT 0 COMMENT 0空闲 1占用 2维修, INDEX idx_kebie (kebie) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT病床信息表; CREATE TABLE patient ( id VARCHAR(32) PRIMARY KEY COMMENT 病人编号, bed_no VARCHAR(32) NOT NULL COMMENT 病床号, name VARCHAR(32) NOT NULL COMMENT 姓名, age INT NULL COMMENT 年龄, bingzheng VARCHAR(255) NULL COMMENT 病症, yisi VARCHAR(32) NULL COMMENT 主治医师, riyuan DATE NOT NULL COMMENT 入院日期, chuyuan DATE NULL COMMENT 出院日期, CONSTRAINT fk_patient_bed FOREIGN KEY (bed_no) REFERENCES bed(bed_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT病人信息表;DDL 说明use_status 用 TINYINT 而不是 VARCHAR 存空闲/占用前端页面显示时再翻译成中文后端判断只用 0/1/2避免中文编码和空格问题。病人表的主键用业务编号 VARCHAR 而不是自增 int是因为原系统中病人编号直接参与收费和病床的关联用自增 id 反而要在多处回查。外键约束 fk_patient_bed 在 InnoDB 下能保证病人不会引用一张不存在的病床。但注意外键会带来额外锁开销并发量上来之后可以考虑去掉外键、由应用层保证完整性。MySQL 5.7 默认存储引擎 InnoDB支持行级锁和外键约束这是选它的核心理由。字符集用 utf8mb4 而不是 utf8utf8mb4 是 UTF-8 的完整实现能存 emoji 和生僻字5.7 对 utf8mb4 的索引长度支持也足够。2.4 为什么是 MySQL 5.7 而不是 8.0选 MySQL 5.7 的核心理由是运行快、占用内存少、与老技术栈兼容性好。放到今天看5.7 相比 8.0 最大的优势在于配套工具链成熟Tomcat 7、老版本 JDBC 驱动、Druid 连接池这套组合在 8.0 下经常遇到 Public Key Retrieval is not allowed 的报错5.7 几乎没有这类问题。如果项目约束了必须用 5.7那就坚持不需要为了追新而升级。3. 登录验证与 Session 权限控制Filter 与角色隔离的实现细节3.1 登录流程从 form 表单到数据库校验登录页的核心实现是页面加载公共 CSS/JS 文件用户填写账号密码后触发 onsubmit 绑定函数先做前端非空校验再提交到后台 Controller。后台的分层是 Controller → Service → DAO → MySQL。// LoginServlet.java protected void doPost(HttpServletRequest req, HttpServletResponse resp) { String name req.getParameter(name); String password req.getParameter(password); if (name null || password null || .equals(name.trim())) { req.setAttribute(msg, 账号不能为空); req.getRequestDispatcher(/login.jsp).forward(req, resp); return; } Admin admin adminService.login(name, DigestUtils.md5DigestAsHex(password.getBytes())); if (admin null) { req.setAttribute(msg, 账号或密码错误); req.getRequestDispatcher(/login.jsp).forward(req, resp); return; } req.getSession().setAttribute(loginUser, admin); resp.sendRedirect(req.getContextPath() /index.jsp); }代码逻辑说明密码比较这里用的是 Spring 的 DigestUtils 工具做 MD5。MD5 本身不安全只能防明文存储生产环境建议换成 BCrypt论文场景用 MD5 足够。校验顺序很关键先判空、再查库、最后写 Session。把账号或密码错误统一成一条提示避免暴露某个账号是否存在。登录成功后通过 sendRedirect 跳转而不是 forward这样用户刷新页面不会重复提交表单。判空条件里先判断 null 再判断空字符串顺序反了会触发 NullPointerException这是 JSP 项目里最常见的低级报错。3.2 Session 里存什么角色隔离的起点系统里有管理员、医生、患者三种角色。最简单的做法是在 admin 表里加一个 role 字段登录后把整个对象放进 Session后续页面通过 session.getAttribute(loginUser) 判断当前操作者身份。但要注意HttpSession 默认存的是对象的引用如果在 session 生命周期内修改了 admin 对象的字段Session 里的数据也跟着变——多线程操作同一个 session 对象时要考虑这个副作用。3.3 用 Filter 拦截未登录请求JSP 页面数量多不可能在每个页面里都写一遍是否登录的判断。标准做法是在 web.xml 里声明一个 Filter拦截受保护路径filter filter-nameAuthFilter/filter-name filter-classcom.hospital.filter.AuthFilter/filter-class /filter filter-mapping filter-nameAuthFilter/filter-name url-pattern/admin/*/url-pattern url-pattern/doctor/*/url-pattern /filter-mapping// AuthFilter.java public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { HttpServletRequest req (HttpServletRequest) request; HttpServletResponse resp (HttpServletResponse) response; Object loginUser req.getSession().getAttribute(loginUser); if (loginUser null) { resp.sendRedirect(req.getContextPath() /login.jsp); return; } chain.doFilter(request, response); }参数说明url-pattern 用路径前缀匹配比逐个页面配置省事也避免漏配某张 JSP。Filter 里只做有没有登录的判断角色判断放在具体 Controller 中把权限校验全部塞进 Filter 会导致逻辑臃肿且难以调试。login.jsp、静态 css/js 资源不要配置进这个 Filter否则登录页自己都会被拦截出现重定向死循环。3.4 Session 超时与固定会话攻击Tomcat 默认 Session 超时是 30 分钟可以在 web.xml 里显式配置session-config session-timeout30/session-timeout cookie-config http-onlytrue/http-only /cookie-config /session-confighttp-only 防止 JavaScript 通过 document.cookie 读到 JSESSIONID降低 XSS 窃取会话的风险。登录成功后调用 req.changeSessionId() 重新生成会话 ID能防御 Session 固定攻击。这些细节在论文里通常不会细写但答辩时被问到的概率很高。4. 入院登记与病床状态流转事务边界与并发控制4.1 一次入院登记牵动几张表一次入院操作后台要完成三件事把病人信息插入 patient 表、把病床状态从空闲改成占用、建立病人与病床的关联。这三步任何一步失败数据就乱了。病床状态的定义是整个流程的核心use_status 值含义触发操作0空闲可分配1占用入院登记2维修管理员标记4.2 用事务保证三步操作的一致性DAO 层基于 Spring JDBC 的 JdbcTemplate但原生 JDBC 的事务写法更能说明问题public void registerPatient(Patient patient) throws SQLException { Connection conn dataSource.getConnection(); try { conn.setAutoCommit(false); // 1. 条件更新病床只有空闲状态才允许占用 String updateBedSql UPDATE bed SET use_status 1 WHERE bed_no ? AND use_status 0; PreparedStatement ps1 conn.prepareStatement(updateBedSql); ps1.setString(1, patient.getBedNo()); int rows ps1.executeUpdate(); if (rows 0) { throw new RuntimeException(病床已被占用或不存在); } // 2. 插入病人记录 String insertPatientSql INSERT INTO patient (id, name, bed_no, yisi, riyuan) VALUES (?, ?, ?, ?, NOW()); PreparedStatement ps2 conn.prepareStatement(insertPatientSql); ps2.setString(1, patient.getId()); ps2.setString(2, patient.getName()); ps2.setString(3, patient.getBedNo()); ps2.setString(4, patient.getYisi()); ps2.executeUpdate(); conn.commit(); } catch (SQLException e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(true); conn.close(); } }逻辑说明关键在第一步的 updateBedSql 带了 AND use_status 0 条件。两个护士同时给病人分配同一张床时InnoDB 对这一行加锁后执行的那个 update 影响行数是 0直接抛异常回滚不会出现两张病人记录挂一张床的情况。先更新病床、再插入病人顺序不能反。反过来先插病人再改病床状态改状态失败回滚时病人记录一起消失但从业务上看病床状态变化的日志会留下痕迹排查更绕。事务隔离级别用 MySQL 默认的 REPEATABLE_READ 够用不需要刻意调整。InnoDB 的行锁机制在 REPEATABLE_READ 下通过当前读触发行锁上面的 UPDATE 就是当前读。conn.setAutoCommit(false) 必须在 try 块第一行执行如果放在 try 外面finally 里恢复 true 时可能因为前面没正常开启事务而把后续请求的隔离行为改坏。4.3 出院流程的对称性出院是入院的反向操作把病人表的 chuyuan 日期写当天把病床状态改回 0结清剩余费用。业务上必须放在同一个事务里。另外建议加一张 operate_log 表记录每次状态变更的操作人和时间排障时能看出谁在几点改了哪张床。5. 收费明细与统计分析GROUP BY 之外的 SQL 实践5.1 金额字段必须用 decimal收费信息表由收费项目、单价、数量、金额、日期组成。金额字段如果按论文原稿使用 varchar会出现排序错乱和汇总不准。建表后先做一次修正ALTER TABLE charge MODIFY COLUMN amount DECIMAL(10,2) NOT NULL DEFAULT 0;单条收费记录在插入时由 Java 端用 BigDecimal 计算金额前端展示也是 BigDecimal避免 double 的二进制浮点误差。数据库中只存这条记录的金额汇总在 SQL 层面完成。5.2 按科室、按月份的收费汇总统计分析模块在论文里对应的功能是查看收费明细和病床利用率。收费汇总的标准写法SELECT p.kebie AS 科室, DATE_FORMAT(c.riqi, %Y-%m) AS 月份, COUNT(DISTINCT c.patient_id) AS 收费人数, SUM(c.amount) AS 总收入 FROM charge c JOIN patient p ON c.patient_id p.id WHERE c.riqi DATE_SUB(CURDATE(), INTERVAL 6 MONTH) GROUP BY p.kebie, DATE_FORMAT(c.riqi, %Y-%m) ORDER BY 科室, 月份 DESC;说明DATE_FORMAT 把日期归一化到月份配合 GROUP BY 实现按月统计。COUNT(DISTINCT c.patient_id) 统计人数而不是流水条数一个病人多次收费只算一个人。WHERE 用 DATE_SUB 卡最近 6 个月避免全表扫描。MySQL 5.7 默认开启 only_full_group_bySELECT 出来的非聚合列必须出现在 GROUP BY 中。上面 p.kebie 和月份表达式都在 GROUP BY 里不会报错。如果换成分组键的别名会直接报错这是 5.7 与 5.6 行为差异最大的地方。5.3 床位利用率用 CASE WHEN 做占比床位利用率 已占用床位数 / 总床位数按科别统计SELECT kebie AS 科室, COUNT(*) AS 总床位数, SUM(CASE WHEN use_status 1 THEN 1 ELSE 0 END) AS 占用床位数, ROUND(SUM(CASE WHEN use_status 1 THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS 使用率 FROM bed GROUP BY kebie;这里用 CASE WHEN 把占用翻译成数字 1再用 SUM 累加比在 Java 里遍历统计后拼接字符串要直观。ROUND 保留两位小数前端图表直接引用这个结果。5.4 索引设计与慢查询检查收费表按 patient_id 关联查询频率最高patient_id 上必须有索引按 riqi 做时间范围过滤riqi 也要加索引。建议的索引清单索引名列适用场景idx_patient_idpatient_id收费关联病人查询idx_riqiriqi时间范围统计idx_bed_kebiekebie病床利用率分组建表时漏了索引用 EXPLAIN 检查EXPLAIN SELECT * FROM charge WHERE patient_id P001;如果 type 列出现 ALL 就是全表扫描补上索引即可。索引不是越多越好只加高频查询会用到的列。5.5 费用补录日期要允许手动指定收费信息管理容易被忽略的场景是补录——护士漏记一笔费用隔天再录时日期必须回填到实际发生那天。前端日期输入框给默认值但不能禁用后端接收日期参数时要做非空校验。金额字段用 JS 正则过滤掉非数字和小数点以外的内容避免非数字文本进入数据库。6. 部署验证Tomcat 7 Druid 连接池的配置与排错6.1 Druid 连接池的关键参数系统用到 Alibaba Druid 做数据库连接池。论文场景下最需要关注的几个参数是初始连接数、最大连接数、空闲回收周期和连接有效性验证。直接替换 spring-jdbc 的 dataSource 配置jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/hospital?useUnicodetruecharacterEncodingutf8useSSLfalse jdbc.usernameroot jdbc.password123456 druid.initialSize5 druid.maxActive20 druid.minIdle5 druid.maxWait60000 druid.timeBetweenEvictionRunsMillis60000 druid.validationQuerySELECT 1参数说明initialSize5 表示应用启动时预创建 5 个连接避免第一个请求进来还要现场建连接。maxActive20 是连接上限超过后请求等待 maxWait 毫秒超时抛异常。医院住院系统不是高并发场景20 够用。timeBetweenEvictionRunsMillis60000 是空闲连接回收检测周期。validationQuerySELECT 1 用于验证连接有效性MySQL 断连后能及时剔除坏连接。6.2 用 Navicat 验证数据库连接部署前先用 Navicat 连一次数据库。MySQL 5.7 默认 root 只允许 localhost 登录远程连接会报 Host is not allowed to connect。执行授权GRANT ALL PRIVILEGES ON hospital.* TO root% IDENTIFIED BY 123456; FLUSH PRIVILEGES;这个语句只建议在开发环境执行生产环境要给最小权限账号。6.3 Tomcat 部署常见报错项目打包成 war 丢进 Tomcat 7 的 webapps 目录启动后看 catalina.out 日志。最常见的三个问题ClassNotFoundException: com.mysql.jdbc.Driverlib 目录缺 mysql-connector-java 的 jar或者 pom.xml 里依赖的 scope 写错了。Communications link failureMySQL 没有启动或连接串没有加 useSSLfalse。java.sql.SQLException: Access denied for user用户名密码不对检查 jdbc.properties 是否被 IDE 打包进了 classes 目录。6.4 主流程验证跑一遍完整流程管理员登录添加医生新增一张病床录入一个病人观察病床状态从 0 变成 1给病人录两条收费记录到统计页面看科室费用汇总和床位利用率。最后用 SQL 核对页面数字与数据库金额SELECT SUM(amount) FROM charge WHERE riqi 2024-01-01;页面统计模块显示的值与这条 SQL 的差值在 0.01 以内说明金额计算链路没有问题如果差得多优先检查是否混用了 double 字段把 amount 统一改成 decimal(10,2) 再跑一遍验证。本文还有配套的精品资源点击获取
返回列表