☰
Javaweb药店管理系统源码解析:环境配置与CRUD闭环实战
2026/10/8 6:02:40 网站建设 项目流程

简介:这是一套基于Java Web技术栈的药店管理系统完整项目,包含可运行源码与配套数据库,面向Java Web初学者、课程设计学生及需要实战练手的开发者,帮助理解Servlet、JSP、JSTL与MVC分层开发在真实业务中的应用。系统覆盖用户管理、药品管理、销售管理、库存管理、报表统计与权限控制等模块,数据库设计涉及药品表、用户表、订单表等核心结构,并包含库存预警、销售报表生成等实用功能,可作为课程设计或毕业设计的参考方案。资源包共2493个文件,以js脚本、png与gif图片、css样式、jsp页面、java源码、class文件及jar依赖为主,另含sql建库脚本与xml配置,压缩包约22.51MB,目录按MVC结构组织,便于按模块查阅与二次开发。目前已有882人学习下载,适合希望系统掌握Java Web开发流程、积累项目经验的读者参考借鉴。

1. 药店管理系统源码拆包:从一堆 JSP 到能跑起来的 CRUD 闭环

拿到「基于 Javaweb 的药店管理系统及源码和数据库」这个包时,我第一反应不是打开 IDE,而是先看目录结构——因为 Javaweb 老项目的坑,八成埋在路径和编码里。这套源码是典型的 Servlet + JSP + MySQL 组合,带完整 SQL 脚本,覆盖药品、库存、销售、用户四个核心模块,适合课程设计、毕设起步,也适合想补一遍原生 Javaweb 增删改查闭环的开发者。它不依赖 Spring 全家桶,纯手写 JDBC 和 DAO,对理解 MVC 分层比看框架源码更直观。但正因为它「老」,Tomcat 版本、JDBC 驱动、字符集这三处不先对齐,导入后大概率是白屏或 500。下面按我实际跑通的顺序拆。

2. 环境对齐:JDK、Tomcat 与 MySQL 驱动的版本咬合

2.1 为什么这套源码对版本敏感

原生 Javaweb 项目没有 Maven 或 Gradle 帮你仲裁依赖,WEB-INF/lib下放什么版本的 jar,运行时就用什么。常见翻车场景是:JDK 17 编译、Tomcat 10 部署,结果javax.servlet包名在 Tomcat 10 里已经改成jakarta.servlet,所有 Servlet 直接报ClassNotFoundException。这套药店系统的源码用的是javax.servlet.*,所以 Tomcat 必须停在 9.x 及以下。MySQL 驱动同理,com.mysql.jdbc.Driver是 5.x 的写法,8.x 要换成com.mysql.cj.jdbc.Driver并加时区参数。我一般会先做一张版本对照表,再动手导入。

组件推荐版本原因
JDK1.8源码语法级别低,高版本编译易出--release警告
Tomcat9.0.x保留javax.servlet命名空间
MySQL5.7 或 8.08.0 需改驱动类名和 URL 参数
mysql-connector-java5.1.49 或 8.0.28与 MySQL 版本对应
IDEIDEA 2021+对 Web Facet 配置友好

2.2 IDEA 导入与运行配置的实操步骤

IDEA 导入老 Web 项目,最容易卡在「没有 Web Facet」和「Artifact 没配」。下面是我跑通这套源码的完整配置流程,按顺序执行即可。

# 第一步:确认 JDK 和 Tomcat 已安装 java -version # 输出应为 1.8.x # 第二步:把 mysql-connector-java 放入 WEB-INF/lib # 如果源码包内已有,跳过;没有则手动放入 ls WebContent/WEB-INF/lib/ # 应看到 mysql-connector-java-5.1.49.jar
IDEA 内配置顺序: 1. File -> Project Structure -> Project,SDK 选 1.8,语言级别选 8 2. Modules -> 选中模块 -> Sources,标记 src 为 Sources 3. Facets -> 添加 Web,Web Resource Directory 指向 WebContent 4. Artifacts -> 添加 Web Application: Exploded,输出目录默认 5. Run -> Edit Configurations -> 添加 Tomcat Local 6. Deployment 选项卡 -> 添加 Artifact -> 应用

上面这段配置里,第 3 步的 Web Resource Directory 必须指向WebContent而不是项目根目录,否则 JSP 找不到。第 4 步的 Artifact 如果显示红色叹号,说明WEB-INF/lib下有 jar 没被识别,右键Add as Library即可。第 6 步部署后,Application context 建议设为/pharmacy,和源码里web.xml的url-pattern保持一致,避免路径 404。

2.3 数据库脚本导入与连接参数修改

源码包里的.sql文件通常包含建库、建表、初始数据三部分。导入前先确认脚本里的库名,再改 Java 侧的连接配置。

-- 常见脚本开头,先建库再建表 CREATE DATABASE IF NOT EXISTS pharmacy DEFAULT CHARACTER SET utf8mb4; USE pharmacy; -- 药品表结构示例(字段名以实际脚本为准) CREATE TABLE medicine ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, spec VARCHAR(50), price DECIMAL(10,2), stock INT DEFAULT 0, warn_stock INT DEFAULT 10 );
// DBUtil.java 或类似工具类中的连接配置 private static final String URL = "jdbc:mysql://localhost:3306/pharmacy?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai"; private static final String USER = "root"; private static final String PASSWORD = "你的密码"; // MySQL 8.x 驱动类名改为 com.mysql.cj.jdbc.Driver

SQL 脚本执行时,注意utf8mb4而不是utf8,否则药品名里的生僻字会变问号。Java 侧 URL 里的characterEncoding=utf8要和数据库字符集对齐,serverTimezone在 MySQL 8 下不加会报时区异常。如果导入脚本报「Unknown collation: utf8mb4_0900_ai_ci」,说明脚本是 MySQL 8 导出的,而你本地是 5.7,把该排序规则改成utf8mb4_general_ci再执行。

3. 核心模块拆解:用户、药品、库存、销售四条链路怎么串

3.1 MVC 分层在源码里的实际落点

这套系统没有用框架,但目录结构是标准 MVC。src下通常分dao、service、servlet、entity四层,WebContent下放 JSP 和静态资源。理解这个分层,改功能时才知道该动哪个文件。

src/ com.pharmacy.entity/ -> 实体类,和数据库表字段一一对应 com.pharmacy.dao/ -> JDBC 增删改查,SQL 写在这里 com.pharmacy.service/ -> 业务逻辑,如库存扣减、库存预警判断 com.pharmacy.servlet/ -> 接收请求,调 service,转发 JSP WebContent/ jsp/ -> 页面 WEB-INF/web.xml -> Servlet 映射 WEB-INF/lib/ -> 驱动 jar

实体类里字段名和表字段的对应关系,是排查「查出来是 null」的第一现场。比如表字段warn_stock,实体类里如果是warnStock,那 DAO 里rs.getInt("warn_stock")必须写对,否则默认返回 0,库存预警永远不触发。我见过有人把price定义成int,结果 12.50 存进去变 12,金额对不上,这种就是实体类型和表类型没对齐。

3.2 药品管理与库存预警的代码链路

药品管理是这套系统的核心,涉及录入、查询、更新、删除,以及库存低于阈值时的提醒。下面以「库存扣减 + 预警判断」为例,看一条完整链路。

// MedicineService.java 中的库存扣减方法 public boolean reduceStock(int medicineId, int quantity) { Medicine m = medicineDao.findById(medicineId); if (m == null || m.getStock() < quantity) { return false; // 库存不足,直接拦截 } int newStock = m.getStock() - quantity; boolean ok = medicineDao.updateStock(medicineId, newStock); if (ok && newStock <= m.getWarnStock()) { // 触发预警,常见做法是写一条预警记录或置标志位 warnDao.insert(new Warn(medicineId, newStock)); } return ok; }

这段逻辑里,findById先查当前库存,避免并发下超卖;updateStock更新后再判断是否低于warnStock。参数quantity来自销售模块的订单明细,warnStock是药品表里的预警阈值。常见坑是:扣减和预警判断不在同一事务里,扣减成功但预警写入失败,导致库存低了却没提醒。原生 JDBC 下,我一般会在 service 方法上加手动事务,setAutoCommit(false),两步都成功再commit。

3.3 销售管理与报表统计的数据来源

销售模块生成订单,订单明细关联药品,统计报表按日期聚合。这套源码的报表通常是 JSP 里嵌 Java 代码直接查,或者 Servlet 查完放 request 再转发。

-- 每日销售额统计,按订单日期分组 SELECT DATE(order_time) AS day, SUM(total_price) AS amount FROM orders WHERE order_time BETWEEN ? AND ? GROUP BY DATE(order_time) ORDER BY day DESC;
// ReportServlet.java 中调用统计 String start = request.getParameter("start"); String end = request.getParameter("end"); List<Report> list = reportDao.statByDate(start, end); request.setAttribute("reportList", list); request.getRequestDispatcher("/jsp/report.jsp").forward(request, response);

SQL 里DATE(order_time)把时间戳截断到天,SUM(total_price)聚合当天金额。参数start和end从页面表单传入,格式要和数据库时间字段匹配,常见做法是yyyy-MM-dd。如果报表查出来是空,先看order_time存的是datetime还是varchar,后者会导致BETWEEN比较按字符串走,跨月时结果错乱。

4. 避坑与排查:导入后跑不起来的五类高频问题

4.1 现象:访问任何页面都 404

原因:IDEA 的 Artifact 没配置,或者 Application context 和web.xml里的路径不一致。解决:检查 Run Configuration 的 Deployment 选项卡,确认 Artifact 已添加;再看web.xml里url-pattern的前缀,把 Application context 改成一致,或者访问时手动加上前缀。

4.2 现象:JSP 页面中文乱码

原因:JSP 文件本身编码、pageEncoding、响应头字符集、数据库字符集四处不一致。解决:JSP 首行统一写<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>;Servlet 里response.setContentType("text/html;charset=UTF-8");数据库连接 URL 加characterEncoding=utf8;建库用utf8mb4。四处对齐后乱码基本消失。

4.3 现象:登录后跳转空白页,控制台无报错

原因:Servlet 里forward的路径写错,或者 JSP 放在WEB-INF下被直接访问限制。解决:WEB-INF下的 JSP 只能通过 Servlet 转发访问,检查getRequestDispatcher的路径是否以/开头,且相对于 Web 根目录。常见错误是写成jsp/main.jsp少了前导斜杠。

4.4 现象:MySQL 连接报「Public Key Retrieval is not allowed」

原因:MySQL 8 默认认证插件是caching_sha2_password,旧驱动或未加参数时拒绝连接。解决:URL 后加allowPublicKeyRetrieval=true&useSSL=false,或者把用户认证插件改回mysql_native_password。生产环境不建议关 SSL,本地调试可临时用。

4.5 现象:库存扣减后出现负数

原因:并发请求下,两个线程同时查到相同库存,各自扣减后写回,导致超卖。解决:在 SQL 层用条件更新,UPDATE medicine SET stock = stock - ? WHERE id = ? AND stock >= ?,根据返回的影响行数判断是否成功,而不是先查再改。这是原生 JDBC 下最省事的防超卖手段。

5. 二次开发与验证:把课程设计改成能写进简历的项目

5.1 从「能跑」到「能讲」的验证清单

跑通只是第一步,要让它变成能写进简历的项目,得能说清每条链路。我一般会按下面这张清单逐项验证,每通过一项,就在 README 里记一条。

验证项操作预期结果
用户登录用管理员账号登录跳转管理首页,普通用户看不到管理菜单
药品录入新增一条药品,库存设 5,预警设 10列表可见,库存显示 5
库存预警销售该药品 1 件库存变 4,预警列表出现记录
销售报表选日期范围查询显示当天销售额汇总
权限拦截普通用户直接访问管理 URL被拦截或跳转登录

5.2 三个低成本进阶方向

第一个方向是加过滤器做统一登录校验。原生 Javaweb 用Filter拦截/admin/*,判断 session 里有没有用户信息,没有就跳登录页。这样不用在每个 Servlet 里重复写校验。

public class AuthFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; Object user = request.getSession().getAttribute("user"); if (user == null) { response.sendRedirect(request.getContextPath() + "/login.jsp"); return; } chain.doFilter(req, res); } }

第二个方向是把 JDBC 连接改成连接池,比如 Druid 或 C3P0,减少频繁建连开销。第三个方向是给报表加导出 Excel,用 POI 把查询结果写成.xlsx,面试时能讲出「从查询到导出」的完整链路。这三个方向都不需要引入 Spring,保持原生栈,改动量可控。

5.3 我踩过的那次「库存对不上」

有次帮人调这套系统,销售扣了库存,但报表里的销售额和库存变动对不上。查了半天发现,订单主表和明细表插入不在同一事务,主表成功、明细失败,导致有订单没明细,库存没扣但销售额记了。从那以后我每次碰原生 JDBC 的多表写入,都强制先确认setAutoCommit(false)有没有加,再走一遍异常回滚测试。这套源码的价值不在于代码多优雅,而在于它把 Javaweb 的完整链路摊开给你看,坑踩一遍,比看十篇教程都记得牢。希望帮到你。

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

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

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

立即咨询