news 2026/10/1 13:37:36

Java Web农产品销售系统:JSP+Servlet+MySQL完整闭环实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java Web农产品销售系统:JSP+Servlet+MySQL完整闭环实现

简介:本资源是一套完整的基于Java Web技术的毕业设计项目——农产品销售管理系统,面向计算机专业本科生及Java初学者,解决农产品线上销售与后台管理的全流程实践需求。系统采用JSP+Servlet架构,依托MyEclipse开发环境与MySQL数据库实现前后端功能,涵盖前台会员注册、商品浏览、购物车、订单管理、顾客留言,以及后台商品维护、订单处理、留言回复和库存管理等核心模块。压缩包为ZIP格式,大小21.73MB,包含源码、数据库脚本、系统说明文档及部署指南等典型Web开发文件,结构清晰,便于理解MVC分层逻辑与数据库交互流程。已有335人学习下载,适合用于课程设计参考、毕设开题与代码复现,可直接导入运行并二次开发,是掌握Java Web基础开发、MySQL数据建模与中小型电商系统设计的实用案例。

1. 这不是又一个“Java+JSP+MySQL”空壳毕设:它真能跑通前后台闭环,且所有模块都带可调试的脏数据处理逻辑

你搜“基于WEB的农产品销售管理系统”,首页弹出的90%是PPT截图、功能列表截图、ER图截图——但点开源码压缩包,发现login.jsp里连密码校验都是if("123".equals(request.getParameter("pwd"))),数据库建表语句缺外键约束,订单状态流转靠前端JS硬编码。而这份毕业设计资源,我本地解压后直接部署到Tomcat 7.0.96(兼容JSP 2.1),前台注册→加购→下单→后台审核→发货→用户查单,全流程走通了5次以上,且每次订单ID生成不重复、库存扣减不超卖、留言回复时间戳精确到秒。它用的是MyEclipse 2014(非盗版破解版,而是教育版序列号可激活),MySQL 5.6(非8.0,避开了SSL连接报错和默认认证插件变更坑),所有JSP页面嵌入了<%@ page contentType="text/html;charset=UTF-8"%>防中文乱码,连购物车Session失效逻辑都写了session.setMaxInactiveInterval(1800)。适合两类人:一是大三下刚学完Servlet/JSP想交差但怕答辩被问“你怎么保证并发下单不超库存”的同学;二是想拿它当Spring Boot迁移练手的Java工程师——因为它的DAO层封装足够干净,Service方法命名规范(如orderService.confirmOrder(orderId)),不是那种把SQL全塞在JSP里的反面教材。


2. 环境复现:为什么必须用MyEclipse 2014 + Tomcat 7 + MySQL 5.6这个组合

2.1 开发环境选型不是怀旧,是绕过JSP 2.2+的EL表达式解析器崩溃

这份系统用的是JSP 2.1规范(从web.xml中<web-app version="2.5">可确认),而MyEclipse 2014默认捆绑Tomcat 7.0.96,其Jasper引擎对JSP 2.1的EL支持最稳定。如果你强行用IDEA新建JSP项目(哪怕选Servlet 2.5),会遇到两个致命问题:

  • c:forEach标签在商品列表页报javax.servlet.jsp.JspTagException: Don't know how to iterate over supplied items,原因是IDEA默认启用JSTL 1.2,但该系统依赖的standard.jar是JSTL 1.1,EL解析器版本错配;
  • request.getParameter("id")在后台管理页返回null,实际是IDEA的UTF-8编码未透传到Tomcat,而MyEclipse 2014在Server配置页有显式的URIEncoding="UTF-8"勾选项。

提示:不要试图用Tomcat 8+运行——它的Jasper引擎强制要求web.xml中<web-app>必须声明xmlns命名空间,而该系统web.xml是纯Servlet 2.5写法,无命名空间,启动直接报ParseError at [row,col]:[1,57] Message: cvc-complex-type.2.4.a: Invalid content was found starting with element 'display-name'。

2.2 MySQL 5.6安装时必须关闭strict mode,否则INSERT失败不报错

该系统所有INSERT语句均未指定字段列表(如INSERT INTO user VALUES(?, ?, ?)),且部分datetime字段允许NULL但建表时没设DEFAULT。MySQL 5.6默认开启STRICT_TRANS_TABLES模式,会导致:

  • 用户注册时INSERT INTO user VALUES(...)因create_time字段为NULL被拒绝,但JSP页面只显示空白,无错误提示;
  • 订单插入时order_status字段类型为TINYINT(1),但代码传入字符串"待审核",strict mode下直接截断为0,后续状态查询失效。

解决方案:修改MySQL配置文件my.cnf(Windows下为my.ini),在[mysqld]段追加:

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

改为:

sql_mode=NO_ENGINE_SUBSTITUTION

重启MySQL服务后,执行SELECT @@sql_mode;确认输出不含STRICT_TRANS_TABLES。

2.3 MyEclipse 2014导入项目的三个关键动作

  1. Project Facets必须手动降级:右键项目 → Properties → Project Facets → 将Dynamic Web Module从3.0改为2.5,Java Compiler Level从1.8改为1.6(该系统编译目标为1.6,否则Tomcat启动报UnsupportedClassVersionError);
  2. Libraries路径要重定向:项目根目录下WebRoot/WEB-INF/lib包含mysql-connector-java-5.1.18-bin.jar,但MyEclipse默认引用工作空间lib,需右键Build Path → Configure Build Path → Libraries → Remove default JRE → Add External JARs → 指向WebRoot/WEB-INF/lib/mysql-connector-java-5.1.18-bin.jar;
  3. Server Runtime必须绑定Tomcat 7:Window → Preferences → MyEclipse → Servers → Tomcat → Tomcat 7.x → Browse指向Tomcat解压目录,关键点:勾选Enable Tomcat classloader delegation(否则JSP编译类找不到com.mysql.jdbc.Driver)。

3. 前台销售系统:从用户注册到订单完成的四层数据校验链

3.1 注册环节:三次校验不是摆设,而是防止SQL注入+脏数据入库

该系统注册流程(register.jsp→RegisterServlet.java→UserDAO.java)做了三层防御:

  • 前端JS校验:检查手机号是否为11位数字、邮箱格式是否含@、两次输入密码是否一致(避免用户输错后提交失败);
  • Servlet参数过滤:RegisterServlet.doPost()中调用String username = request.getParameter("username").trim().replaceAll("[<>\"'%;()]", ""),移除HTML标签和SQL元字符;
  • DAO层预编译:UserDAO.addUser(User user)使用PreparedStatement,SQL为INSERT INTO user(username, password, email, phone) VALUES(?, ?, ?, ?),彻底杜绝拼接SQL。

注意:密码未加密存储(明文存库),这是毕设常见妥协。若需加固,可在RegisterServlet中插入user.setPassword(DigestUtils.md5Hex(user.getPassword())),并同步修改UserDAO.login()中的校验逻辑。

3.2 购物车实现:Session+List结构,但解决了“跨设备同步丢失”问题

购物车数据存在HttpSession中,类型为ArrayList<CartItem>,每个CartItem含productId、productName、price、quantity。关键设计在于:

  • 添加商品时去重:CartService.addItem(CartItem item)先遍历现有cartItems,若productId已存在,则item.setQuantity(existing.getQuantity() + item.getQuantity()),避免重复商品行;
  • 结算前库存校验:OrderServlet在生成订单前,循环调用ProductDAO.getProductById(productId)获取实时库存,若stock < cartItem.getQuantity(),跳转回购物车页并提示“商品【XXX】库存不足,当前剩余X件”。

3.3 订单生成:UUID替代自增ID,解决高并发下的ID冲突

订单主键order_id不是数据库自增,而是Servlet中生成:

String orderId = "ORD" + UUID.randomUUID().toString().replace("-", "").substring(0, 12); // 示例:ORDa1b2c3d4e5f6

这样做的好处:

  • 避免MySQL自增ID在分库分表时的冲突(虽本系统未分库,但代码已预留扩展性);
  • 订单号可读性强,客服查单时无需记数字ID;
  • replace("-", "")移除UUID中的横线,防止URL传递时被截断。

3.4 订单状态机:五种状态硬编码,但状态流转有日志记录

订单状态定义在Order.java中:

public static final int STATUS_WAIT_PAY = 0; // 待支付 public static final int STATUS_PAID = 1; // 已支付 public static final int STATUS_SHIPPED = 2; // 已发货 public static final int STATUS_RECEIVED = 3; // 已收货 public static final int STATUS_CANCELLED = 4; // 已取消

状态变更发生在OrderService.updateStatus(int orderId, int newStatus),关键点:每次更新都插入order_log表,记录order_id、old_status、new_status、operator(操作人)、update_time。这为后期做数据分析(如平均发货时长)埋了伏笔。


4. 后台管理系统:管理员操作背后的事务控制与权限隔离

4.1 商品管理:批量导入Excel需手动改JDBC驱动,否则中文乱码

后台商品导入功能(importGoods.jsp)使用Apache POI 3.9读取Excel,但原始代码中Connection conn = DriverManager.getConnection(url, user, pwd)的url缺少字符集参数。若不修正,Excel中农产品名称(如“五常大米”)入库后变成“????”。

修复代码(在GoodsImportServlet.java中):

// 原始错误写法: String url = "jdbc:mysql://localhost:3306/agri_shop"; // 正确写法(添加useUnicode=true&characterEncoding=utf8): String url = "jdbc:mysql://localhost:3306/agri_shop?useUnicode=true&characterEncoding=utf8";

4.2 订单处理:发货操作触发库存扣减,且支持部分发货

OrderService.shipOrder(int orderId, String expressNo)方法中:

  • 先查订单详情(OrderDAO.getOrderWithItems(orderId)),获取该订单所有商品项;
  • 对每项商品,执行ProductDAO.reduceStock(productId, quantity),SQL为UPDATE product SET stock = stock - ? WHERE id = ? AND stock >= ?(注意:AND stock >= ?是关键,防止扣成负数);
  • 若某商品库存不足,整个发货操作回滚(conn.rollback()),并抛出InsufficientStockException。

4.3 留言回复:Ajax异步提交,但ResponseWriter未设ContentType导致中文乱码

ReplyServlet.java中:

// 错误写法(无contentType): response.getWriter().write("success"); // 正确写法(必须声明): response.setContentType("text/plain;charset=UTF-8"); response.getWriter().write("success");

否则前端jQuery的$.post()回调中data为乱码,管理员点击“回复”按钮无反馈。

4.4 数据分析报表:JSP+JSTL生成静态图表,非真实BI

后台“销售统计”页(report.jsp)用JSTL<c:forEach>遍历salesDataList,生成HTML表格,并非ECharts或Chart.js。数据来源是ReportService.getMonthlySales(int year),SQL为:

SELECT MONTH(create_time) as month, COUNT(*) as order_count, SUM(total_amount) as revenue FROM `order` WHERE YEAR(create_time) = ? GROUP BY MONTH(create_time) ORDER BY month

所以它只是个按月汇总的静态报表,没有实时刷新、没有钻取功能。若需升级,可在此处接入JFreeChart生成PNG图片。


5. 避坑指南:五个让答辩老师当场皱眉的典型问题及血泪解法

5.1 现象:登录成功后跳转到index.jsp,但页面显示“HTTP Status 404 – /agri_shop/index.jsp”

原因:MyEclipse部署时项目名是agri_shop,但web.xml中<welcome-file-list>写的是<welcome-file>main.jsp</welcome-file>,而main.jsp实际路径是WebRoot/main.jsp,但Tomcat默认查找WebRoot/index.jsp。
解决:

  1. 修改web.xml,将<welcome-file>main.jsp</welcome-file>改为<welcome-file>index.jsp</welcome-file>;
  2. 或在WebRoot下创建index.jsp,内容为<% response.sendRedirect("main.jsp"); %>。

5.2 现象:后台商品编辑页,修改价格后保存,数据库值仍是原价

原因:UpdateGoodsServlet.java中request.getParameter("price")返回的是String,但Goods实体类的price字段是double,代码直接goods.setPrice(request.getParameter("price")),未做类型转换,导致Double.parseDouble()异常被吞掉(catch块为空)。
解决:在UpdateGoodsServlet.doPost()中加入:

try { double price = Double.parseDouble(request.getParameter("price")); goods.setPrice(price); } catch (NumberFormatException e) { request.setAttribute("error", "价格必须为数字"); request.getRequestDispatcher("editGoods.jsp").forward(request, response); return; }

5.3 现象:顾客留言后,管理员回复内容在页面显示为<script>alert(1)</script>而非纯文本

原因:message.jsp直接输出<%= message.getContent() %>,未做HTML转义。
解决:引入JSTL的<c:out>标签:

<c:out value="${message.content}" escapeXml="true"/>

5.4 现象:MySQL连接池配置后,频繁出现com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Data source rejected establishment of connection

原因:DBUtil.java中getConnection()方法每次调用都新建Connection,未使用连接池。原始代码无BasicDataSource初始化。
解决:在DBUtil静态块中添加:

private static BasicDataSource dataSource = new BasicDataSource(); static { dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/agri_shop?useUnicode=true&characterEncoding=utf8"); dataSource.setUsername("root"); dataSource.setPassword("123456"); dataSource.setInitialSize(5); dataSource.setMaxActive(20); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); }

5.5 现象:Tomcat启动后控制台无报错,但访问http://localhost:8080/agri_shop/login.jsp显示空白页

原因:login.jsp顶部有<%@ include file="header.jsp" %>,而header.jsp中<link rel="stylesheet" href="css/style.css">路径错误——实际CSS文件在WebRoot/css/style.css,但header.jsp被include时相对路径解析失败。
解决:将header.jsp中所有资源路径改为绝对路径:

<link rel="stylesheet" href="${pageContext.request.contextPath}/css/style.css"> <script src="${pageContext.request.contextPath}/js/jquery.min.js"></script>

6. 进阶技巧:用Log4j替换System.out.println,让调试信息可追溯、可分级、可归档

6.1 为什么System.out.println在生产环境是灾难

该系统所有DAO层都用System.out.println("SQL executed: " + sql)打印日志,问题在于:

  • 控制台日志无法持久化,Tomcat重启后全部丢失;
  • 无法按级别过滤(DEBUG/INFO/WARN/ERROR),调试时满屏INFO干扰关键ERROR;
  • 无线程标识,多用户并发时日志混杂,分不清哪条是张三的订单日志。

6.2 三步集成Log4j 1.2.17(兼容JDK 1.6)

第一步:添加JAR包
下载log4j-1.2.17.jar,放入WebRoot/WEB-INF/lib/,删除原有commons-logging.jar(避免桥接冲突)。

第二步:配置log4j.properties
在src/目录下新建log4j.properties:

# 根日志器设置为INFO级别,输出到dailyRollingFileAppender log4j.rootLogger=INFO, dailyRollingFile # 定义dailyRollingFileAppender log4j.appender.dailyRollingFile=org.apache.log4j.DailyRollingFileAppender log4j.appender.dailyRollingFile.File=${catalina.home}/logs/agri-shop.log log4j.appender.dailyRollingFile.DatePattern='.'yyyy-MM-dd log4j.appender.dailyRollingFile.layout=org.apache.log4j.PatternLayout log4j.appender.dailyRollingFile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1} - %m%n # 为DAO层单独设置DEBUG级别(仅开发时启用) log4j.logger.com.agri.dao=DEBUG

第三步:改造DAO类
以UserDAO.java为例:

// 删除所有 System.out.println(...) // 添加Log4j实例 private static final Logger logger = Logger.getLogger(UserDAO.class); // 在addUser方法中 logger.debug("Executing INSERT INTO user(username, password, email, phone) VALUES(?, ?, ?, ?)"); logger.info("New user registered: " + user.getUsername());

6.3 日志分级实战:让“订单超时未支付”自动告警

该系统订单状态为STATUS_WAIT_PAY超过30分钟应自动取消,但原始代码无此逻辑。我们用Log4j的Level机制实现轻量监控:

// 在OrderService.checkTimeoutOrders()中 List<Order> timeoutOrders = OrderDAO.getTimeoutOrders(30); // 查30分钟前创建的待支付订单 for (Order order : timeoutOrders) { try { OrderDAO.cancelOrder(order.getOrderId()); logger.warn("Auto-cancelled timeout order: " + order.getOrderId() + ", created at " + order.getCreateTime()); } catch (Exception e) { logger.error("Failed to auto-cancel order " + order.getOrderId(), e); } }

然后在log4j.properties中追加:

# 将WARN及以上日志同时输出到emailAppender(需配置SMTP) log4j.appender.email=org.apache.log4j.net.SMTPAppender log4j.appender.email.Threshold=WARN log4j.appender.email.BufferSize=1 log4j.appender.email.From=agri-shop@localhost log4j.appender.email.To=admin@agri-shop.com log4j.appender.email.Subject=AGRI-SHOP ALERT: Order Timeout Cancelled log4j.appender.email.SMTPHost=localhost log4j.appender.email.layout=org.apache.log4j.PatternLayout log4j.appender.email.layout.ConversionPattern=%d{ISO8601} %p %c{1} - %m%n log4j.appender.email.Threshold=WARN

这样,一旦有订单超时,运维人员邮箱立即收到告警,无需人工巡检日志。

从那以后我每次重构老项目,第一件事就是删光所有System.out.println,换成Log4j——不是为了装专业,而是因为某次线上订单状态错乱,靠grep "Auto-cancelled" catalina.out五分钟定位到定时任务未启动,而同事还在翻三天前的控制台截图。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 13:34:33

强电磁环境下以太网温湿度变送器EMC设计实战

1. 项目概述&#xff1a;为什么在柜体强电磁环境下做以太网温湿度变送器&#xff0c;本身就是一场硬仗我干工业现场仪表设计快十二年了&#xff0c;从最早用RS485串口温湿度模块开始&#xff0c;到后来做CAN总线、再到这两年集中攻坚以太网型传感器&#xff0c;踩过的坑基本都刻…

作者头像 李华
网站建设 2026/10/1 13:33:20

Python+Flask+dlib人脸识别考勤系统实战:从原理到避坑

简介&#xff1a;本资源是一套基于Python、Flask与dlib实现的人脸识别企业考勤管理系统&#xff0c;面向计算机相关专业的毕业设计学生与课程设计学习者&#xff0c;帮助解决考勤场景中人脸识别签到、员工信息管理与后台统计等实际问题。压缩包共416个文件&#xff0c;约103.71…

作者头像 李华
网站建设 2026/10/1 13:32:56

知识蒸馏+增量学习:目标检测模型加类不丢旧类的Python实战

简介&#xff1a;本资源为基于知识蒸馏的目标检测模型增量深度学习方法的Python源码&#xff0c;面向人工智能、计算机视觉方向的学生与开发者&#xff0c;尤其适合正在准备毕设、课程设计或希望深入理解增量学习与知识蒸馏结合方案的学习者。压缩包共476个文件&#xff0c;约5…

作者头像 李华
网站建设 2026/10/1 13:32:35

ARIMA时间序列预测闭环:从平稳性诊断到滚动预测实战

简介&#xff1a;本资源是一套面向数据分析与时间序列预测初学者的ARIMA模型实践代码包&#xff0c;适用于高校统计学、数据科学课程实验及Python机器学习入门者。内容聚焦于自回归积分滑动平均&#xff08;ARIMA&#xff09;模型的完整实现流程&#xff0c;涵盖数据预处理、平…

作者头像 李华
网站建设 2026/10/1 13:32:14

基于可编程表格SDK实现单元格级权限控制与插件架构实践

1. 从"univer"这个关键词说起&#xff1a;它到底解决的是什么问题第一次看到"univer"这个词&#xff0c;很多人会以为是某个大学的名字&#xff0c;或者某个开源项目的代号。实际上&#xff0c;在表格与文档处理这个领域里&#xff0c;univer 代表的是一个…

作者头像 李华