ARTICLE DETAIL

资讯详情

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

绿色农产品销售与配送毕设源码:Servlet+JSP+MySQL全流程解析

绿色农产品销售与配送毕设源码:Servlet+JSP+MySQL全流程解析 简介一套基于Java的绿色农产品销售与配送系统毕业设计源码包面向计算机相关专业的学生与教师尤其适合作为本科毕业设计、课程设计或课程大作业的选题参考可帮助理解农产品从线上销售到线下配送的完整业务流程与实现方式。压缩包总共包含一百九十九个文件大小约五点七四兆字节内容覆盖前端页面、后端逻辑与数据库脚本其中前端涉及Vue、JavaScript、Sass等样式与交互文件后端提供Java源码与SQL数据库脚本并整理有十八份项目开发文档包括需求说明书、框架说明和质量评估材料便于对照学习与二次开发。目前已有约一百八十人浏览学习资源经过测试运行成功可直接导入开发工具部署演示也能在此架构上扩展模块适合初学者进阶或作为项目初期演示版本。1. 绿色农产品销售与配送毕设源码这套 Java 系统包里到底有什么拿到这套绿色农产品销售与配送源码包的时候大多数人第一反应是找 README、导入数据库、点启动。但把它当成一个完整项目来拆你会看到一条非常标准的 Java Web 课设主线Servlet JSP MySQL没有 Spring Boot 那层自动装配挡在面前请求怎么走、权限怎么拦、订单和配送怎么衔接每一行都能在答辩时讲出原理。这套源码包适合两类人一类是正在做 Java 课设、实训或毕业设计的学生不想只交一个“能登录的商品列表”另一类是想快速搭一个农产品电商骨架、再往里面填自己业务的开发者。它能给你的是一整条从注册登录、浏览绿色农产品、加购物车、下单生成配送单的闭环外加 sql 数据库脚本和项目开发文档跑起来不是终点能讲清楚才是本事。2. 技术选型与项目骨架Servlet/JSP 还是 Spring Boot决定你的毕设好不好讲拿到源码先别急着导依赖先想明白一个事为什么这套系统用经典 Java EE 三层架构而不是 Spring Boot MyBatis-Plus 一把梭。这个判断直接影响你后面怎么改、怎么答、怎么跟导师解释。2.1 为什么毕设用经典三层架构更划算绿色农产品的销售与配送听起来业务不复杂但落到代码上它需要同时覆盖用户登录态、商品库存、订单状态、配送单生成几条链路。如果用 Spring Boot MyBatis-Plus开发确实快Mapper 接口一写、ServiceImpl 一补CRUD 半小时做完。但问题也出在这里答辩时导师问“你这个配送单生成的事务是怎么控制的”你不能只回答“加了 Transactional 注解”。这套源码用 Servlet JSP DAO 的写法反而是更适合学习和答辩的选择。每一层都看得到——JSP 负责页面渲染Servlet 接收请求和做参数校验Service 层写业务判断DAO 层用原生 JDBC 操作 MySQL。数据怎么从浏览器落到数据表、再被查出来回显没有任何黑匣子。我一般会建议拿到源码后先画一张分层图把三层架构和请求流向标清楚。这张图比代码本身更能帮你在答辩时稳住场面。如果导师硬要求用 Spring Boot 重写也不要慌DAO 层换成 MyBatis 的 MapperService 层逻辑基本不用动那又是一篇“基于 Spring Boot 的改造”素材。对比一下两种方案的成本对比项经典三层 Servlet/JSPSpring Boot MyBatis-Plus学习曲线平缓能看到每一步陡峭自动配置很多答辩追问压力低原理都能答高容易问到底层机制事务控制手动 try-catch 控制Transactional 声明式依赖复杂度一个 Tomcat 就够Maven 全家桶改造空间可迁 Spring Boot难退回经典写法2.2 从请求到响应一次下单操作在源码包里如何串起来这套系统的请求流转很典型我拆一个“用户提交订单”的链路给你看。用户在购物车页面点“提交订单”浏览器向 CartServlet 发一个 POST 请求CartServlet 拿到 session 里的用户 id 和购物车商品列表先校验库存校验通过后调 CartService 里的 createOrder 方法CartService 里再分别调 OrderDao 和 ProductDao往订单表插一条记录、扣掉商品库存最后把订单 id 返回给 JSP 页面跳转到配送信息填写页。这个链路里项目开发文档一般会把每一步的表单参数、Servlet 映射路径、跳转页面都列在需求说明书里。拿到源码后我建议你做一件事把 web.xml 或者 WebServlet 注解里的 URL 映射抄到一张纸上然后用箭头把页面之间怎么跳的连起来。下面是一个典型的 Servlet 处理骨架注意看方法分工和参数获取方式WebServlet(/order/submit) public class OrderServlet extends HttpServlet { private OrderService orderService new OrderService(); Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 从 session 里拿当前登录用户拿不到说明会话过期 HttpSession session request.getSession(false); if (session null || session.getAttribute(user) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } // 2. 手动接收表单参数并做基础校验 String receiverName request.getParameter(receiverName); String receiverPhone request.getParameter(receiverPhone); String receiverAddress request.getParameter(receiverAddress); if (receiverName null || receiverName.trim().isEmpty()) { request.setAttribute(errorMsg, 收货人姓名不能为空); request.getRequestDispatcher(/order/confirm.jsp).forward(request, response); return; } // 3. 获取购物车数据交给 Service 层处理 User user (User) session.getAttribute(user); MapInteger, CartItem cart (MapInteger, CartItem) session.getAttribute(cart); boolean success orderService.createOrder(user.getId(), cart, receiverName, receiverPhone, receiverAddress); // ... 后续跳转 } }这里有两个参数细节要记住。第一request.getSession(false)里的false表示“如果没有会话就返回 null”而不是帮你新建一个——很多翻车现场就是用了getSession(true)导致未登录用户也被创建了 session。第二前端传上来的收货信息一定要在 Servlet 层做一次非空校验JSP 里的前端校验是可以被绕过验证的这是必答的安全一问。2.3 项目目录拆解源码包里的 src、WebContent、sql 脚本与开发文档怎么配合一套典型的毕设源码包解压之后通常是“源码 数据库脚本 文档”三大块。源码部分看两个目录。src 目录下按包名组织com.xxx.entity放实体类com.xxx.dao放数据库访问层com.xxx.service放业务逻辑层com.xxx.servlet放控制器。WebContent 目录下是页面资源WEB-INF/web.xml是部署描述符css/js/images放静态资源jsp页面按功能拆成前台页面和后台管理页面。green-agricultural-system/ ├── src/ │ ├── com/edu/entity/ # User, Product, Order, OrderItem, Delivery │ ├── com/edu/dao/ # 各实体对应的 DAO 接口与实现 │ ├── com/edu/service/ # 业务逻辑登录、购物车、订单、配送 │ └── com/edu/servlet/ # 控制层UserServlet, ProductServlet, OrderServlet... ├── WebContent/ │ ├── index.jsp # 首页绿色农产品列表 │ ├── product_detail.jsp # 商品详情 │ ├── cart.jsp # 购物车 │ ├── order_confirm.jsp # 订单确认 配送信息填写 │ ├── admin/ # 后台管理页面 │ └── WEB-INF/web.xml ├── sql/ │ └── green_food.sql # 建库建表 基础数据 └── 项目开发文档/ ├── 需求说明书.md └── 数据库设计说明书.md这里我要多说一句拿到源码第一步不是改代码而是先读数据库设计说明书。它决定了你后面改业务要动哪些表。比如这套系统核心表是用户表、商品表、订单表、订单明细表和配送单表配送单独成表这意味着“销售”和“配送”是两个被独立设计的业务模块后面加物流状态、配送员分配都有位置可放。3. 数据库设计与 SQL 脚本五张核心表把“绿色”和“配送”两个卖点落进数据结构这个项目的名字里带了“绿色”和“配送”两个关键词。绿色体现在商品的品类字段和产地溯源信息上配送体现在订单下单后生成独立配送单的流程上。搞懂表结构你就搞懂了这套系统一半的架构。3.1 E-R 关系用户、商品、订单、配送单之间怎么连线先画清楚实体关系再动手改代码。这套系统至少有五个核心实体。用户和订单是一对多一个用户能下多张订单商品和订单是多对多但通过订单明细表把多对多拆成两个一对多一个订单含多个明细一个明细对应一个商品订单和配送单是一对一下单成功后立刻生成一条配送记录配送单里存收货人、地址、配送状态和预计送达时间。为什么配送单独建表而不是直接塞在订单表里因为配送环节需要记录物流状态流转待接单、配送中、已签收还可能要记录配送员或车辆信息。这些字段如果全放在订单表里订单表会越来越膨胀而且“销售”和“配送”两个模块的业务节奏不同分表更利于后面扩展。如果导师问表设计亮点这就是一个现成的回答角度。3.2 核心建表 SQL从商品表到配送单表数据库脚本导入这件事翻车率其实很高。最常见的问题就是字符集不对页面显示问号。这套系统的 sql 脚本通常会把建库、建表和初始数据写在同一个文件里导入时注意用 utf8mb4而不是老的 utf8。MySQL 8.0 默认字符集是 utf8mb4但如果脚本里写的是 latin1导入照样乱码。-- 建库字符集必须用 utf8mb4兼容表情符号和生僻农产品名 CREATE DATABASE IF NOT EXISTS green_food_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE green_food_db; -- 用户表前台顾客和管理员共用用 role 字段区分 CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 用户ID, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录名, password VARCHAR(64) NOT NULL COMMENT 密码建议存 MD5 或加盐哈希, role TINYINT NOT NULL DEFAULT 1 COMMENT 角色1-顾客2-管理员, phone VARCHAR(20), address VARCHAR(200), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB COMMENT用户表; -- 商品表绿色农产品的核心信息 CREATE TABLE t_product ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 商品ID, product_name VARCHAR(100) NOT NULL COMMENT 商品名称如高山有机绿茶, category VARCHAR(50) COMMENT 商品分类蔬菜、水果、粮油、茶叶, origin_address VARCHAR(100) COMMENT 产地体现绿色卖点, price DECIMAL(10,2) NOT NULL COMMENT 销售单价, stock INT NOT NULL DEFAULT 0 COMMENT 库存数量, description TEXT COMMENT 绿色认证与产品描述, image_path VARCHAR(255) COMMENT 商品图片路径, status TINYINT DEFAULT 1 COMMENT 上下架状态1-上架0-下架 ) ENGINEInnoDB COMMENT绿色农产品商品表; -- 订单表记录一次购买的总体信息 CREATE TABLE t_order ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 订单ID, user_id INT NOT NULL COMMENT 下单用户ID, order_no VARCHAR(32) NOT NULL UNIQUE COMMENT 订单编号可带日期前缀, total_price DECIMAL(10,2) NOT NULL COMMENT 订单总金额, status TINYINT NOT NULL DEFAULT 0 COMMENT 订单状态0-待支付1-已支付2-已取消, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, pay_time DATETIME NULL, CONSTRAINT fk_order_user FOREIGN KEY (user_id) REFERENCES t_user(id) ) ENGINEInnoDB COMMENT订单表; -- 订单明细表把订单和商品的多对多拆开 CREATE TABLE t_order_item ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL COMMENT 所属订单ID, product_id INT NOT NULL COMMENT 商品ID, product_name VARCHAR(100) COMMENT 冗余商品名称防止商品改名影响历史订单, price DECIMAL(10,2) NOT NULL COMMENT 下单时单价快照, quantity INT NOT NULL COMMENT 购买数量, CONSTRAINT fk_item_order FOREIGN KEY (order_id) REFERENCES t_order(id), CONSTRAINT fk_item_product FOREIGN KEY (product_id) REFERENCES t_product(id) ) ENGINEInnoDB COMMENT订单明细表; -- 配送单表销售与配送两个模块的衔接点 CREATE TABLE t_delivery ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL UNIQUE COMMENT 与订单一对一, receiver_name VARCHAR(50) NOT NULL COMMENT 收货人, receiver_phone VARCHAR(20) NOT NULL, receiver_address VARCHAR(200) NOT NULL, delivery_status TINYINT NOT NULL DEFAULT 0 COMMENT 配送状态0-待接单1-配送中2-已签收, delivery_time DATETIME NULL COMMENT 实际送达时间, CONSTRAINT fk_delivery_order FOREIGN KEY (order_id) REFERENCES t_order(id) ) ENGINEInnoDB COMMENT配送单表;上述建表逻辑里有三个细节值得说道。第一t_order_item里冗余了product_name和price两个字段这是个刻意设计——用户下单后商品改价或下架订单明细不能跟着变所以做了“快照”。第二订单表和配送单表都挂着状态字段后面代码里就是用数字去判断流程走向的。第三表名统一加t_前缀这是一些数据库设计文档里的规范让表名和关键字如 order区分开避免ORDER在 SQL 里的语义冲突。3.3 状态字段设计为什么订单状态和配送状态用 int 不用 char看表结构你会发现凡是状态字段都是 TINYINT 数字类型注释里写明了每个数字代表什么。这是 Java Web 课设里一个容易被忽视但值得展开讲的设计决策。用int存状态代码里就是一个switch或if判断用char存状态比如PAID、CANCELED代码得先转换成枚举再判断多一道工序而且拼 SQL 时还要注意引号。这个系统的订单状态流转一般是这样用户确认订单后先置为“待支付”支付成功后改成“已支付”未支付前用户可以取消取消后订单状态为“已取消”。配送单的状态独立流转订单支付成功时插入一条“待接单”的配送记录配送员操作后改为“配送中”最后变成“已签收”。注意订单里没有“待配送”之类的字段配送的事情由配送单表单独管业务边界清楚。那么问题来了如果用户下单后不支付配送单会不会生成看代码逻辑正常设计是“已支付”才生成配送单下单只生成订单记录。如果源码里下单同时生成配送单说明作者没有区分支付和配货两个时间点你可以把这个当作一个改进点写进论文里属于真实的产品逻辑缺陷不算鸡蛋里挑骨头。4. 核心功能代码链路从注册登录到配送单生成每一步在 Java 层怎么实现表结构定了再看 Java 层怎么把这些表用起来。这一章我按业务主链路拆解登录拦截、购物车、订单与配送单生成。这三块覆盖了这套系统八成以上的核心代码逻辑。4.1 登录与 Session 拦截Filter 过滤器的两种写法坑几乎所有 Java Web 课设都需要一个“未登录用户不能访问购物车和订单页面”的过滤器。常见做法是写一个AuthFilter实现javax.servlet.Filter接口在doFilter里判断 session 是否包含用户对象。如果没登录重定向到登录页如果已登录放行继续走后续链路。import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; WebFilter(/*) public class AuthFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req (HttpServletRequest) request; HttpServletResponse resp (HttpServletResponse) response; HttpSession session req.getSession(false); // 白名单集合登录页、注册页、首页和静态资源直接放行 String uri req.getRequestURI(); String contextPath req.getContextPath(); String path uri.substring(contextPath.length()); boolean isPublicPage path.equals(/login.jsp) || path.equals(/register.jsp) || path.equals(/index.jsp) || path.startsWith(/css/) || path.startsWith(/js/) || path.startsWith(/images/) || path.equals(/user/login) || path.equals(/user/register); if (isPublicPage) { chain.doFilter(request, response); return; } // 私密资源session 里没有 user 就拦住 if (session null || session.getAttribute(user) null) { resp.sendRedirect(contextPath /login.jsp); return; } chain.doFilter(request, response); } }这段代码的思路是维护一个白名单把所有不需要登录就能访问的 URL 都列出来然后对剩余路径做校验。两个容易踩的坑一是WebFilter(/*)会把静态资源一起拦截所以白名单里必须包含/css/、/js/、/images/这些前缀否则页面样式全丢二是在被人直接传/WEB-INF/路径时容器本身会拒绝访问但如果你的 JSP 放在了WEB-INF外面过滤器还得防止用户绕过登录直接访问原始 JSP 文件。另一种写法是给私密 JSP 加登录检查片段但那太分散不如一个 Filter 管全局答辩时也更有“系统设计”的味道。4.2 购物车实现用 Session 装 Map 还是用数据库购物车表购物车是该系统一个典型业务点也是两种实现的岔路口。第一种常见做法用 Session 里的MapInteger, CartItem当临时购物车加购、删购物项、改数量都在内存里操作点“提交订单”时一次性把数据落到订单表。第二种做法建一张购物车表用户每次加购都写库提交订单时从库里读出来。毕设源码里用 Session 存购物车更常见因为它实现简单、演示效果好而且不用考虑“用户关闭浏览器后购物车残留”的清理问题。但它在并发场景下会有丢数据风险刷新页面丢购物车、长时间停留后 session 过期清空这些都可以写成系统改进点。我给的判断标准是如果项目文档强调“性能优化”就用数据库购物车如果强调“业务流程闭环”Session 版本完全够用。public class CartService { // 加购把商品放进 Session 的 cartMap 里 public void addToCart(Product product, int quantity, HttpSession session) { // 从 Session 取出购物车第一次访问时创建 MapInteger, CartItem cartMap (MapInteger, CartItem) session.getAttribute(cart); if (cartMap null) { cartMap new HashMap(); session.setAttribute(cart, cartMap); } // 如果购物车已有该商品累加数量否则新建一个条目 CartItem item cartMap.get(product.getId()); if (item ! null) { item.setQuantity(item.getQuantity() quantity); } else { cartMap.put(product.getId(), new CartItem(product, quantity)); } } // 计算购物车总价 public double calculateTotal(MapInteger, CartItem cartMap) { double total 0.0; if (cartMap null) { return total; } for (CartItem item : cartMap.values()) { total item.getProduct().getPrice() * item.getQuantity(); } return total; } }这里有个 Java 集合层面的细节需要讲给答辩老师听Map的 key 用商品 idvalue 里同时存商品对象和数量。为什么不直接存数量因为结算页面要展示商品名称、单价、图片这些信息都来自Product对象。如果只存数量每次渲染购物车都要重新查库不划算。把整个Product对象放进CartItem页面直接取属性即可这是一个典型的以空间换时间的处理。4.3 订单与配送单生成库存扣减与状态机生成订单是整套系统中事务性最强的一段逻辑。一次createOrder至少要完成校验库存、插入订单表、批量插入订单明细、扣减库存、生成配送单。这五步必须“要么全成功要么全失败”这就是 ACID 里的原子性。经典三层架构里没有 Spring 帮忙管理事务常用的方法是Connection手动控制关闭自动提交在 catch 块里rollback()全部完成后commit()。public class OrderService { private OrderDao orderDao new OrderDao(); private ProductDao productDao new ProductDao(); private DeliveryDao deliveryDao new DeliveryDao(); private CartItemDao cartItemDao new CartItemDao(); /** * core method * param userId 当前登录用户ID * param cartMap 购物车key 为商品ID */ public boolean createOrder(int userId, MapInteger, CartItem cartMap, String receiverName, String receiverPhone, String receiverAddress) { // 获取数据库连接后手动管理事务核心是 conn.setAutoCommit(false) Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 1. 生成总订单拿到新增订单ID double totalPrice calculateTotal(cartMap); Order order new Order(); order.setUserId(userId); order.setTotalPrice(totalPrice); order.setStatus(0); // 待支付 order.setOrderNo(generateOrderNo()); int orderId orderDao.insert(conn, order); // 2. 遍历购物车逐条插入明细并扣库存 for (CartItem item : cartMap.values()) { Product product item.getProduct(); int quantity item.getQuantity(); // 2.1 扣库存前再查一次库存防止超卖 int stock productDao.getStockById(conn, product.getId()); if (stock quantity) { throw new RuntimeException(商品[ product.getProductName() ]库存不足); } // 2.2 插入订单明细 cartItemDao.insert(conn, orderId, product, quantity); // 2.3 扣减库存SQL里带 WHERE stock ? 更安全 int updated productDao.deductStock(conn, product.getId(), quantity); if (updated 0) { throw new RuntimeException(商品[ product.getProductName() ]库存扣减失败); } } // 3. 生成配送单状态为待接单(0) Delivery delivery new Delivery(); delivery.setOrderId(orderId); delivery.setReceiverName(receiverName); delivery.setReceiverPhone(receiverPhone); delivery.setReceiverAddress(receiverAddress); delivery.setDeliveryStatus(0); deliveryDao.insert(conn, delivery); // 全流程结束提交事务 conn.commit(); return true; } catch (Exception e) { // 出了问题整体回滚库存和订单都不会留下半截数据 if (conn ! null) { try { conn.rollback(); } catch (Exception ex) { ex.printStackTrace(); } } throw new RuntimeException(订单创建失败已回滚 e.getMessage(), e); } finally { DBUtil.closeConnection(conn);; } } }这里有几个关键手法值得截图保存。第一扣库存的 SQL 写成UPDATE t_product SET stock stock - ? WHERE id ? AND stock ?这会同时完成“校验 扣减”省掉一次查询请求第二每次操作都传同一个Connection进去保证所有 DAO 方法在同一个事务里第三订单编号建议拼接时间戳和随机数比如20240601103000001不能直接用自增 id 当订单号发给客户。这三个点任何一个能在答辩时主动讲出来导师都会觉得你真正理解了业务落库的细节。5. 部署与排查把系统跑起来的全流程和四个高频翻车点环境问题占毕设项目“跑不起来”原因的七成以上。这一章把从零到能访问首页的完整步骤理一遍并提供一套排查办法。注意很多教程会引导你装 SQL Server 2025 或者 Visual Studio Installer那套工具链是 .NET 方向用的这套绿色农产品系统连的是 MySQL别装错方向。5.1 环境准备JDK、Tomcat、MySQL 的版本搭配与 Java 环境变量配置这套源码的部署对版本有隐性要求核心是 jakarta 命名空间的问题。JDK 8 配套 Tomcat 8.5 或 Tomcat 9代码里的javax.servlet包能正常编译如果你装的是新版 Tomcat 10Servlet 包名变成了jakarta.servlet旧源码基本会报“找不到类”的编译错误。所以环境版本不是越新越好而是要和源码匹配。Java 环境变量配置是高频卡点很多人的java -version能出结果但 Tomcat 启动后报ClassNotFoundException原因是JAVA_HOME没配对。标准做法是三个变量新建JAVA_HOME指向 JDK 安装目录注意别指到bin目录PATH里新增%JAVA_HOME%\binCLASSPATH配.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar。配置完最好在命令行里确认一次echo %JAVA_HOME% java -version javac -version如果你装了多个 JDKjava -version显示的版本和JAVA_HOME不一致多半是 PATH 里有一个更靠前的 Java 路径把你拉偏了。这时检查系统变量和用户变量里有没有残留的 JDK 路径把多余的删掉。5.2 SQL 导入与数据库连接配置避免 2003、1045 和中文乱码数据库脚本导入失败是另一个重灾区。打开sql/green_food.sql文件看第一行通常会有CREATE DATABASE那就没必要手动新建库直接一行命令导入进去。如果源码包只给了建表语句没有建库语句那就先手动建库再指定库名导入。mysql -uroot -p --default-character-setutf8mb4 sql/green_food.sql导入成功后去改源码里的数据库连接配置。Java Web 项目里常见的写法是src/db.properties或src/jdbc.properties内容大致如下jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/green_food_db?useUnicodetruecharacterEncodingutf8mb4useSSLfalseserverTimezoneAsia/Shanghai jdbc.usernameroot jdbc.password123456三个参数说明useUnicodetruecharacterEncodingutf8mb4保证写入库的中文不出乱码serverTimezoneAsia/Shanghai是 MySQL 8.0 连接必须加的否则时间字段会差 8 小时useSSLfalse是为了省掉本地连接的 SSL 握手报警告。密码不要写错本地 MySQL 的 root 密码不是默认的。如果你用 DBeaver 这类图形工具连数据库连上之后发现看不到表先检查左侧树里选中的 schema 是不是green_food_dbDBeaver 连 MySQL 有时会默认挂在别的库下表和表都找错位置。5.3 四个高频排查项现象、原因、解决500 错误 ClassNotFoundException驱动 jar 包没进 WEB-INF/lib。现象是 Tomcat 启动后访问任意带数据库操作的页面直接 500。原因多半是 MySQL 驱动 jar 放在了项目的构建路径里但没复制到WebContent/WEB-INF/lib目录。解决方法是把mysql-connector-java-x.x.x.jar拷贝进该目录并重新发布项目。Could not open JDBC Connection for transaction 或 1045 Access denied。现象是服务能启动但一操作数据库就报连接失败。原因基本是db.properties的密码或者账号不对。解决方法是先单独用命令行mysql -uroot -p123456验证凭据再核对 properties 里是否多了空格jdbc.password123456等号后面多了空格是最隐蔽的翻车点。HTTP 404请求路径对不上。现象是点页面的按钮跳转 404。原因通常是 Servlet 上的 WebServlet 注解路径和 JSP 里表单的 action 路径不一致或者 web.xml 版本不匹配。解决办法是把WebServlet(/product/list)的路径和页面里action${pageContext.request.contextPath}/product/list对齐特别是不要漏掉request.getContextPath()。中文乱码页面显示问号或火星文。现象是数据库里的中文正常但 JSP 页面显示乱码。原因有两条线JSP 页面头部缺了% page contentTypetext/html;charsetUTF-8 %或者数据库连接串没带characterEncodingutf8mb4。解决时还要检查 MySQL 端表结构和连接工具两边的字符集设置运行SHOW VARIABLES LIKE character%;确认。还有一个很现实的坑启动 Tomcat 时提示端口被占用。遇到这种情况先在命令行查占用端口的进程然后决定是换端口还是杀进程。# Windows下查8080端口占用并杀掉占用进程 netstat -ano | findstr :8080 taskkill /PID 进程号 /F如果是你自己之前启动的 Tomcat 没关干净直接去任务管理器把 java 进程结束掉比改端口更省事。端口问题的处理就一条原则尽早解决别硬等。6. 把毕设源码变成自己的二次开发切入点与答辩验收清单源码跑通不是终点重点是让它变成你能讲清楚、能展示亮点的作品。这里给出三个二次开发切入点和一份验收清单照着做一遍就知道项目是不是真的没毛病。切入点一把支付环节做成闭环。这套源码多半是模拟支付——点一下“已支付”按钮就直接改订单状态。你可以接支付宝沙箱环境在order_confirm.jsp页面生成支付二维码支付回调里再更新订单状态为“已支付”。这个改动涉及支付接口对接、回调验签、状态幂等问题是答辩时最能镇场的一块。切入点二给商品补搜索和分页。农产品系统数据量不大但你可以用 SQL 的LIMIT做分页用LIKE做模糊搜索再在页面上加一个按分类筛选的下拉框。切入点三给配送管理加一个后台页面。管理员能看到所有配送单按状态筛选点击“配送完成”按钮更新delivery_status为已签收。这个功能直接用到你前面对配送单表的理解比在用户端加功能更贴合系统名里的“配送系统”。做完改动验收别只点一遍流程要按角色走完整链路注册新用户如果源码有注册功能→ 苗木登录 → 加购两个商品 → 提交订单 → 模拟支付 → 去后台看到订单和配送单 → 修改配送状态 → 用户端看到订单状态变化。中间刻意制造异常场景库存不足时下两倍数量的订单看会不会报错不登录直接访问购物车页面看会不会被过滤器拦住。这套系统真正的价值不在“能跑”在于它的业务闭环恰好覆盖了 Java Web 的核心考点Servlet 生命周期、Session 管理、Filter 过滤器、JDBC 事务、SQL 多表查询。把这些点一个一个吃透这个项目放进简历面试里被问 Java 八股时你反而比那些只做过玩具 CRUD 的人更有话讲。我做毕设时最亏的一件事就是拿到源码后先改功能后读文档结果压着截稿日才发现自己加的需求和数据表设计对不上。希望你拿到这套绿色农产品销售与配送源码时先花两小时把表结构和 Servlet 映射理清楚再动手改第一个功能后面会顺手很多。希望帮到你。本文还有配套的精品资源点击获取
返回列表