ARTICLE DETAIL

资讯详情

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

JavaWeb原生项目实战:JSP+Servlet+JDBC药店管理系统详解

JavaWeb原生项目实战:JSP+Servlet+JDBC药店管理系统详解 简介这是一套面向计算机专业本科生的JavaWeb毕业设计实战资源聚焦药店业务场景涵盖药品进销存、员工管理、订单处理与用户权限控制等核心功能模块适合课程设计、毕设开发及JavaWeb技术栈入门者快速上手。资源包共943个文件包含150个Java源码如Medicine、Orders、Admin等实体与控制器类、188个XML配置文件Spring/SpringMVC/MyBatis整合相关、83个JS与79个Vue前端交互脚本、63个CSS样式文件以及完整SQL数据库脚本和运行所需静态资源整体体积128.27MB结构规范、分层清晰。已有1408人学习下载代码经实测可直接部署运行无需额外修改配套数据库已初始化基础数据小白开发者亦能通过阅读Controller与Service层逻辑快速理解MVC架构实践路径并基于现有模块拓展报表统计或微信对接等功能。1. 为什么这个“药店管理系统”毕业设计源码比你改了三天还跑不起来的 Spring Boot 项目更值得细抠你是不是也经历过在 GitHub 上搜“JavaWeb 毕业设计”下载了十几个 zip 包解压后发现——web.xml里 servlet 映射路径写错了、jdbc.properties里 MySQL 用户名还是root、lib文件夹缺servlet-api.jar、Tomcat 启动报ClassNotFoundException: javax.servlet.http.HttpServlet……最后只能删掉重下循环到答辩前一周。这个标题为“基于JavaWeb的药店管理系统源码数据库毕业设计.zip”的资源不是某个网红教程的配套代码而是一套完整闭环、可本地秒启、带真实业务逻辑的 JSP Servlet JDBC 原生栈落地方案。它不炫技、不堆框架但恰恰卡在毕业设计最硬的几个需求点上有登录鉴权、有药品进销存核心流程、有库存预警逻辑、有管理员/店员双角色权限隔离、数据库脚本可一键执行、所有 JSP 页面含基础校验和跳转控制。它适合两类人一是大三刚学完《Java Web 编程》想拿个稳过项目的同学二是想反向吃透“Servlet 生命周期怎么影响页面跳转”“JDBC 手动事务怎么防超卖”的初阶开发者——因为它的每一行代码都暴露在 Tomcat 日志和浏览器 Network 面板之下没有黑匣子。别被“毕业设计”四个字劝退。这套系统里藏着的是 JavaWeb 最原始却最扎实的肌肉记忆如何用RequestDispatcher做服务端跳转而不暴露 URL、为什么HttpSession要手动invalidate()、PreparedStatement的?占位符怎么避免 SQL 注入、web.xml中load-on-startup为 1 的 Servlet 怎么预加载药品缓存……这些在 Spring Boot 自动装配的迷雾里反而越来越难摸到。2. 从解压到首页显示5 分钟跑通原生 JavaWeb 项目的最小必要步骤提示本节所有操作均基于 JDK 8 Tomcat 8.5 MySQL 5.7 环境。若你用的是 JDK 11 或 Tomcat 10请先跳到第 4 章「避坑指南」看兼容性处理。2.1 解压后第一眼要确认的三个关键目录结构拿到.zip文件后不要急着导入 IDEA。先用任意解压工具打开重点检查以下三处是否齐全/WebContent/WEB-INF/web.xml必须存在且servlet-class标签内类名需与实际包路径一致常见错误是写成com.servlet.LoginServlet但实际类在servlet包下/WebContent/WEB-INF/lib/必须包含mysql-connector-java-5.1.47.jar或类似版本缺则数据库连不上若看到servlet-api.jar说明作者已打包好运行时依赖可跳过手动添加/src/目录下必须有com.entity、com.dao、com.servlet三级包结构注意是com.开头不是cn.或org.如果发现/src是空的或.java文件全在根目录下——说明该压缩包是编译后的classes文件夹误打包立刻放弃。真正的源码包必须能直接在 IDEA 中右键Add Framework Support → Web Application成功识别。2.2 数据库初始化用命令行比图形化工具更可控很多同学卡在“页面显示空白”其实只是数据库没建好。别急着开 Navicat用终端执行更可靠# 1. 登录 MySQL假设用户名 root密码 123456 mysql -u root -p # 2. 创建数据库注意字符集必须是 utf8mb4否则中文药品名会乱码 CREATE DATABASE pharmacy_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 3. 切换到该库 USE pharmacy_db; # 4. 执行 SQL 脚本路径替换成你本地解压后的绝对路径 SOURCE /Users/yourname/Downloads/pharmacy_system/sql/pharmacy_init.sql;逻辑说明pharmacy_init.sql通常位于/sql/或/database/子目录下文件内应包含CREATE TABLE drug_info、CREATE TABLE user_info、INSERT INTO user_info VALUES (admin, e10adc3949ba59abbe56e057f20f883e, 管理员);等语句。其中密码e10adc3949ba59abbe56e057f20f883e是123456的 MD5 值这是该系统默认管理员账号务必记牢。2.3 IDEA 中配置 Tomcat 并部署避开 classpath 错误的核心三步很多人导入后点 Run 报java.lang.NoClassDefFoundError: javax/servlet/Servlet本质是 IDEA 没把 Tomcat 的servlet-api.jar加入编译路径。按以下顺序操作File → Project Structure → Modules → Dependencies点击→JARs or directories→ 选择你本地 Tomcat 安装目录下的lib/servlet-api.jar路径如/opt/tomcat/lib/servlet-api.jarScope 选Provided重要不能选 CompileFile → Project Structure → Artifacts点击→Web Application: Archive→From modules...→ 选中你的模块 → OK在右侧Output Directory旁点击小齿轮 →Show all files→ 确保WEB-INF/lib/下已包含mysql-connector-java-*.jarRun → Edit Configurations → Tomcat Server → Deployment点击→Artifact→ 选择刚创建的xxx:war exploded关键设置Application context 必须填/pharmacy或查看web.xml中display-name值保持一致否则访问http://localhost:8080/会 404完成上述三步后点击绿色三角形启动。若控制台输出INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [xxx] ms且浏览器打开http://localhost:8080/pharmacy/login.jsp能显示登录框则第一步成功。3. 从登录到库存预警读懂核心业务链路的三段关键代码这套系统的价值不在“能跑”而在“每一步都可调试、可修改”。下面带你精读三个最具教学价值的环节登录鉴权如何防止绕过、药品入库如何保证库存原子性、销售出库如何触发预警。3.1 登录 Servlet为什么request.getSession().setAttribute(user, user)后还要response.sendRedirect()打开/src/com/servlet/LoginServlet.java找到doPost方法末尾// 关键代码段 if (user ! null) { request.getSession().setAttribute(user, user); // ① 将用户对象存入 session response.sendRedirect(request.getContextPath() /main.jsp); // ② 强制重定向 } else { request.setAttribute(msg, 用户名或密码错误); request.getRequestDispatcher(/login.jsp).forward(request, response); // ③ 服务端转发 }参数说明与逻辑说明①setAttribute(user, user)是将登录成功的User对象绑定到当前 session后续所有请求可通过request.getSession().getAttribute(user)获取。注意session默认有效期 30 分钟若需延长需在web.xml中添加session-configsession-timeout60/session-timeout/session-config②sendRedirect()是客户端跳转URL 地址栏会变成/main.jsp且新请求不携带原 request 中的 attribute所以不能用request.setAttribute()传值。这是防止用户 F5 刷新时重复提交表单的关键手段。③forward()是服务端内部跳转URL 不变且request中的msg属性仍有效因此能在 login.jsp 中用% request.getAttribute(msg) %输出错误提示。血泪经验曾有同学把第②步改成request.getRequestDispatcher(...).forward(...)结果登录后 URL 还是/login一刷新就重新提交导致后台日志刷屏。记住口诀“登录成功用 redirect登录失败用 forward”。3.2 入库操作JDBC 手动事务如何避免“钱扣了但货没加”药品入库功能通常在/src/com/servlet/DrugInServlet.java中。重点看事务控制部分Connection conn null; PreparedStatement ps1 null; PreparedStatement ps2 null; try { conn JDBCUtil.getConnection(); // 自定义工具类返回连接 conn.setAutoCommit(false); // ① 关闭自动提交开启事务 // 更新药品库存UPDATE String sql1 UPDATE drug_info SET stock stock ? WHERE id ?; ps1 conn.prepareStatement(sql1); ps1.setInt(1, inCount); // 入库数量 ps1.setInt(2, drugId); ps1.executeUpdate(); // 插入入库记录INSERT String sql2 INSERT INTO drug_in_log(drug_id, in_count, operator, in_time) VALUES (?, ?, ?, NOW()); ps2 conn.prepareStatement(sql2); ps2.setInt(1, drugId); ps2.setInt(2, inCount); ps2.setString(3, operator); ps2.executeUpdate(); conn.commit(); // ② 两步都成功才提交 } catch (SQLException e) { if (conn ! null) { try { conn.rollback(); // ③ 任一步失败则回滚 } catch (SQLException ex) { ex.printStackTrace(); } } request.setAttribute(msg, 入库失败 e.getMessage()); request.getRequestDispatcher(/error.jsp).forward(request, response); }逻辑说明若不用事务可能出现UPDATE成功但INSERT因网络中断失败导致库存虚高却无记录可查。此处通过setAutoCommit(false)commit()/rollback()显式控制确保“要么全成功要么全不生效”。注意JDBCUtil.getConnection()内部必须使用DriverManager.getConnection(url, user, pwd)不能用连接池如 DBCP否则conn.rollback()可能无效——这是原生 JDBC 项目与 Spring 事务的本质区别。3.3 库存预警一个被忽略的WHERE条件如何让预警失效预警功能常放在main.jsp的顶部 banner 或独立页面/warn.jsp。其 SQL 很简单SELECT * FROM drug_info WHERE stock warn_level AND status 上架;但问题在于warn_level字段在数据库中是INT类型而部分版本的pharmacy_init.sql初始化时给drug_info表插入的warn_level值为NULL。此时stock NULL永远为false预警永远不触发。解决方法在查询前加IS NOT NULL判断String sql SELECT * FROM drug_info WHERE stock warn_level AND warn_level IS NOT NULL AND status 上架;这个细节暴露出原生开发中最容易被忽视的点数据库约束缺失导致业务逻辑崩塌。Spring Boot 项目会用NotNull注解强制校验而这里只能靠人工补 SQL 条件。这也是为什么建议你在drug_info表上加一条约束ALTER TABLE drug_info MODIFY COLUMN warn_level INT NOT NULL DEFAULT 10;4. 避坑指南那些让你在答辩前夜崩溃的 4 个高频问题4.1 现象页面中文显示为 “???”数据库里却是正常中文原因MySQL 连接 URL 缺少useUnicodetruecharacterEncodingutf8mb4参数或 Tomcat 的server.xml中Connector未配置URIEncodingUTF-8解决修改jdbc.propertiesurljdbc:mysql://localhost:3306/pharmacy_db?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/Shanghai修改 Tomcatconf/server.xml找到Connector port8080 ... /添加URIEncodingUTF-8属性4.2 现象登录后点击“退出”返回空白页F12 看 Network 显示 302 但没跳转原因LogoutServlet中调用了session.invalidate()但之后未做任何跳转导致响应体为空解决在session.invalidate()后必须加response.sendRedirect(request.getContextPath() /login.jsp);4.3 现象IDEA 控制台报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver原因MySQL 8 驱动类名从com.mysql.jdbc.Driver改为com.mysql.cj.jdbc.Driver但源码中仍写旧类名解决方案 A推荐降级 MySQL 驱动为mysql-connector-java-5.1.47.jar兼容 MySQL 5.7/8.0方案 B修改JDBCUtil.java中Class.forName(com.mysql.cj.jdbc.Driver)并确保url中含serverTimezoneAsia/Shanghai4.4 现象新增药品时提示 “Duplicate entry ‘xxx’ for key ‘PRIMARY’”但数据库明明没这条记录原因drug_info表主键id是INT类型且设为AUTO_INCREMENT但插入时手动指定了id值如INSERT INTO drug_info(id, name, ...) VALUES (1, 阿莫西林, ...)而下一次自增仍从 1 开始导致冲突解决插入时永远不要指定id字段写成INSERT INTO drug_info(name, spec, unit, price, stock, warn_level, status) VALUES (?, ?, ?, ?, ?, ?, ?)若必须重置自增起点执行ALTER TABLE drug_info AUTO_INCREMENT 100;设为当前最大 id15. 进阶技巧把毕业设计升级为可演示的“半生产级”系统做到能跑通、能登录、能查库存只完成了 60%。真正让答辩老师眼前一亮的是那些“看起来很专业、实现却只要改三行”的细节优化。下面这几个动作我带过 12 届毕设学生90% 都用上了。5.1 给登录页加验证码30 行代码防暴力破解原系统通常无验证码易被脚本爆破。用kaptcha实现最轻量在pom.xml若有或WEB-INF/lib/中加入kaptcha-2.3.2.jar在web.xml中配置 servletservlet servlet-nameKaptcha/servlet-name servlet-classcom.google.code.kaptcha.servlet.KaptchaServlet/servlet-class /servlet servlet-mapping servlet-nameKaptcha/servlet-name url-pattern/kaptcha.jpg/url-pattern /servlet-mapping在login.jsp中添加img src${pageContext.request.contextPath}/kaptcha.jpg onclickthis.src${pageContext.request.contextPath}/kaptcha.jpg?dnew Date().getTime() alt验证码 / input typetext namecaptcha placeholder请输入验证码 /在LoginServlet.java的验证逻辑中加String inputCaptcha request.getParameter(captcha); String realCaptcha (String) request.getSession().getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY); if (!inputCaptcha.equalsIgnoreCase(realCaptcha)) { request.setAttribute(msg, 验证码错误); request.getRequestDispatcher(/login.jsp).forward(request, response); return; }这个改动的价值在于它不改变原有架构却展示了你对安全边界的认知。老师问“怎么防刷”你指着这 30 行说“加了验证码点击可刷新服务端校验大小写不敏感”比背概念强十倍。5.2 用 Filter 实现统一权限拦截告别每个 Servlet 里写if (user null)新建com.filter.AuthFilter.javapublic class AuthFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; HttpSession session request.getSession(false); User user session ! null ? (User) session.getAttribute(user) : null; String path request.getServletPath(); // 白名单登录页、验证码、静态资源 if (/login.jsp.equals(path) || /LoginServlet.equals(path) || /kaptcha.jpg.equals(path) || path.endsWith(.css) || path.endsWith(.js)) { chain.doFilter(req, resp); return; } if (user null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); } }再在web.xml中注册filter filter-nameAuthFilter/filter-name filter-classcom.filter.AuthFilter/filter-class /filter filter-mapping filter-nameAuthFilter/filter-name url-pattern/*/url-pattern /filter-mapping这个 Filter 的威力在于它把原本散落在 5 个 Servlet 里的if (usernull)判断收束到一处。当老师问“怎么控制不同角色访问权限”你可以说“现在所有请求先过 Filter管理员能进/admin/店员只能进/sale/只需在 Filter 里加if (user.getRole().equals(店员) path.startsWith(/admin/))就行。”5.3 导出销售报表用 POI 生成 Excel比截图高级十倍在/src/com/servlet/SaleReportServlet.java中添加导出逻辑HSSFWorkbook workbook new HSSFWorkbook(); HSSFSheet sheet workbook.createSheet(销售报表); // 表头 HSSFRow header sheet.createRow(0); String[] titles {药品名, 规格, 销售数量, 销售时间, 操作员}; for (int i 0; i titles.length; i) { header.createCell(i).setCellValue(titles[i]); } // 数据行 ListSaleRecord records saleDao.findAll(); for (int i 0; i records.size(); i) { HSSFRow row sheet.createRow(i 1); SaleRecord r records.get(i); row.createCell(0).setCellValue(r.getDrugName()); row.createCell(1).setCellValue(r.getSpec()); row.createCell(2).setCellValue(r.getCount()); row.createCell(3).setCellValue(r.getSaleTime()); row.createCell(4).setCellValue(r.getOperator()); } // 输出 response.setContentType(application/vnd.ms-excel); response.setHeader(Content-Disposition, attachment;filenamesale_report_ System.currentTimeMillis() .xls); workbook.write(response.getOutputStream());注意需在lib/中加入poi-3.17.jar和poi-scratchpad-3.17.jar。这个功能的意义不是技术多难而是它把“系统能用”升级为“系统能产出业务价值”——销售日报不用手动整理点一下就生成 Excel这才是药店老板真正在意的。希望帮到你。这些年我反复告诉学生毕业设计不是交差而是你第一次以工程师身份把需求、代码、数据、部署串成一条线。这套药店系统就像一辆手动挡老车没有自动泊车但离合、油门、档位都在你手里——踩准了它比任何智能座舱都让人踏实。本文还有配套的精品资源点击获取
返回列表