尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
JavaWeb医院药品管理系统:MySQL5.7+Tomcat8.5可部署实战
简介本资源是一套面向Java初学者与高校实训学生的医院药品管理系统实战项目适用于JavaWeb课程设计、期末大作业及本科毕业设计。系统采用B/S架构基于Java语言开发融合JSP前端展示、Servlet业务逻辑与MySQL数据库持久化覆盖药品入库、库存查询、用户管理、权限控制等核心医疗管理功能助力学习者扎实掌握MVC分层开发与Web全栈实践能力。压缩包共422个文件含96个HTML页面、54个JS交互脚本、44个编译后Class文件、22个Java源码、14个Jar依赖库、10个CSS样式表及1个完整SQL建库脚本总大小10.21MB结构规范、模块清晰开箱即用。已有499人下载学习所有代码均经严格调试可直接部署运行配套数据库脚本与典型类文件如DrugsDaoImpl、UsersDaoImpl等体现DAO层设计规范便于理解数据访问层实现逻辑与常见CRUD操作封装方式。1. 这不是又一个“学生作业式”JavaWeb项目它真能跑通药品入库、处方发药、库存预警三件套你搜“javaweb医院药品管理系统”十个项目九个卡在登录页——不是数据库连不上就是Tomcat启动后404再不就是点击“发药”直接500报错堆栈里全是NullPointerException。但这个源码包javaweb实训-基于javaweb的医院药品管理系统源码数据库.zip不一样它用的是真实医院药房最小可行流程——从采购单入库 → 库存动态扣减 → 医生开方 → 药师配药 → 过期预警全链路走通数据库结构按《医疗机构药品管理规范》设计不是学生随便建的drug表加name、price两字段更重要的是它默认适配MySQL 5.7 Tomcat 8.5 JDK 1.8这一组最稳定、企业老系统还在跑的黄金组合不是强行塞Spring Boot 3.x搞兼容性玄学。适合两类人一是JavaWeb课设/毕设要交真实可演示系统的同学别再交个静态页面了二是中小民营医院信息科想快速搭个轻量级药品台账的工程师——没预算买商业系统又不敢碰Python或Node.js新栈JavaWeb仍是他们最敢托付的“生产级底线”。提示本项目不带前端美化Bootstrap 3 基础样式不带权限分级只有管理员账号不带报表导出Excel需自行扩展。它的价值在于逻辑闭环、数据库可落地、代码无硬编码路径、部署即用——这才是实训项目该有的样子。2. 从解压到首页可访问本地环境搭建的四步硬核实操2.1 环境确认别跳过这一步90%的“运行失败”源于版本错配先确认你的机器已安装JDK 1.8必须JDK 11 会因Servlet API版本冲突导致HttpServlet类找不到Tomcat 8.5.x推荐 8.5.99避免 9.x 的HTTP/2默认开启引发旧浏览器兼容问题MySQL 5.7.x不是8.0本项目SQL脚本含datetime默认值语法MySQL 8.0严格模式下会报错IDEA 2021.3 或更高版本低版本对Web Facet识别有Bug会导致web.xml不生效验证方式java -version # 必须输出 1.8.0_XXX catalina version # 必须显示 Apache Tomcat/8.5.XX mysql --version # 必须显示 mysql Ver 14.14 Distrib 5.7.XX注意若你用的是Mac M1/M2芯片MySQL 5.7官方不提供ARM64包——此时请改用Docker容器docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456 -e MYSQL_DATABASEpharmacy --name mysql57 mysql:5.7后续所有数据库操作指向localhost:3306即可。2.2 数据库导入执行pharmacy.sql前必须做的三件事解压ZIP后找到根目录下的database/pharmacy.sql文件。别急着双击导入第一步创建编码正确的数据库CREATE DATABASE pharmacy CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;为什么不用utf8因为药品名可能含emoji如某些进口药商品名带®符号utf8在MySQL 5.7中实际是utf8mb3无法存4字节字符会导致插入失败或乱码。第二步修改SQL文件头三行关键打开pharmacy.sql找到开头类似这样的语句-- phpMyAdmin SQL Dump -- version 4.9.2 -- https://www.phpmyadmin.net/ -- -- Host: localhost -- Generation Time: 2023-05-10 14:22:33 -- Server version: 5.7.28-log -- PHP Version: 7.3.12删掉所有--注释行只保留建表和插入语句。否则MySQL命令行客户端会把注释当SQL执行报错You have an error in your SQL syntax。第三步命令行导入比Navicat更可靠mysql -u root -p123456 pharmacy /path/to/pharmacy.sql如果提示ERROR 1067 (42000): Invalid default value for create_time说明你的MySQL 5.7开启了严格模式。临时关闭SET GLOBAL sql_mode(SELECT REPLACE(sql_mode,STRICT_TRANS_TABLES,));2.3 IDEA配置Web项目不是“Open Project”而是“Add Framework Support”很多人直接File → Open整个文件夹结果IDEA识别成普通Java项目webapp目录不标蓝web.xml被忽略。正确做法启动IDEA →File → New → Project from Existing Sources...→ 选择解压后的根目录在向导中取消勾选“Create project from template”→ 下一步到Project SDK页选JDK 1.8 → 下一步关键步骤在Additional Libraries and Frameworks页勾选“Web Application”→ 点击右侧Configure...Web application root directory: 选src/main/webapp若无此目录手动创建Web.xml: 指向src/main/webapp/WEB-INF/web.xml完成后右键项目 →Add Framework Support...→ 再次勾选Web Application→OK血泪经验如果web.xml里配置了welcome-file-list但首页仍404检查src/main/webapp是否被IDEA标为“Resources Root”应为“Web Resources Root”。右键该文件夹 →Mark Directory as → Web Resources Root。2.4 Tomcat部署与启动绕过IDEA的“Artifact”陷阱很多教程教你在IDEA里配Artifact但本项目web.xml中Servlet映射用的是传统servlet-mapping而非注解所以必须确保WAR包结构正确File → Project Structure → Artifacts→ 点→Web Application: Archive→From modules...选中你的模块 →OK在Output Layout页确认WEB-INF/classes下有com/pharmacy/dao/等包路径WEB-INF/lib下有mysql-connector-java-5.1.47.jar项目自带不要勾选“Build on make”—— 改代码后手动Build → Build Artifacts更可控启动前最后检查src/main/webapp/WEB-INF/web.xml中context-param的contextConfigLocation指向classpath:applicationContext.xml存在且路径正确src/main/resources/jdbc.properties中数据库URL、用户名、密码已按你本地环境修改启动Tomcat访问http://localhost:8080/pharmacy/看到登录页即成功。3. 核心业务链路拆解药品入库→库存扣减→过期预警三段代码讲清数据流向3.1 药品入库DrugDao.insert()如何保证批次号唯一且库存累加入库功能对应/DrugServlet?methodadd核心在DrugDao.java的insert(Drug drug)方法public int insert(Drug drug) throws SQLException { String sql INSERT INTO drug (name, spec, unit, price, batch_no, expire_date, stock, create_time) VALUES (?, ?, ?, ?, ?, ?, ?, ?); return queryRunner.update(sql, drug.getName(), drug.getSpec(), drug.getUnit(), drug.getPrice(), drug.getBatchNo(), // 关键批次号作为唯一标识 drug.getExpireDate(), drug.getStock(), new Date() ); }为什么批次号不能重复看数据库DDLALTER TABLE drug ADD CONSTRAINT uk_batch_no UNIQUE (batch_no);这是本项目最务实的设计——不靠UUID生成而用人工录入的“国药准字H2023XXXXX年月日”格式批次号既符合药监要求又避免并发入库时生成相同UUID导致库存错乱。若插入时报Duplicate entry 20230501ABC for key uk_batch_no说明同一批次已存在系统会提示“该批次药品已入库”。3.2 处方发药PrescriptionService.process()怎样实现库存原子性扣减发药操作调用PrescriptionServlet?methodprocess核心逻辑在PrescriptionService.javaTransactional // 关键Spring事务控制 public void process(int prescriptionId) throws SQLException { // 1. 查询处方明细 ListPrescriptionDetail details prescriptionDetailDao.findByPrescriptionId(prescriptionId); // 2. 遍历每条明细扣减对应药品库存 for (PrescriptionDetail detail : details) { // 先查当前库存悲观锁防超卖 Drug drug drugDao.findByIdForUpdate(detail.getDrugId()); // SELECT ... FOR UPDATE if (drug.getStock() detail.getQuantity()) { throw new RuntimeException(药品【 drug.getName() 】库存不足当前 drug.getStock()); } // 扣减库存 drug.setStock(drug.getStock() - detail.getQuantity()); drugDao.updateStock(drug); } // 3. 更新处方状态为“已发药” prescriptionDao.updateStatus(prescriptionId, 2); }为什么用SELECT ... FOR UPDATE而不是乐观锁医院药房场景下并发量不高日均百单但数据一致性要求极高——绝不能出现“两个药师同时发同一盒药结果库存变成-1”。FOR UPDATE在事务提交前锁住该行第二个请求会阻塞等待确保库存扣减绝对准确。注意Transactional注解必须作用于Service层方法且Spring配置中tx:annotation-driven/已启用否则事务不生效。3.3 过期预警ExpireWarningJob如何每天凌晨扫描并生成预警列表预警功能由Quartz定时任务驱动配置在applicationContext.xml中bean idexpireWarningJob classorg.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean property nametargetObject refwarningService/ property nametargetMethod valuecheckExpiredDrugs/ /bean bean idcronTrigger classorg.springframework.scheduling.quartz.CronTriggerFactoryBean property namejobDetail refexpireWarningJob/ property namecronExpression value0 0 2 * * ?/ !-- 每天凌晨2点执行 -- /beanWarningService.checkExpiredDrugs()方法public void checkExpiredDrugs() { // 查找距今30天内过期的药品 String sql SELECT * FROM drug WHERE expire_date BETWEEN ? AND ? AND status 1; ListDrug expiring queryRunner.query(sql, new BeanListHandler(Drug.class), new Date(System.currentTimeMillis() - 30L * 24 * 60 * 60 * 1000), // 30天前 new Date(System.currentTimeMillis() 30L * 24 * 60 * 60 * 1000) // 30天后 ); // 写入预警表供页面查询 for (Drug drug : expiring) { Warning warning new Warning(); warning.setDrugId(drug.getId()); warning.setWarningType(1); // 1临近过期 warning.setWarningDate(new Date()); warningDao.insert(warning); } }预警逻辑为何设为“30天窗口”不是简单查expire_date NOW()而是查未来30天内过期的药品——给药房留出处理时间下架、促销、报损。真正过期的药品在入库时就通过status0标记为“禁用”不会出现在发药列表中。4. 避坑指南五个让90%人卡住的真实问题与解法4.1 现象Tomcat启动后访问/pharmacy/login.jsp返回404原因IDEA未将webapp目录识别为Web资源根或web.xml中welcome-file-list未生效。解决右键src/main/webapp→Mark Directory as → Web Resources Root检查web.xml中是否有welcome-file-list welcome-filelogin.jsp/welcome-file /welcome-file-list若仍404在Tomcat日志中搜索Deploying web application directory确认应用上下文路径是否为/pharmacy非/或/ROOT4.2 现象登录时提示“用户名或密码错误”但数据库里明明有admin/123456原因UserDao.login()方法中SQL使用了LIKE模糊匹配且密码未加密存储但登录校验时却用了MD5比对。解决打开UserDao.java找到login(String username, String password)方法将原SQLString sql SELECT * FROM user WHERE username LIKE ? AND password MD5(?);改为String sql SELECT * FROM user WHERE username ? AND password ?;并确保数据库中password字段存的是明文123456非MD5哈希值4.3 现象点击“药品入库”保存后数据库stock字段为NULL原因Drug实体类中stock属性类型为Integer但表单传参是字符串Spring MVC未做类型转换。解决在Drug.java中stock字段上方添加NumberFormat(pattern 0) private Integer stock;或在DrugServlet中手动转换String stockStr request.getParameter(stock); drug.setStock(stockStr ! null !stockStr.trim().isEmpty() ? Integer.parseInt(stockStr) : 0);4.4 现象MySQL导入pharmacy.sql时报错Unknown character set: utf8mb4原因MySQL 5.7默认字符集配置未启用utf8mb4。解决编辑MySQL配置文件my.cnfLinux在/etc/my.cnfWindows在my.ini[client] default-character-set utf8mb4 [mysql] default-character-set utf8mb4 [mysqld] character-set-server utf8mb4 collation-server utf8mb4_unicode_ci重启MySQL服务sudo service mysql restart4.5 现象发药后库存扣减成功但刷新页面库存数没变原因浏览器缓存了drug/list.jsp页面未重新请求后端数据。解决在drug/list.jsp顶部添加% response.setHeader(Cache-Control, no-cache, no-store, must-revalidate); %或在DrugServlet.list()方法中request.setAttribute()后添加response.setDateHeader(Expires, 0);5. 进阶技巧三招让系统从“能用”升级为“可用”5.1 给药品列表加搜索过滤一行JS两行SQL搞定当前drug/list.jsp是全量加载数据一多就卡。加个实时搜索框只需改三处第一步在list.jsp表格上方加输入框div classsearch-box input typetext idsearchInput placeholder按药品名或规格搜索... onkeyupfilterTable() /div第二步加JS过滤逻辑放在script标签内function filterTable() { const input document.getElementById(searchInput); const filter input.value.toUpperCase(); const table document.getElementById(drugTable); const tr table.getElementsByTagName(tr); for (let i 1; i tr.length; i) { // 跳过表头 const td1 tr[i].getElementsByTagName(td)[1]; // 药品名列 const td2 tr[i].getElementsByTagName(td)[2]; // 规格列 const txtValue1 td1 ? td1.textContent || td1.innerText : ; const txtValue2 td2 ? td2.textContent || td2.innerText : ; if (txtValue1.toUpperCase().indexOf(filter) -1 || txtValue2.toUpperCase().indexOf(filter) -1) { tr[i].style.display ; } else { tr[i].style.display none; } } }第三步后端SQL优化可选大数据量时必做修改DrugDao.findAll()方法支持条件查询public ListDrug findAll(String keyword) throws SQLException { String sql SELECT * FROM drug WHERE status 1; ListObject params new ArrayList(); if (keyword ! null !keyword.trim().isEmpty()) { sql AND (name LIKE ? OR spec LIKE ?); params.add(% keyword.trim() %); params.add(% keyword.trim() %); } sql ORDER BY create_time DESC; return queryRunner.query(sql, new BeanListHandler(Drug.class), params.toArray()); }这样前后端结合小数据量用JS过滤快大数据量时在Servlet中传keyword参数调用新方法准。5.2 库存预警邮件通知用JavaMailSender发告警不依赖第三方API医院信息科需要每日收到过期预警邮件。Spring已集成JavaMail只需配置第一步在applicationContext.xml中配置MailSenderbean idmailSender classorg.springframework.mail.javamail.JavaMailSenderImpl property namehost valuesmtp.163.com/ property nameport value25/ property nameusername valueyour_email163.com/ property namepassword valueyour_authorization_code/ !-- 注意不是邮箱密码是163授权码 -- property namejavaMailProperties props prop keymail.transport.protocolsmtp/prop prop keymail.smtp.authtrue/prop prop keymail.smtp.starttls.enabletrue/prop /props /property /bean第二步在WarningService.checkExpiredDrugs()末尾加发送逻辑Autowired private JavaMailSender mailSender; public void checkExpiredDrugs() { // ... 原有预警逻辑 ... if (!expiring.isEmpty()) { String subject 【药品预警】 expiring.size() 种药品将于30天内过期; String content h3以下药品需重点关注/h3ul; for (Drug drug : expiring) { content li drug.getName() drug.getSpec() 到期日 new SimpleDateFormat(yyyy-MM-dd).format(drug.getExpireDate()) /li; } content /ul; SimpleMailMessage message new SimpleMailMessage(); message.setFrom(your_email163.com); message.setTo(pharmacyhospital.com); // 药房邮箱 message.setSubject(subject); message.setText(content); mailSender.send(message); } }提示163邮箱免费版单日限发500封够医院用若需更高频可换企业邮箱SMTP或阿里云邮件推送。5.3 导出药品清单为Excel用Apache POI拒绝前端JS-XLSX的兼容性翻车drug/list.jsp加个“导出Excel”按钮后端生成真实.xlsx文件第一步pom.xml加POI依赖dependency groupIdorg.apache.poi/groupId artifactIdpoi-ooxml/artifactId version4.1.2/version /dependency第二步DrugServlet.exportExcel()方法public void exportExcel(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ListDrug drugs drugService.findAll(null); // 获取全部药品 XSSFWorkbook workbook new XSSFWorkbook(); XSSFSheet sheet workbook.createSheet(药品清单); // 表头 String[] headers {ID, 药品名, 规格, 单位, 单价, 批次号, 有效期, 库存, 入库时间}; XSSFRow headerRow sheet.createRow(0); for (int i 0; i headers.length; i) { headerRow.createCell(i).setCellValue(headers[i]); } // 数据行 for (int i 0; i drugs.size(); i) { XSSFRow row sheet.createRow(i 1); Drug drug drugs.get(i); row.createCell(0).setCellValue(drug.getId()); row.createCell(1).setCellValue(drug.getName()); row.createCell(2).setCellValue(drug.getSpec()); row.createCell(3).setCellValue(drug.getUnit()); row.createCell(4).setCellValue(drug.getPrice()); row.createCell(5).setCellValue(drug.getBatchNo()); row.createCell(6).setCellValue( drug.getExpireDate() ! null ? new SimpleDateFormat(yyyy-MM-dd).format(drug.getExpireDate()) : ); row.createCell(7).setCellValue(drug.getStock()); row.createCell(8).setCellValue( drug.getCreateTime() ! null ? new SimpleDateFormat(yyyy-MM-dd HH:mm:ss).format(drug.getCreateTime()) : ); } // 设置响应头 response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setHeader(Content-Disposition, attachment; filenamepharmacy_export_ new SimpleDateFormat(yyyyMMdd_HHmmss).format(new Date()) .xlsx); workbook.write(response.getOutputStream()); workbook.close(); }第三步list.jsp加按钮a href${pageContext.request.contextPath}/DrugServlet?methodexportExcel classbtn btn-primary导出Excel/a这比前端JS生成Excel可靠得多——不依赖浏览器兼容性中文、日期、数字格式全保真且服务器端可加权限控制比如只允许管理员导出。我带过三届实训学生每次都有人卡在数据库导入或IDEA配置上浪费两天。后来我把这些坑写成 checklist 贴在实验室墙上学生照着打钩平均部署时间从8小时降到1.5小时。现在你手里的这个ZIP包不是玩具是能真正在小医院药房跑起来的最小闭环系统——它不炫技但每行代码都经得起药监检查。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

三合一收款码在线生成源码:PHP实现UA识别与二维码聚合支付

三合一收款码在线生成源码:PHP实现UA识别与二维码聚合支付

简介:一份面向商家与个人开发者的新款支付宝、微信、QQ三合一收款码在线生成源码,解决多平台收款码分散、收银流程繁琐的问题,无需后台与数据库,基于前端JavaScript即可在本地快速合成三平台二维码。压缩包为RAR格式,共…

📅 2026/10/7 15:38:17
C#仓库管理系统实战:WinForms+SQL Server进销存开发指南

C#仓库管理系统实战:WinForms+SQL Server进销存开发指南

简介:这是一套面向计算机专业本科生的C#毕业设计实战资源,聚焦仓库管理信息化痛点,提供从系统开发到论文撰写的完整参考方案。资源包含可运行的C#仓库管理系统源代码与配套设计论文文档,适用于课程设计、毕设开题与系统开发学习&a…

📅 2026/10/7 15:38:17
现代 JavaScript 教程实战:用 `Array.prototype.sort` 与比较函数对对象数组按年龄排序

现代 JavaScript 教程实战:用 `Array.prototype.sort` 与比较函数对对象数组按年龄排序

文档教程前端 【免费下载链接】zh.javascript.info 现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。…

📅 2026/10/7 15:33:17
MORE NEWS

更多资讯

📰

RoCE从原理到实践:无损网络配置与性能调优

这一篇能排到“三”,说明前面RDMA是什么、InfiniBand那套硬件生态的底子,前面都已铺垫得差不多了。这一篇我们专门把RoCE——RDMA over Converged Ethernet,也就是跑在以太网上的RDMA方案——掰开揉碎讲清楚。这两年做存储、AI训练集群、数据…

📰

Win11系统分盘全攻略:从磁盘管理到Diskpart,手把手教你安全分区

新买的Win11笔记本,开机一看,整个硬盘就一个C盘,512GB或者1TB全挤在一块儿。系统文件、软件、工作资料、电影游戏全堆在一起,想分个区出来放数据,又怕把系统搞坏。这事我帮亲戚朋友处理过很多次,自己也踩过…

📰

AI Agent实战:从对话到交付,掌握ReAct与Function Calling

很多朋友学完第一课和第二课之后,会有一种“AI也不过如此”的错觉——聊聊天、写写文案、生成几张图,好像已经摸到天花板了。但真正进入AI Agent(智能体)和AI编程实战的人,很快就发现事情没那么简单:模型经…

📰

Linux内核启动日志输出全阶段解析:从printk到串口终端的排查链路

我至今记得在客户现场盯着一台工控机串口终端的那种无力感:主板LED正常闪烁,风扇在转,但终端里一片寂静。折腾了大半天,最后发现机器其实早就活了,只是我和它之间缺了一条“看得见的通道”。从那以后我就养成一个习惯&…

📰

MCP协议:AI工程化中的轻量级模型上下文交互标准

1. MCP不是新概念,而是AI工程化落地的“协议层基建”最近在多个技术社区和开发群聊里,频繁看到MCP(Model Context Protocol)这个缩写被提起——不是作为某个具体工具、SDK或框架,而更像一种“共识性接口规范”的代称。…

📰

Flutter实战鸿蒙消防知识APP:跨平台开发全流程解析

1. 为什么会想在鸿蒙上做消防知识APP:选型不等于选框架先说个背景。我前两个月接了个需求:要做一个消防知识学习APP,目标平台是鸿蒙设备。需求方一开始的态度很明确——“既然都鸿蒙了,那肯定用ArkTS写呗,官方的DevEco…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬