news 2026/9/12 2:07:36

Java Web电商系统最小可行原型:Servlet+JSP+JDBC实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java Web电商系统最小可行原型:Servlet+JSP+JDBC实战

简介:本资源是一套面向高校计算机专业本科生的Java毕业设计/课程设计实战项目,聚焦家用电器在线销售系统的全流程开发实践,适用于Java Web技术栈入门到进阶的学习者。项目采用JSP+Servlet+Java+MySQL技术组合,完整实现管理员后台(含用户、商品分类、品牌、订单、留言等10类管理模块)与用户前台(首页、购物车、订单管理、资讯浏览等)双端功能,覆盖B/S架构典型业务场景。压缩包为ZIP格式,大小119.19MB,包含完整可运行源码、详细说明文档、毕业论文(LW)、答辩PPT及系统演示视频,各类文件协同支撑从编码、部署到汇报的全环节需求。目前已有79人学习下载,资源结构清晰、模块划分明确,特别适合用于课程设计参考、毕设选题复用或Java Web开发能力系统性训练。

1. 这不是“又一个Java毕设”,而是一套可跑通、可调试、可延展的家用电器销售系统最小可行原型

你下载到的这个.zip文件,表面看是高校常见的“Java+JSP+MySQL”课程设计压缩包,但真正有价值的部分,是它把电商类Web应用的核心链路——用户浏览商品、加入购物车、下单支付(模拟)、后台管理——用最精简却完整的 Java Web 技术栈串了起来。它不依赖 Spring Boot 自动配置,也不用 Vue/React 前端框架,而是用原生 JSP + Servlet + JDBC 搭建,这意味着:你能一眼看清 HTTP 请求如何从浏览器抵达ProductServlet,SQL 如何通过PreparedStatement绑定参数插入订单表,session 如何在CartBean中维护购物车状态。对刚学完 JDBC 和 Servlet 的开发者,这是比任何“Spring Boot 快速入门”更扎实的实战入口;对准备 Java 面试的求职者,它覆盖了request.getParameter()session.setAttribute()connection.prepareStatement()等高频考点的实际调用上下文;对需要快速验证 MySQL 表结构与 Java 实体映射关系的工程师,它的product.sqlProduct.java就是现成的对照样本。别急着解压就跑,先理清这套架构的边界:它不包含真实支付网关,不处理高并发库存扣减,但所有数据库事务控制(如下单时库存更新与订单插入的原子性)都已用conn.setAutoCommit(false)显式实现——这才是理解“Java Web 基础能力”的黄金切口。

2. 用 Tomcat 9 + JDK 8 搭建运行环境:避开 classpath 和编码两大经典陷阱

2.1 环境版本选择与验证逻辑

项目源码中web.xml<web-app>根标签声明了version="2.5",这直接锁定了 Servlet 规范兼容范围。必须使用 Tomcat 9.x(推荐 9.0.85)或 Tomcat 8.5.x,因为 Tomcat 10+ 默认采用 Jakarta EE 命名空间(jakarta.servlet.*),而本项目所有import javax.servlet.*会编译失败。JDK 版本需严格匹配:源码中Product.java使用了@Override注解修饰toString()方法,且无 Lambda 表达式,JDK 8 是最低安全线;若用 JDK 17,javac会因sourcetarget编译选项缺失报错。验证方式不是看控制台输出,而是检查WEB-INF/classes/下是否生成了Product.class

# 进入项目根目录(解压后) cd webapp/WEB-INF/classes ls -l com/example/bean/Product.class # 若文件存在且大小 > 0,则编译成功;若报 "No such file",说明 javac 未正确执行

2.2 解决 JSP 页面中文乱码的三步硬核操作

JSP 中文乱码常被归咎于页面编码,实则根源在请求解析链。本项目login.jsp提交用户名时若显示????,需同步调整三处:

  1. JSP 页面声明:在login.jsp顶部添加pageEncoding="UTF-8"并设置响应头
    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% response.setContentType("text/html;charset=UTF-8"); %>
  2. Servlet 请求解码:在LoginServlet.javadoPost方法开头强制设置请求编码
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); // 关键!必须在 getParameter() 前调用 String username = request.getParameter("username"); // 此时 username 才是正确中文 // ... 后续逻辑 }
  3. Tomcat 配置文件修正:编辑conf/server.xml,在<Connector>标签中增加URIEncoding="UTF-8"属性
    <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8" /> <!-- 此行必须添加 -->

提示:若跳过第 2 步,仅靠 JSP 页面声明,request.getParameter()仍会返回 ISO-8859-1 编码的字节流,new String(username.getBytes("ISO-8859-1"), "UTF-8")是临时补救,但治标不治本。

2.3 MySQL 连接驱动与 URL 的精确配置

项目DBUtil.java中的 JDBC URL 形如jdbc:mysql://localhost:3306/electronic?useUnicode=true&characterEncoding=utf8,此写法在 MySQL 8.0+ 会触发警告。必须升级为utf8mb4并显式指定时区

// 修改 DBUtil.java 中 getConnection() 方法内的 URL String url = "jdbc:mysql://localhost:3306/electronic?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false";

对应地,MySQL 数据库及表需执行以下命令确保字符集一致:

-- 创建数据库时指定字符集 CREATE DATABASE electronic CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 修改现有表字符集(若已存在) ALTER TABLE product CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ALTER TABLE user CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

注意:characterEncoding=utf8是历史写法,MySQL 官方文档明确要求使用utf8mb4以支持 emoji 及四字节 UTF-8 字符;serverTimezone参数缺失会导致java.sql.SQLException: The server time zone value 'XXX' is unrecognized错误。

3. 调试核心业务流程:从商品列表到模拟下单的完整数据流追踪

3.1 商品列表页(product_list.jsp)的数据加载机制

该页面不通过 AJAX 加载,而是由ProductServletdoGet中完成全量查询并转发至 JSP。关键在于理解request.setAttribute()如何将 Java 对象暴露给 JSP EL 表达式:

// ProductServlet.java protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { List<Product> productList = new ProductDAO().findAll(); // 查询全部商品 request.setAttribute("productList", productList); // 将 List 存入 request 作用域 request.getRequestDispatcher("product_list.jsp").forward(request, response); // 转发 }

product_list.jsp中,EL 表达式${productList}直接访问该 List,JSTL<c:forEach>标签遍历渲染:

<c:forEach items="${productList}" var="p"> <div class="product-item"> <h3>${p.name}</h3> <p>价格:¥${p.price}</p> <a href="cart_add.jsp?id=${p.id}">加入购物车</a> </div> </c:forEach>

提示:若页面显示为空白,首先检查ProductDAO.findAll()是否返回空集合——在ProductDAO.java的 SQL 查询语句后添加日志:System.out.println("SQL executed: " + sql);,再确认electronic数据库中product表是否有数据。

3.2 购物车添加(cart_add.jsp)的 session 状态管理

本项目购物车未用数据库持久化,而是将CartBean对象存入HttpSessionCartBean是一个 POJO,其addProduct()方法通过HashMap<Integer, Integer>记录商品 ID 与数量映射:

public class CartBean { private Map<Integer, Integer> items = new HashMap<>(); // key: product_id, value: quantity public void addProduct(int productId, int quantity) { items.put(productId, items.getOrDefault(productId, 0) + quantity); } public Map<Integer, Integer> getItems() { return items; } }

cart_add.jsp通过session.getAttribute("cart")获取 cart 实例,若为 null 则新建:

<% CartBean cart = (CartBean) session.getAttribute("cart"); if (cart == null) { cart = new CartBean(); session.setAttribute("cart", cart); } int productId = Integer.parseInt(request.getParameter("id")); cart.addProduct(productId, 1); response.sendRedirect("cart_view.jsp"); %>

注意:session.setAttribute("cart", cart)必须在addProduct()之后执行,否则新添加的商品不会被保存;response.sendRedirect()触发重定向,避免用户刷新页面时重复提交。

3.3 模拟下单(order_submit.jsp)的事务控制实现

下单操作涉及两个关键表:orders(主订单)和order_item(订单明细)。OrderServlet.java中的doPost方法用 JDBC 事务保证数据一致性:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 关闭自动提交 // 1. 插入 orders 表 String orderSql = "INSERT INTO orders(user_id, total_amount, create_time) VALUES(?, ?, NOW())"; PreparedStatement ps1 = conn.prepareStatement(orderSql, Statement.RETURN_GENERATED_KEYS); ps1.setInt(1, userId); ps1.setDouble(2, totalAmount); ps1.executeUpdate(); // 2. 获取生成的 order_id ResultSet rs = ps1.getGeneratedKeys(); rs.next(); int orderId = rs.getInt(1); // 3. 插入 order_item 表(循环插入每条明细) String itemSql = "INSERT INTO order_item(order_id, product_id, quantity, price) VALUES(?, ?, ?, ?)"; PreparedStatement ps2 = conn.prepareStatement(itemSql); for (CartItem item : cart.getItems()) { ps2.setInt(1, orderId); ps2.setInt(2, item.getProductId()); ps2.setInt(3, item.getQuantity()); ps2.setDouble(4, item.getPrice()); ps2.addBatch(); } ps2.executeBatch(); conn.commit(); // 手动提交事务 request.setAttribute("message", "订单创建成功!"); } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException ex) {} } request.setAttribute("message", "订单创建失败:" + e.getMessage()); } finally { DBUtil.closeConnection(conn); } request.getRequestDispatcher("order_result.jsp").forward(request, response); }

提示:若ps1.getGeneratedKeys()返回空结果,检查orders表的id字段是否设置为AUTO_INCREMENTps2.addBatch()后必须调用ps2.executeBatch(),否则明细数据不会写入。

4. 优化与排错:解决部署后 404、500 及数据库连接超时三大高频问题

4.1 404 错误的精准定位路径

当访问http://localhost:8080/electronic/product_list.jsp报 404,不要盲目重启 Tomcat。按顺序检查:

  1. 项目部署路径是否正确:Tomcatwebapps目录下必须存在electronic文件夹(即项目 WAR 包解压后的根目录名),且electronic/WEB-INF/web.xml文件存在;
  2. URL 路径大小写敏感:Linux 系统下product_list.jspProduct_List.jsp是不同文件,确认浏览器地址栏输入与实际文件名完全一致;
  3. Servlet 映射是否生效:检查web.xmlProductServlet<servlet-mapping>
    <servlet-mapping> <servlet-name>ProductServlet</servlet-name> <url-pattern>/product</url-pattern> <!-- 访问 /product 即触发该 Servlet --> </servlet-mapping>
    若想通过/product_list.jsp直接访问,该文件必须位于webapp/目录下,而非WEB-INF/内(WEB-INF下资源受容器保护,无法直接访问)。

4.2 500 错误的堆栈分析法

服务器返回 500 时,Tomcatlogs/catalina.out是第一手线索。典型错误如:

  • java.lang.ClassNotFoundException: com.mysql.jdbc.Driver:MySQL 5.1~5.7 驱动类名为com.mysql.jdbc.Driver,而 MySQL 8.0+ 改为com.mysql.cj.jdbc.Driver。修改DBUtil.java
    // MySQL 8.0+ 必须使用新驱动类 Class.forName("com.mysql.cj.jdbc.Driver");
  • java.sql.SQLException: Access denied for user 'root'@'localhost':检查DBUtil.java中的数据库用户名密码是否与 MySQL 实际配置一致,特别注意 root 用户默认可能只允许localhost连接,若 Tomcat 与 MySQL 不在同一机器,需授权:
    CREATE USER 'electronic_user'@'%' IDENTIFIED BY 'your_password'; GRANT ALL PRIVILEGES ON electronic.* TO 'electronic_user'@'%'; FLUSH PRIVILEGES;

4.3 数据库连接池缺失导致的超时问题

项目当前使用DriverManager.getConnection()每次创建新连接,高并发下极易耗尽 MySQL 连接数(默认 151)。必须引入 DBCP 连接池

  1. 下载commons-dbcp-1.4.jarcommons-pool-1.6.jar,放入WEB-INF/lib/
  2. 修改DBUtil.java,用BasicDataSource替代DriverManager
    public class DBUtil { private static BasicDataSource dataSource = new BasicDataSource(); static { dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/electronic?..."); dataSource.setUsername("root"); dataSource.setPassword("123456"); dataSource.setInitialSize(5); // 初始连接数 dataSource.setMaxActive(20); // 最大活跃连接数 dataSource.setMaxWait(60000); // 获取连接最大等待时间(毫秒) } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); // 从此处获取连接 } }

提示:setMaxActive(20)需根据服务器内存调整,每连接约占用 1MB 内存;setMaxWait设置过小会导致请求排队超时,过大则影响响应速度。

5. 从演示视频到生产级改造:三个可立即落地的增强技巧

5.1 用 CSS Grid 重构商品列表页,替代原始 table 布局

product_list.jsp原始代码用<table>实现商品展示,响应式差且维护成本高。替换为语义化 HTML + CSS Grid:

<!-- product_list.jsp 中替换原有 table 结构 --> <div class="product-grid"> <c:forEach items="${productList}" var="p"> <div class="product-card"> <img src="${p.imagePath}" alt="${p.name}" class="product-img"> <h3 class="product-title">${p.name}</h3> <p class="product-price">¥${p.price}</p> <a href="cart_add.jsp?id=${p.id}" class="btn-add">加入购物车</a> </div> </c:forEach> </div>

配套 CSS(放入css/style.css):

.product-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 20px; padding: 20px; } .product-card { border: 1px solid #e0e0e0; border-radius: 8px; overflow: hidden; transition: box-shadow 0.3s; } .product-card:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.1); } .product-img { width: 100%; height: 180px; object-fit: cover; } .product-title { font-size: 16px; margin: 10px 0 5px; color: #333; } .product-price { font-size: 18px; color: #e74c3c; font-weight: bold; margin: 0 0 15px; } .btn-add { display: inline-block; background: #3498db; color: white; padding: 8px 16px; text-decoration: none; border-radius: 4px; }

提示:grid-template-columns: repeat(auto-fill, minmax(280px, 1fr))实现自动列数适配,手机端单列,桌面端三列,无需媒体查询。

5.2 为管理员登录增加验证码防暴力破解

admin_login.jsp当前无验证码,易被脚本爆破。添加简单数字验证码:

  1. AdminLoginServlet.javadoPost方法开头生成随机 4 位数并存入 session:
    String verifyCode = String.valueOf((int)(Math.random() * 9000 + 1000)); session.setAttribute("verifyCode", verifyCode);
  2. admin_login.jsp表单中添加验证码输入框及图片占位:
    <div class="form-group"> <label>验证码:</label> <input type="text" name="verifyCode" required> <span style="color:red;">${requestScope.verifyError}</span> </div>
  3. 登录验证时比对:
    String inputCode = request.getParameter("verifyCode"); String sessionCode = (String) session.getAttribute("verifyCode"); if (!inputCode.equalsIgnoreCase(sessionCode)) { request.setAttribute("verifyError", "验证码错误"); request.getRequestDispatcher("admin_login.jsp").forward(request, response); return; }

5.3 使用 Log4j2 替代 System.out.println 进行日志分级

项目所有System.out.println()在生产环境无法关闭且无级别区分。引入log4j2.xml

  1. 下载log4j-api-2.20.0.jarlog4j-core-2.20.0.jar放入WEB-INF/lib/
  2. src/main/resources/(或WEB-INF/classes/)下创建log4j2.xml
    <?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <File name="FileAppender" fileName="logs/electronic.log"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </File> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="FileAppender"/> </Root> </Loggers> </Configuration>
  3. 修改ProductDAO.java中的日志:
    import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class ProductDAO { private static final Logger logger = LogManager.getLogger(ProductDAO.class); public List<Product> findAll() { logger.info("开始查询全部商品"); // ... 查询逻辑 logger.debug("查询到 {} 条商品记录", productList.size()); return productList; } }

注意:log4j2.xmlfileName="logs/electronic.log"会自动在 Tomcat 根目录下创建logs文件夹写入日志,无需额外配置路径。

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

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

免费数据恢复软件能救回误删文件吗?原理与实操指南

1. 这类软件到底能不能救回误删的文件&#xff1f;先说结论再聊细节“免费数据恢复软件值得用吗&#xff1f;”——这个问题我每天在技术社区、客户咨询和售后工单里至少看到15次。去年帮一位做短视频的创作者抢救过一块被格式化的移动硬盘&#xff0c;里面存着37个未发布的4K样…

作者头像 李华
网站建设 2026/9/12 2:07:33

AnimeGAN2人脸动漫化实战:从模型原理到ONNX推理与参数调优

简介&#xff1a;基于AnimeGAN2的人脸动漫化实现包&#xff0c;面向深度学习开发者与图像风格迁移爱好者&#xff0c;聚焦真实人脸转动漫风格&#xff0c;覆盖模型结构定义、训练/推理脚本、PyTorch与ONNX格式互转、dlib人脸关键点对齐等环节。压缩包共28个文件&#xff0c;约1…

作者头像 李华
网站建设 2026/9/12 2:05:51

JVM调优实战:内存管理与GC策略详解

1. JVM调优核心概念解析JVM调优是Java开发者进阶路上必须掌握的硬核技能。我从事Java开发十年来&#xff0c;处理过上百个性能问题案例&#xff0c;90%的线上故障都能通过合理的JVM参数调整得到缓解。不同于框架API的快速上手&#xff0c;JVM调优需要开发者深入理解Java程序的运…

作者头像 李华
网站建设 2026/9/12 2:05:23

ECS自建数据库与瑶池RDS等保三级合规对比

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 2:03:04

AI Agent双层记忆架构:Working Memory与Long-term Memory工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华