拓冰建站拓冰建站
首页 / 资讯中心 / 正文

JavaWeb药店管理系统实战:Servlet+JSP+MySQL从建表到部署

简介这是一套基于JavaWeb技术栈的药店管理系统完整源码与数据库资源采用servlet与jsp实现业务逻辑MySQL负责数据存储适合正在学习JavaWeb课程设计或需要完成药店管理类毕业设计的学生与开发者参考。系统覆盖客户信息、药品类别、药品信息、出入库记录及用户权限等核心模块可实现药品的新增、修改、删除、出库、入库与记录查询帮助理解分层开发与数据库交互的完整流程。资源包共112个文件包含32个java源文件、32个class编译文件、7个jsp页面、8个css样式、6个js脚本及6个jar依赖另有sql数据库脚本与Eclipse项目配置压缩包约2.19MB结构清晰便于导入运行。目前已有574人学习下载可作为课程实践与项目练手的实用参考。1. 药店管理系统从建表到 Servlet 分发一套能跑通的 JavaWeb 最小闭环接手一个药店管理系统的毕设或课设最怕的不是功能多而是环境跑不起来。源码拿到手IDEA 一开Tomcat 一配数据库一连报错能刷满三屏。这个标题背后其实是一套非常经典的 JavaWeb 技术栈Servlet 做控制器、JSP 做视图、MySQL 存数据、JDBC 打通连接。它解决的是药品信息录入、库存查询、销售出库、用户权限这几件事适合正在做课程设计的学生也适合想用一个小项目把 Servlet 生命周期、请求转发、会话管理串一遍的初学者。我见过太多人卡在ClassNotFoundException: com.mysql.cj.jdbc.Driver上其实问题根本不在代码而在驱动包没放进WEB-INF/lib。这篇笔记就按我实际搭这套系统的顺序把建库、连库、写 Servlet、配 JSP 的每一步拆开讲参数怎么设、坑在哪都写清楚。2. 数据库先落地药品表、库存表和用户表的字段怎么定2.1 三张核心表的建表语句与字段类型选择药店管理系统的数据模型不复杂但字段类型选错后面查询和更新都会难受。我一般先建三张表drug药品信息、stock库存流水、user系统用户。药品表里药品名称用varchar(100)批准文号用varchar(50)并加唯一索引价格用decimal(10,2)而不是float因为金额计算用浮点会出现0.10.20.30000000000000004这种玄学问题。库存表记录每次入库和出库用int存数量用tinyint区分操作类型1 入库2 出库。用户表密码字段至少varchar(64)方便存哈希值。CREATE DATABASE pharmacy_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE pharmacy_db; CREATE TABLE user ( id INT NOT NULL AUTO_INCREMENT, username VARCHAR(50) NOT NULL COMMENT 登录名, password VARCHAR(64) NOT NULL COMMENT 密码哈希, role TINYINT DEFAULT 1 COMMENT 1管理员 2普通员工, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE drug ( id INT NOT NULL AUTO_INCREMENT, drug_name VARCHAR(100) NOT NULL, approval_no VARCHAR(50) NOT NULL COMMENT 批准文号, price DECIMAL(10,2) NOT NULL DEFAULT 0.00, stock_num INT NOT NULL DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_approval (approval_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE stock ( id INT NOT NULL AUTO_INCREMENT, drug_id INT NOT NULL, quantity INT NOT NULL, op_type TINYINT NOT NULL COMMENT 1入库 2出库, op_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_drug (drug_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;建表时字符集统一用utf8mb4否则药品名里的生僻字或特殊符号会变成问号。approval_no加唯一索引是为了防止同一批准文号的药品被重复录入这是药店系统里最常见的脏数据来源。stock表不直接改drug表的库存数而是通过流水累加这样对账时能追溯每一笔变动。2.2 在 Windows 上装 MySQL 8.0 并导入脚本的完整命令热词里很多人搜mysql安装教程8.0和mysql在windows10上怎么安装我直接给一套能复现的流程。下载 ZIP 版解压到D:\tool\mysql-8.0.xx-winx64然后在根目录建my.ini内容如下[mysqld] basedirD:\tool\mysql-8.0.46-winx64 datadirD:\tool\mysql-8.0.46-winx64\data port3306 character-set-serverutf8mb4 default_authentication_pluginmysql_native_password初始化并安装服务mysqld --initialize-insecure --usermysql mysqld --install MySQL80 net start MySQL80 mysql -u root -p第一次登录密码为空直接回车。然后执行ALTER USER rootlocalhost IDENTIFIED BY 你的密码;。导入脚本用source命令source D:/pharmacy_db.sql;注意路径用正斜杠反斜杠在 MySQL 里是转义符。如果net start mysql报错先看data目录是否已存在存在就删掉重新初始化。default_authentication_plugin设成mysql_native_password是为了兼容老版本 JDBC 驱动不然连接时会报Unable to load authentication plugin caching_sha2_password。3. Servlet 做控制器请求分发、参数接收和 JDBC 工具类3.1 用 IDEA 建 JavaWeb 项目并配置 Tomcat 的步骤在 IDEA 里选File - New - Project - Java Enterprise勾选Web Application不要选 Maven课设环境用普通 Web 项目更省事。建好后在WEB-INF下新建lib目录把mysql-connector-java-8.0.xx.jar和servlet-api.jar拷进去然后File - Project Structure - Modules - Dependencies里把这两个 jar 加进去。Tomcat 配置在Run - Edit Configurations点选Tomcat Server - Local在Deployment标签页加Artifact选war exploded。这一步不做启动后访问 404别问我是怎么知道的。3.2 JDBC 工具类连接、关闭和防止空指针的写法每个 Servlet 都写一遍DriverManager.getConnection是血泪教训的开始。我一般抽一个DBUtil类用静态块加载驱动用ThreadLocal或者直接方法返回连接。下面是最简版本public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/pharmacy_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8; private static final String USER root; private static final String PWD 你的密码; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new RuntimeException(MySQL驱动未找到检查WEB-INF/lib, e); } } public static Connection getConn() throws SQLException { return DriverManager.getConnection(URL, USER, PWD); } public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { try { if (rs ! null) rs.close(); } catch (SQLException ignored) {} try { if (ps ! null) ps.close(); } catch (SQLException ignored) {} try { if (conn ! null) conn.close(); } catch (SQLException ignored) {} } }serverTimezoneAsia/Shanghai不加会报时区错误useSSLfalse去掉 SSL 警告。close方法里每个关闭都单独 try-catch避免一个异常导致后面的资源没释放。参数说明URL里的pharmacy_db换成你自己的库名USER和PWD换成安装时设的。3.3 药品列表 Servlet从查询到转发 JSP 的完整链路以药品列表为例DrugListServlet继承HttpServlet重写doGetWebServlet(/drug/list) public class DrugListServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ListDrug list new ArrayList(); String sql SELECT id, drug_name, approval_no, price, stock_num FROM drug ORDER BY id DESC; try (Connection conn DBUtil.getConn(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { Drug d new Drug(); d.setId(rs.getInt(id)); d.setDrugName(rs.getString(drug_name)); d.setApprovalNo(rs.getString(approval_no)); d.setPrice(rs.getBigDecimal(price)); d.setStockNum(rs.getInt(stock_num)); list.add(d); } } catch (SQLException e) { e.printStackTrace(); req.setAttribute(error, 查询失败 e.getMessage()); } req.setAttribute(drugList, list); req.getRequestDispatcher(/drug_list.jsp).forward(req, resp); } }WebServlet注解省去 web.xml 配置路径/drug/list就是访问地址。查询用ORDER BY id DESC让新录入的药品排前面。try-with-resources自动关闭连接比手动 close 更稳。转发用forward而不是sendRedirect因为要把drugList带到 JSP 页面。如果查询报错把错误信息塞进requestJSP 里用${error}显示。4. JSP 页面渲染表格展示、表单提交和中文乱码处理4.1 用 JSTL 遍历药品列表并生成表格JSP 里不要写 Java 脚本片段用 JSTL 和 EL 表达式。先引入标签库% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html headtitle药品列表/title/head body h2药品库存/h2 table border1 cellpadding5 tr thID/thth药品名/thth批准文号/thth价格/thth库存/th /tr c:forEach items${drugList} vard tr td${d.id}/td td${d.drugName}/td td${d.approvalNo}/td td${d.price}/td td${d.stockNum}/td /tr /c:forEach /table c:if test${not empty error} p stylecolor:red${error}/p /c:if /body /htmlc:forEach的items对应 Servlet 里setAttribute(drugList, list)的 key。vard是循环变量EL 里用${d.drugName}取属性前提是Drug类有getDrugName()方法。如果页面显示乱码检查三处JSP 的pageEncoding、Servlet 的resp.setContentType(text/html;charsetUTF-8)、数据库连接 URL 里的characterEncodingutf8。三处缺一不可。4.2 新增药品表单POST 提交与 Servlet 接收参数新增页面drug_add.jsp放一个表单form action${pageContext.request.contextPath}/drug/add methodpost 药品名input typetext namedrugName requiredbr 批准文号input typetext nameapprovalNo requiredbr 价格input typenumber step0.01 nameprice requiredbr 库存input typenumber namestockNum requiredbr input typesubmit value提交 /form对应的DrugAddServlet在doPost里接收Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String drugName req.getParameter(drugName); String approvalNo req.getParameter(approvalNo); BigDecimal price new BigDecimal(req.getParameter(price)); int stockNum Integer.parseInt(req.getParameter(stockNum)); String sql INSERT INTO drug(drug_name, approval_no, price, stock_num) VALUES(?,?,?,?); try (Connection conn DBUtil.getConn(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, drugName); ps.setString(2, approvalNo); ps.setBigDecimal(3, price); ps.setInt(4, stockNum); ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } resp.sendRedirect(req.getContextPath() /drug/list); }req.setCharacterEncoding(UTF-8)必须放在getParameter之前否则中文药品名会变成乱码。插入成功后用sendRedirect跳回列表页避免刷新重复提交。BigDecimal接收价格和数据库decimal类型对应。5. 避坑与排查连接失败、乱码和 404 的 5 个真实翻车记录5.1 现象启动报ClassNotFoundException: com.mysql.cj.jdbc.Driver原因MySQL 驱动 jar 没放进WEB-INF/lib或者 IDEA 的 Artifact 里没包含这个 lib。解决确认WEB-INF/lib下有mysql-connector-java-8.0.xx.jar然后在Project Structure - Artifacts里看WEB-INF/lib是否在输出布局中。如果用的是 Tomcat 的lib目录把驱动也拷一份过去。5.2 现象页面显示??????或测试原因字符编码链路某一环断了。解决按顺序检查——数据库建库时utf8mb4、连接 URL 加characterEncodingutf8、Servlet 里req.setCharacterEncoding(UTF-8)和resp.setContentType(text/html;charsetUTF-8)、JSP 顶部pageEncodingUTF-8。五处全对才不会乱码。5.3 现象访问/drug/list返回 404原因WebServlet的路径和浏览器地址不一致或者 Tomcat 部署的 Artifact 是war而不是war exploded。解决看 Tomcat 启动日志里Deployment的 context path如果是/pharmacy_war_exploded访问地址就是http://localhost:8080/pharmacy_war_exploded/drug/list。用war exploded支持热更新改 JSP 不用重启。5.4 现象Public Key Retrieval is not allowed原因MySQL 8.0 默认用caching_sha2_passwordJDBC 连接时不允许公钥检索。解决在连接 URL 后面加allowPublicKeyRetrievaltrue或者把用户认证插件改回mysql_native_passwordALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 密码;。5.5 现象库存更新后列表没变原因Servlet 里用了sendRedirect但浏览器缓存了旧页面或者查询 SQL 没加ORDER BY导致看到的是旧数据。解决在sendRedirect后加时间戳参数或者 JSP 里加meta http-equivCache-Control contentno-cache。更稳的做法是库存变动后直接查一次数据库再转发。6. 进阶技巧用过滤器统一编码和登录拦截6.1 一个 Filter 解决全站中文乱码每个 Servlet 都写setCharacterEncoding太啰嗦建一个EncodingFilterWebFilter(/*) public class EncodingFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; request.setCharacterEncoding(UTF-8); response.setContentType(text/html;charsetUTF-8); chain.doFilter(request, response); } }WebFilter(/*)拦截所有请求在chain.doFilter之前设置编码。这样 JSP 和 Servlet 都不用再单独处理。注意如果用了WebFilterweb.xml里不要再配一遍否则过滤器执行两次。6.2 登录拦截没登录不能进药品管理页再写一个LoginFilter拦截/drug/*和/stock/*WebFilter(urlPatterns {/drug/*, /stock/*}) public class LoginFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; Object user request.getSession().getAttribute(loginUser); if (user null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(request, response); } }登录成功后session.setAttribute(loginUser, user)拦截器里判断 session 里有没有这个属性。urlPatterns用数组可以配多个路径。注意过滤器的执行顺序按类名字母排序EncodingFilter和LoginFilter同时存在时编码过滤器要先执行所以类名可以改成AEncodingFilter和BLoginFilter或者用web.xml的filter-mapping控制顺序。6.3 验证方法用 Postman 和浏览器双端确认写完接口后我习惯先用 Postman 发 POST 请求测drug/add看返回状态码和数据库里是否真的插入了数据。然后再用浏览器走一遍完整流程登录 - 列表 - 新增 - 返回列表。如果 Postman 成功但浏览器失败多半是 session 或过滤器的问题。最后把 Tomcat 日志级别调到FINE看 SQL 有没有执行、参数有没有传对。这套系统不大但把编码、连接池、过滤器、JSTL 这几块吃透后面换 SpringBoot 或 MyBatis 都是顺水推舟的事。希望帮到你。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门