SSM药房管理系统源码拆解:环境配置、数据库设计与避坑指南
简介一套基于SSM框架的药房管理系统毕业设计项目面向计算机相关专业学生以及需要SSM项目实战的Java开发者。项目经导师指导认可评审分为98分源码全部本地编译调试通过附带数据库脚本可直接导入运行。系统覆盖药品入库、库存预警、处方管理、供应商信息维护等典型药房业务前端采用JSP与EasyUI组合后端以Spring、SpringMVC、MyBatis分层实现适合作为毕业设计参照或课程设计进阶练习。资源包共1222个文件压缩后大小19.56MB其中包含48个Java源码文件、70个JSP页面、256个HTML页面、227个CSS样式、207个JS脚本以及SQL数据库脚本、XML配置文件、JAR依赖库等。静态页面、业务逻辑与数据表结构均有对应内容目录结构清晰便于分模块学习与二次开发。目前已有174人学习或下载下载即可获得可运行的完整工程、数据库初始化脚本及配置说明能够帮助快速跑通项目并理解SSM框架整合与药房业务流程。1. SSM 药房管理系统值得下载的 Java 毕业设计源码到底解决什么问题做 Java 毕设最怕的不是需求复杂而是系统看起来完整、实际跑不起来。这套基于 SSM 框架的药房管理系统属于典型的业务闭环型项目药品信息管理、供应商管理、入库出库、库存预警、销售记录都用同一个技术栈串了起来前端用 EasyUI 做后台界面数据库脚本直接导入 MySQL 就能出演示数据。评审分 98 分、源码本地编译可运行这些信息说明它不是纯理论拼凑而是按完整工程的标准交付的。适合两类人一类是正在做 Java 毕业设计、需要一个能讲清楚业务逻辑表结构框架配置三件事的完整项目另一类是刚学完 SSM、想找一个非教学 Demo 级别的实战源码来练手的人。这篇笔记会把我的拆解过程、环境配置、表结构逻辑和踩过的坑全部摊开讲。2. 先看懂底座SSM 整合、核心数据表和前端角色分工2.1 Spring、SpringMVC、MyBatis 在这个项目里各管什么SSM 是老牌 Java Web 组合但很多人只是背了概念没理解它在落地时的分工。你自己打开这份源码后会发现工程结构其实非常规整controller、service、dao、entity、mapper五个核心包挨个排好配置集中在resources下。Spring 负责的是对象管理也就是把Service层的类交给容器托管让Controller里可以直接注入不再自己new。SpringMVC 负责请求分发前端页面提交的 URL 会通过web.xml里配置的DispatcherServlet进入对应的Controller方法。MyBatis 负责 SQL 那一层每个dao接口对应一个mapper.xml里面的resultMap和select/insert/update标签直接定义了 Java 对象与数据库字段的映射关系。提示打开工程后先看web.xml里面会同时配置 Spring 的ContextLoaderListener和 SpringMVC 的DispatcherServlet这两个配置的加载顺序决定项目能不能正常启动。2.2 数据库脚本里的核心表结构药品、供应商、入库、出库这套系统的数据库脚本是打包下载里最关键的部分。按药房管理业务的常见设计脚本会包含这几类表表名常见命名职责关键字段sys_user系统用户区分管理员和操作员username、password、roledrug_info药品信息档案drug_name、specification、unit、price、stocksupplier供应商档案supplier_name、contact、phonedrug_inbound入库记录drug_id、quantity、unit_price、inbound_timedrug_outbound/sale_record出库/销售记录drug_id、quantity、sale_price、sale_time看这套表结构时我建议你重点盯drug_info和入库、出库两张关联表因为药房系统最核心的业务逻辑就是库存同步——药品表里的stock字段是一个冗余值实际库存应该等于所有入库数量减去所有出库数量这在答辩里是最好问、也最好答的点。2.3 前端用的 EasyUI 和 Bootstrap后台管理界面的实现思路源码包里出现了easyui.css、bootstrap.css、animate.css这些静态资源文件对应的是后台管理界面常见的组合方案。EasyUI 是做后台管理系统比较省事的组件库它的datagrid组件直接和 JSON 数据对接后端返回一个PageResult对象含total和rows前端就能自动渲染成带分页的表格。bootstrap.css在这里主要用来兜底样式animate.css是给页面切换加一点动效让演示时看起来更自然。这个组合说明项目的前后端交互方式是典型的页面差一个 JSON、后端差一个请求也就是 JSP 页面通过 jQuery$.ajax或者 EasyUI 封装的datagrid请求 URL后端返回 JSON。你在写开题报告和答辩 PPT 时基于 EasyUI 的快速开发后台界面是可以放在技术选型章节的观点。3. 把项目跑起来环境配置、导入步骤和数据库初始化3.1 版本清单JDK、MySQL、Tomcat、Maven 怎么选这套源码基于 SSM属于传统 Servlet 容器项目不是 Spring Boot所以环境版本不能乱配。我建议按下面的组合来装兼容性最好组件推荐版本备注JDK1.8这是 SSM 项目最稳妥的版本高版本 JDK 可能需要在编译参数里加--add-opensMySQL5.75.7 对utf8字符集和 SQL 模式的兼容最好Tomcat8.5支持 Servlet 3.1和 Spring 4/5 都匹配Maven3.6.x用于解析依赖如果源码里带了.classpathEclipse 项目标记也可以用 IDE 的导入功能直接解析提示如果你电脑上装的是 MySQL 8.0也可以跑但要把驱动换成mysql-connector-java8.x 版本、驱动类改为com.mysql.cj.jdbc.Driver还要在连接串里加上时区参数。这个细节在后面避坑章节我会详细说。3.2 导入 Eclipse 工程到 IDEA处理.classpath和依赖缺失源码包里有.classpath这个文件说明原开发环境大概率是 Eclipse 或 MyEclipse。如果你用的是 IntelliJ IDEA不要直接Open文件夹正确做法是File - New - Project from Existing Sources找到工程目录然后选择Eclipse导入方式IDEA 会识别.classpath中记录的源目录和输出目录。很多人在这一步翻车就是因为导入后项目结构识别错了。这里有一个判断技巧项目根目录下如果有.classpath和.project就按 Eclipse 工程导入如果只有pom.xml才按 Maven 工程直接打开。工程导入后先执行一次 MavenreimportIDEA 右侧 Maven 面板点刷新按钮等依赖下载完成再继续。如果项目里已经带了lib目录说明原项目可能不是纯 Maven 管理的这时候要检查WEB-INF/lib里是否有必需的 jar 包没有的话需要手动从 Maven 仓库补齐。3.3 初始化数据库用脚本建库、建表、灌入演示数据拿到数据库脚本后不要直接双击打开灌 SQL最好用命令行方式这样报错信息更清晰。假设脚本文件名为pharmacy.sql执行命令如下mysql -u root -p pharmacy.sql执行完之后登录 MySQL 确认结果mysql -u root -p show databases; use pharmacy; show tables; select count(*) from drug_info;执行逻辑说明mysql 命令会逐条执行脚本文件里的 SQL 语句脚本里通常包含CREATE DATABASE、CREATE TABLE和INSERT INTO三个部分。如果脚本里没有建库语句说明它默认是在pharmacy库内建表你需要先手动执行CREATE DATABASE pharmacy DEFAULT CHARACTER SET utf8;。参数说明-u root是用户名-p会提示输入密码。如果脚本文件很大可以加--default-character-setutf8参数强制客户端用 UTF-8 编码发送 SQL避免中文注释和中文数据写入时变成乱码。3.4 修改配置文件并启动到登录页数据库初始化完成后打开resources目录下的数据库连接配置文件常见名是jdbc.properties或db.properties把账号密码改成你本机的jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/pharmacy?useUnicodetruecharacterEncodingutf8 jdbc.usernameroot jdbc.password123456配置说明useUnicodetrue和characterEncodingutf8这两个参数必须保留否则后续页面上药品名称等中文会乱码。3306是 MySQL 默认端口如果你安装时改过端口这里要同步改。然后把项目部署到 TomcatIDEA 里配置 Tomcat 8.5Deployment选项卡添加war exploded或者war包Application context设置为/pharmacy启动后浏览器访问http://localhost:8080/pharmacy/就能看到登录页。4. 核心业务怎么落地的药品管理和入库出库的数据流转4.1 药品增删改查从 Controller 到 Mapper XML 的完整调用链药品管理是这套系统的门面功能。前端 EasyUI 的datagrid在加载时会向后端发送请求URL 类似/drug/list?page1rows10。后端DrugController接收参数后调用 Service 层Service 再调 DAO 接口由 MyBatis 的 Mapper 执行 SQL。代码逻辑大概是这样的Controller RequestMapping(/drug) public class DrugController { Autowired private DrugService drugService; // 分页查询药品列表返回 easyui datagrid 需要的 JSON 格式 RequestMapping(/list) ResponseBody public PageResult list(Drug drug, int page, int rows) { PageResult result new PageResult(); // 计算分页起始位置 int start (page - 1) * rows; result.setTotal(drugService.count(drug)); result.setRows(drugService.selectList(drug, start, rows)); return result; } // 新增或修改药品 RequestMapping(/save) ResponseBody public JsonResult save(Drug drug) { if (drug.getId() null) { drugService.insert(drug); } else { drugService.update(drug); } return JsonResult.success(); } }逻辑说明Autowired是 Spring 依赖注入ResponseBody会把PageResult对象序列化成 JSON 字符串写回前端。EasyUI 表格收到{total: 10, rows: [...]}后自动渲染分页列表这是这类系统最常见的返回格式。参数说明page是当前页码rows是每页条数这两个参数名是 EasyUI 默认发送的。如果你改前端表格组件记得后端这里的参数名也要对得上。4.2 库存出库逻辑数量扣减和事务处理的实现要点药房系统的出库操作比单纯的增删改查复杂一些它涉及两张表的数据变更先在sale_record表插入一条销售记录再更新drug_info表的stock字段。这两个操作必须在一个事务里完成否则会出现销售记录有了但库存没扣的问题。源码里的 Service 层一般会用 Spring 的事务注解Service public class SaleServiceImpl implements SaleService { Autowired private SaleMapper saleMapper; Autowired private DrugMapper drugMapper; Override Transactional(rollbackFor Exception.class) public void saleDrug(SaleRecord record) { // 1. 插入销售记录 saleMapper.insert(record); // 2. 扣减库存SQL 里会带上 stock quantity 的条件 int updated drugMapper.decreaseStock(record.getDrugId(), record.getQuantity()); if (updated 0) { // 库存不足时抛出异常事务回滚 throw new RuntimeException(库存不足); } } }逻辑说明decreaseStock对应的 Mapper SQL 通常是这样写的UPDATE drug_info SET stock stock - #{quantity} WHERE id #{drugId} AND stock #{quantity}。这里把库存是否足够的判断放到 SQL 的WHERE条件里而不是先查一次再判断是为了避免并发场景下两个请求同时读到同一个库存值然后一起扣减。参数说明Transactional默认只在抛出RuntimeException时回滚rollbackFor Exception.class是让所有受检异常也触发回滚。如果你在二次开发时新增了会抛出异常的操作记得保持这个约定不然会出现数据不一致。4.3 权限控制不同登录角色看到不同菜单的实现药房系统一般分管理员和普通操作员两个角色。源码里最可能采用的是登录后 Session 存角色信息 拦截器拦请求的方案。LoginInterceptor会检查未登录的请求而菜单的显示则根据角色字段在前端 JSP 页面里做判断。mvc:interceptors mvc:interceptor mvc:mapping path/**/ mvc:exclude-mapping path/login/ mvc:exclude-mapping path/css/**/ mvc:exclude-mapping path/js/**/ mvc:exclude-mapping path/images/**/ bean classcom.pharmacy.interceptor.LoginInterceptor/ /mvc:interceptor /mvc:interceptors配置说明mvc:exclude-mapping是白名单登录页和静态资源不拦。其他所有路径都要经过LoginInterceptor这个 Java 类里做的事情非常简单从session里取loginUser如果不存在就response.sendRedirect(/pharmacy/login)。静态资源放行是一个容易忽略的点如果没配CSS 和 JS 文件会被拦截器挡掉页面变成无样式状态。5. 避坑指南从导入源码到答辩演示的 5 个高频问题5.1 Tomcat 启动报ClassNotFoundException: com.mysql.jdbc.Driver现象启动 Tomcat 时后台日志直接报找不到驱动类或者页面访问数据库时报同样的错。原因数据库连接是由 Tomcat 或 Web 应用加载驱动类的如果mysql-connector-java这个 jar 包没有打到WEB-INF/lib目录下Eclipse 项目常见Tomcat 运行时根本找不到这个类。还有一种情况是 MySQL 8 环境下驱动类名变成com.mysql.cj.jdbc.Driver而你用的还是老名字。解决打开项目结构确认WEB-INF/lib下存在mysql-connector-java-x.x.x.jar如果不存在在 Maven 依赖里找到对应 jar 后手动复制进去。同时核对你的 MySQL 版本如果数据库是 8.x把jdbc.properties里的驱动类改成com.mysql.cj.jdbc.Driver并给 URL 加上serverTimezoneAsia/Shanghai。5.2 页面中文显示乱码药品名称全是???现象数据库表和页面上显示的中文全部变成问号或者登录用户名显示乱码。原因乱码有三个独立链路任何一个断了都会出问题第一MySQL 表结构和连接的字符集不是utf8第二JSP 页面没有声明pageEncodingUTF-8第三SpringMVC 的CharacterEncodingFilter没配置POST 请求的中文参数没有被正确解码。解决按顺序检查三层。先看jdbc.properties里有没有characterEncodingutf8再查表结构SHOW CREATE TABLE drug_info;如果DEFAULT CHARSET不是utf8用ALTER TABLE drug_info CONVERT TO CHARACTER SET utf8;转换最后看web.xml里是否配置了CharacterEncodingFilter且forceEncodingtrue。最容易被忽视的是第三个因为 GET 请求乱码偶尔被浏览器掩盖但 POST 提交时一定暴露。5.3 Tomcat 启动正常但访问页面报 404而登录页单独访问没问题现象输入http://localhost:8080/pharmacy/后返回 404但直接访问/pharmacy/login.jsp又能打开。原因这说明项目部署的上下文路径和你访问的路径不一致。如果Application context配的是/pharmacy但页面里的链接写的是绝对路径/login跳转时会到http://localhost:8080/login自然 404。解决在 IDEA 的 Tomcat 配置里确认Deployment选项卡下面的Application context到底是什么然后在浏览器访问时用完整路径http://localhost:8080/项目上下文/login。如果源码里使用了%request.getContextPath()%拼接路径就不会有这个问题没有的话二次开发时建议统一改成这种写法。5.4 登录后页面样式全部丢失表格错乱现象登录成功进入主界面页面 CSS 和 JS 全是裸奔状态表格堆在一起没有任何样式。原因页面里引入的easyui.css、bootstrap.css用的是绝对路径比如/static/css/easyui.css而项目上下文路径不是根路径导致浏览器实际请求的是http://localhost:8080/static/...资源找不到。解决把 JSP 页面里的静态资源引用改成使用 EL 表达式拼路径link href${pageContext.request.contextPath}/static/css/easyui.css。用全局搜索在源码里找出所有以href/或src/开头的引用统一改成上面这种写法然后重启 Tomcat 就好了。5.5 数据库脚本导入时外键约束报错建表失败现象执行mysql -u root -p pharmacy.sql时报Cannot add foreign key constraint的错误。原因脚本里表之间的外键依赖顺序不对比如sale_record表的外键引用了drug_info但drug_info还没创建出来。也可能是两张表的引擎不一致一个用InnoDB、一个用MyISAM外键只能在InnoDB上生效。解决不要直接放弃先看报错语句是哪一条找到依赖的表名。如果脚本顺序是乱的最简单的办法是手工调整把报错的那张表拆到单独的文件执行。也可以用SET FOREIGN_KEY_CHECKS0;放在脚本开头暂时关闭外键检查导入完成后再恢复为1。注意后者只是权宜之计如果你后续要正常使用外键约束还是建议把建表顺序理清楚。6. 二次开发与验证把系统变成真正能答辩的项目6.1 增加药品批次概念一张表补全库存管理的业务深度原系统的库存管理是简单的一个药品对应一个库存数这在答辩时容易被抓住短板。我建议你加一个批次管理字段让库存追踪到批号这在真实药房是有业务含义的——同一种药不同批次效期不同售价相同但进价可能不同。CREATE TABLE drug_batch ( id INT PRIMARY KEY AUTO_INCREMENT, drug_id INT NOT NULL, batch_no VARCHAR(50) NOT NULL COMMENT 批次号, expire_date DATE NOT NULL COMMENT 效期, purchase_price DECIMAL(10,2) NOT NULL COMMENT 批次进价, quantity INT NOT NULL COMMENT 本批次剩余数量, FOREIGN KEY (drug_id) REFERENCES drug_info(id) );逻辑说明有了批次表出库时可以按先过期先出FEFO的原则来选择扣减哪个批次的库存这是药房管理系统的标准做法也是答辩时很出彩的业务细节。加表之后SaleServiceImpl的执行逻辑就变成插入销售记录、按expire_date正序查出批次、扣减批次数量、同步扣减drug_info.stock。6.2 用 SQL 验证库存一致性让数据替你说话演示时不要只靠点按钮你可以准备一条 SQL 现场验证系统的数据一致性。这个动作很能体现工程素养SELECT d.id, d.drug_name, d.stock AS current_stock, (SELECT COALESCE(SUM(quantity), 0) FROM drug_inbound WHERE drug_id d.id) - (SELECT COALESCE(SUM(quantity), 0) FROM sale_record WHERE drug_id d.id) AS calc_stock FROM drug_info d HAVING current_stock ! calc_stock;说明这条 SQL 把系统库存和出入库流水算出来的库存对账如果你加了批次表可以再把批次数量加进来(SELECT COALESCE(SUM(quantity),0) FROM drug_batch WHERE drug_id d.id) AS batch_stock。如果查询结果为空说明数据一致有记录则说明某个环节丢了事务正好可以引出你做的Transactional设计。用 JMeter 对登录接口做一次简单的压测也可以作为加分项50 个线程并发请求登录观察响应时间和错误率把截图放进论文的系统性能测试章节即可。这套项目我从拿到到跑通踩得最多的坑就是环境版本不匹配和路径问题——jar 包版本、上下文路径、字符集这三个点占了八成时间。从那以后我每次部署陌生的 SSM 项目都强制先看一遍web.xml、jdbc.properties和pom.xml三个文件再动手这个习惯帮我省了大量排错时间。希望这些拆解和避坑记录能让你少走这段弯路顺利把项目跑起来、讲明白。本文还有配套的精品资源点击获取