2026/10/7 10:35:55

基于JSP+Servlet+MySQL的在线药店管理系统设计与部署全解析

基于JSP+Servlet+MySQL的在线药店管理系统设计与部署全解析 做JavaWeb方向的朋友应该都接触过JSPServlet这套经典技术组合。这个基于javaweb和mysql的在线药店管理系统正是把Java、JSP、Servlet、Bootstrap、jQuery、MySQL这几个技能点串成一条完整链路的课程设计级项目。整个系统覆盖了前台药品浏览、关键字搜索、购物车管理、在线下单以及后台的药品信息维护、订单处理等模块前后端数据走了完整的HTTP请求-响应闭环。这篇文章我打算从项目整体拆解开始把数据库设计思路、核心模块的Servlet实现细节、BootstrapjQuery前端页面的整合方式再到本地从零部署运行的全流程一次讲清楚。同时会把我在跑通这类项目时踩过的坑和排查套路一并整理出来。无论你是正在做JavaWeb课程设计的学生还是想找一个完整案例练手独立开发的新手这份内容都能给你一个可直接参考复现的路径。1. 项目概述与技术选型分析1.1 这个系统解决了什么问题在线药店管理系统的业务模型并不复杂但胜在完整。普通用户注册登录后可以在前台浏览药品分类列表通过关键字搜索目标药品查看药品详情把需要的药品加入购物车调整购买数量之后提交订单。后台则由管理员负责维护药品数据处理订单状态管理注册用户。这样一个闭环覆盖了电商类系统最核心的流程从商品展示到购物车从订单生成到后台履约。相比单纯做一个学生管理系统或者图书管理系统药店场景多了一层业务真实性药品有规格、有厂家、有生产日期和有效期下单时还要校验库存。这些细节让项目在答辩或展示时更有说服力也让你在写代码的时候能真正理解一个业务系统的数据流转。项目本身的技术重点不在算法而在CRUD的规范组织、会话状态的维护、数据库表之间的关联查询以及前端页面的交互整合。这些恰好是JavaWeb入门阶段最需要熟练掌握的能力。1.2 为什么选JSPServlet而不是直接上SpringBoot很多同学会问现在企业里都用SpringBoot为什么课程设计还要写Servlet这个问题的答案恰恰是这个项目的核心价值所在。Servlet是JavaWeb的地基。SpringMVC的DispatcherServlet本质上也只是一个加强版的Servlet搞懂了Servlet的生命周期、请求参数封装、转发重定向、Session管理你再看SpringMVC的源码就会觉得非常眼熟。JSP页面里如何使用EL表达式和JSTL标签如何与后台Servlet协作也是老JavaWeb项目维护中躲不开的技能点。更重要的是用JSPServlet写项目时所有请求处理、页面跳转、参数传递都是裸奔的你能真切感受到浏览器发一个请求到服务器服务器如何从request里取参数如何查数据库如何把数据set到request域再forward到JSP渲染成HTML返回给浏览器的全过程。这种对HTTP协议和JavaWeb底层模型的直观理解是直接上手SpringBoot很难获得的。技术栈搭配上前端用Bootstrap解决响应式布局和组件样式的问题jQuery负责发异步请求和操作DOM。Bootstrap把页面的美观度下限拉得很高即使你不想花太多时间调CSS也能得到一个看着像模像样的管理后台。jQuery的AJAX封装够简洁配合JSON数据交互购物车增减数量这些操作不刷新页面就能完成体验很顺畅。2. 系统整体设计与数据库建模2.1 分层架构与项目包结构设计这个项目采用经典的JSPServletJavaBean分层模式本质上就是MVC思想的一种落地。JSP充当视图层负责页面展示Servlet充当控制层接收请求、调用业务逻辑、决定跳转到哪个页面JavaBean和DAO类组成模型层负责封装数据和操作数据库。我建议遵循这样一个包结构来组织代码清晰且好维护com.pharmacy.entity存放实体类对应数据库中的表比如User、Medicine、Cart、Order、OrderItemcom.pharmacy.dao数据访问层每个实体配一个DAO类写JDBC增删改查方法com.pharmacy.servlet控制层Servlet按功能模块划分比如LoginServlet、RegisterServlet、MedicineServlet、CartServlet、OrderServletcom.pharmacy.filter过滤器统一处理编码、登录状态拦截com.pharmacy.util工具类比如DBUtil数据库连接、MD5Util密码加密这样的分层带来的直接好处是页面出问题去查JSP请求流转有问题去查ServletSQL写错了去查DAO定位特别快。我见过不少同学把JDBC代码直接写在Servlet里一个Servlet动辄几百行后面想加一个功能根本无从下手。分层之后每个类的职责单一即使项目规模再翻一倍维护成本也不会失控。2.2 数据库表设计从业务流程反推数据库设计是一个项目最不能偷懒的部分表结构一旦定下来后面改起来极其痛苦。我这个项目是从业务流程反推表结构的建议你也这么做。核心表可以拆成五张用户表、药品表、购物车表、订单表、订单明细表。下面是各表的字段设计思路和关键说明。表名关键字段设计要点userid, username, password, phone, address, rolerole字段区分普通用户和管理员0为普通用户1为管理员避免单开一张管理员表medicineid, name, category_id, spec, price, stock, image, description, production_date, expire_date, factoryspec存药品规格比如0.25g*24片price用DECIMAL(10,2)image存图片相对路径或URLcartid, user_id, medicine_id, quantity联合字段可以做唯一索引user_id, medicine_id防止同一用户对同一药品产生多行冗余ordersid, order_no, user_id, total_amount, status, create_timeorder_no生成唯一订单号status用数字表示订单状态比如0待付款、1已付款、2已发货、3已完成order_itemid, order_id, medicine_id, price, quantity下单时要把商品快照价存入price避免药品价格后续调整影响历史订单药品分类信息可以单独建一张category表也可以直接在medicine表里加一个category字段。如果分类数量少且固定用字段就行如果希望后台能动态增删分类就建一张分类表medicine表存category_id关联。对于课设项目单独建表能让代码里按分类筛选的逻辑更规范一些也更方便拓展。2.3 关键SQL与数据一致性需要注意的细节金额字段务必使用DECIMAL(10,2)不要用float或double。这个教训我吃了不止一次浮点数在MySQL里做累加和比较时会产生精度误差比如0.10.2可能等于0.30000000000000004价格算错了在电商类项目里是很尴尬的事。库存字段建议用INT并加无符号约束。下单扣库存时SQL可以写成UPDATE medicine SET stock stock - #{quantity} WHERE id #{id} AND stock #{quantity}通过受影响行数判断是否扣减成功这比先查再改更安全也避免了并发超卖的风险。分页查询是药品列表页的刚需SQL用LIMIT配合偏移量实现SELECT * FROM medicine WHERE category_id #{cid} LIMIT #{offset}, #{pageSize}。对应的分页参数计算是offset (currentPage - 1) * pageSize总页数通过ceil(totalCount / pageSize)得到。这部分逻辑建议封装成一个PageBean工具类避免每个Servlet里各写一遍。订单提交涉及多张表的状态变更必须在同一个事务中执行生成订单记录、插入订单明细、扣减库存、清空购物车任何一步失败都要回滚。JDBC里通过connection.setAutoCommit(false)开启事务最后统一commit遇异常rollback。这也是项目中唯一真正用上事务机制的地方。3. 核心功能模块实现与实操要点3.1 用户注册登录与权限控制用户模块看起来简单但有几个细节直接决定系统的安全性和体验。密码存储不能明文入库至少要做一次MD5加盐处理。最简单的方式是把用户名作为盐拼到密码后面再MD5MD5(password username)。这样做的好处是哪怕两个用户密码相同最终密文也不同能在一定程度上抵御彩虹表攻击。虽然SHA-256更推荐但课设项目用MD5也够说明问题了。登录成功后把用户对象存入Session比如request.getSession().setAttribute(loginUser, user)后续所有需要身份的操作都从Session里取。退出登录则调用session.invalidate()强制销毁会话。权限控制建议用Filter统一处理而不是在每个Servlet里重复判断。写一个LoginFilter在doFilter方法中检查请求路径如果访问的是后台管理路径比如/admin/*而当前Session里没有管理员信息直接重定向到登录页。这样可以避免漏判某个Servlet导致的越权访问。3.2 药品浏览、搜索与分页实现前台药品列表页是整个系统的门面我建议用Bootstrap的卡片Card组件来布局药品卡片每个卡片展示药品图片、名称、规格和价格。Bootstrap的栅格系统很方便col-md-3配合col-sm-6就能实现不同屏幕下每行4个或2个卡片的响应式布局。药品列表Servlet处理请求的流程大致是从request里取当前页码public int currentPage和分类ID默认当前页为1、分类为0全部。调用DAO层按条件查询总记录数和当前页数据封装进PageBean存入request域后forward到medicine_list.jsp。搜索功能用SQL的LIKE语句实现SELECT * FROM medicine WHERE name LIKE CONCAT(%, #{keyword}, %)。需要注意keyword中包含百分号或下划线时会被LIKE当成通配符严谨的做法是用ESCAPE语法转义但课设项目里用CONCAT拼装参数并配合PreparedStatement的占位符传递已经能防止SQL注入了。JSP页面里展示分页导航可以用Bootstrap的Pagination组件。最核心的一点是分页链接要保留当前的搜索关键字和分类状态否则点下一页之后搜索条件就丢了这个细节很多初学者会漏掉。3.3 购物车与订单的完整链路购物车的实现有几种方案基于Session、基于Cookie、基于数据库表。这个项目选择的是数据库表方案也就是把购物车数据持久化到cart表。优点是用户换浏览器登录后购物车数据还在缺点是需要维护user_id和medicine_id的关联关系。对于课程设计而言数据库方案更有利于展示你对表关联的掌握程度也比较容易被提问环节的老师认可。添加购物车用jQuery发AJAX请求往CartServlet传medicineId和quantityServlet返回JSON对象{code:200,msg:添加成功}前端根据code值提示用户操作结果。这样用户体验更好不需要整个页面刷新。提交订单的Servlet是整个项目逻辑最密集的一段代码。需要做的操作包括根据当前用户查购物车数据、遍历计算总金额、生成唯一订单号、插入订单主表和明细表、扣减库存、清空购物车。整个过程必须加事务控制否则中间出错会导致订单数据和库存不一致。订单号生成我用的方式是SimpleDateFormat格式化时间戳再拼四位随机数比如yyyyMMddHHmmss加Random生成的数字。并发量不高的情况下这种方案完全够用如果要更严谨可以引入雪花算法但对课设项目属于过度设计。3.4 后台管理与药品图片上传后台管理模块的核心是药品信息的增删改查。列表页用一个Bootstrap的Table组件展示所有药品每一行末尾放编辑删除按钮。编辑时点击按钮跳转到编辑页面表单里回显原有数据。药品图片上传是这里相对容易翻车的点。上传功能的实现思路是在JSP表单里设置enctypemultipart/form-data后台用Apache Commons FileUpload组件解析请求把文件保存到项目指定的上传目录然后在数据库里记录文件的相对访问路径。图片保存路径要格外注意。很多人喜欢把图片存到Tomcat的webapps目录下但这样做的问题是重新部署项目时上传目录会被清空图片全部丢失。我建议单独指定一个磁盘目录存放上传文件比如D:\\upload\\然后在Tomcat的server.xml里配置虚拟路径映射让访问/upload/xxx.jpg时映射到磁盘目录。这样项目重新部署不影响已上传的图片。如果不想引入FileUpload组件也可以退一步用Base64编码把图片直接存到数据库的TEXT字段里页面展示时用data:image/jpeg;base64,xxxx的方式渲染。这种方式实现最简单但数据库膨胀很快只适合小图片。4. 环境配置与项目完整部署实操4.1 开发环境准备清单跑通这个项目你需要准备的基础环境如下。版本上我推荐一套经过大量验证的稳定组合新手直接按这个来能少踩很多版本兼容性的坑。JDK 8或11Oracle JDK或OpenJDK均可Tomcat 8.5或9.0注意JDK版本和Tomcat版本的兼容性MySQL 5.7或8.0强烈推荐5.7系列或8.0.26以上小版本IntelliJ IDEA社区版即可不一定要旗舰版MySQL驱动jar包mysql-connector-java5.7用5.1.498.0用8.0.33如果你用IDEA创建项目不需要Maven直接把jar包拷到WEB-INF/lib目录下就能被Tomcat识别。如果用Maven在pom.xml里引入依赖会更省事。两种方式我都跑过原生lib方式对理解JavaWeb项目结构更有帮助Maven方式则更接近企业开发习惯。注意MySQL 5.7和8.0的驱动类名不一样5.7是com.mysql.jdbc.Driver8.0是com.mysql.cj.jdbc.Driver。连接URL中8.0必须带时区参数否则会报Server returns invalid timezone错误。这个坑几乎每个新手都会踩一次。4.2 数据库初始化与JDBC连接配置数据库初始化直接执行SQL脚本即可。首先创建数据库CREATE DATABASE pharmacy DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;。然后切换到这个库执行建表和插入测试数据的脚本。JDBC连接信息我建议抽到一个db.properties配置文件中统一管理然后用一个DBUtil工具类读取配置、加载驱动、返回Connection。这样后续修改数据库密码只需要改配置文件不用动代码重新编译。jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/pharmacy?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8 jdbc.usernameroot jdbc.password123456DBUtil类里的加载驱动逻辑要注意用Class.forName()注册驱动只需要执行一次建议放在静态代码块里。获取连接用DriverManager.getConnection(url, username, password)。每次用完连接要关闭遵循谁打开谁关闭的原则可以写一个close方法统一释放Connection、Statement、ResultSet三个资源。4.3 部署到Tomcat并启动验证IDEA中部署JavaWeb项目核心步骤如下。项目名上右键选择Add Framework Support勾选Web Application生成标准web目录结构。然后在Project Structure的Artifacts里配置Web Application Archive或者Exploded目录。打开Run/Debug Configurations点击左上角加号新建Tomcat Server Local配置。如果列表里没有Tomcat Server选项说明没有在Settings里配置Application Servers。选好Tomcat安装目录后在Deployment选项卡里添加ArtifactApplication Context建议设为/pharmacy。启动Tomcat后观察控制台日志看到Server startup in [xxx] milliseconds就说明启动成功。浏览器访问http://localhost:8080/pharmacy/如果能跳转到首页整个部署流程就完成了。首页通常是项目根路径下的index.jsp或者配置在web.xml里的welcome-file。5. 常见问题与排错经验实录5.1 环境与启动类问题排查套路做JavaWeb项目时环境问题占了调试时间的一半以上。我把自己遇到的最高频的问题整理成了一张速查表按症状、原因、解决办法三层列出直接对照操作就行。症状常见原因解决办法Tomcat启动报端口被占用8080端口被其他进程占用修改Tomcat的server.xml端口为8081或查杀占用进程启动报ClassNotFoundJdbcDriver驱动jar包没有放到WEB-INF/lib将mysql-connector-java.jar拷入WEB-INF/lib并重启Tomcat连接MySQL报timezone错误MySQL 8.0连接URL缺少时区参数在jdbc.url后追加serverTimezoneAsia/Shanghai启动报无效的发行版本JDK与Tomcat或编译级别不匹配检查Project Structure中Project SDK和Java Compiler版本一致访问页面报404Artifact部署名和访问路径不一致检查Deployment中Application Context是否为/pharmacy第一个需要掌握的就是看日志。我习惯先看Tomcat的catalina.out和IDEA控制台的红色异常堆栈重点找Caused by那一段那才是问题的真正根源。初学者往往盯着最后一行报错看半天其实很多时候最后一行只是结果Caused by才是诱因。5.2 业务逻辑与页面渲染问题跑通启动只是第一步业务代码里的坑同样不少。中文乱码问题最常见。解决思路很固定JSP页面顶部加pageEncodingUTF-8和contentTypetext/html; charsetUTF-8Servlet里统一设置request.setCharacterEncoding(UTF-8)和response.setContentType(text/html;charsetUTF-8)数据库连接URL加characterEncodingutf8Tomcat的server.xml中Connector加URIEncodingUTF-8。四个位置都对齐了想乱码都难。图片上传后无法显示也是高频问题。排查顺序优先检查两个地方数据库里image字段存的是不是正确的访问路径以及部署目录下是否真的存在这个文件。如果是用IDEA部署的普通web项目上传目录默认在Tomcat的webapps下重新部署时文件会被清空所以建议按前文说的配置虚拟路径映射到磁盘独立目录。购物车Session失效导致用户刚加的药品丢了这个问题的根源通常是web.xml里Session超时时间设置太短或者代码里误调用了session.invalidate()。排查时可以在浏览器开发者工具的Application面板里查看Session的过期时间在服务器端打印Session ID辅助判断。5.3 经验心得与后续扩展建议做完整套流程之后我最大的体会是课设项目的代码量不重要重要的是你能说清楚每个模块的数据是怎么流转的。准备答辩的时候把用户点击添加购物车按钮后jQuery发出什么请求Servlet里怎么取参数DAO怎么执行SQL数据存到了哪张表页面怎么刷新这条链路理清楚评委基本就不会为难你。后续想在这个项目上继续升级路子也不少。最直接的是用Spring Boot SpringMVC MyBatis重构你会发现原来Servlet里那些繁琐的request取值、手动封装参数操作在SpringMVC里都被注解自动完成了。可以把用户和药品相关接口抽成RESTful风格前端改造为AJAX渲染JSON数据。再加上Spring Security实现更完整的权限体系这个项目的架构就和企业级应用比较接近了。如果你的毕业设计方向就是这个沿着这条路深挖完全够用。本系统从功能设计到技术实现都比较标准跟着上面的步骤走一遍你不仅能跑通项目更能理解JavaWeb开发中请求-处理-响应这一整套底层逻辑。如果有哪个环境配置或者代码细节没搞清楚不用急多半是版本或路径的小问题对着日志一步步排查就能解决。