Java Web超市管理系统实战:从数据库设计到Servlet/JSP项目部署
30款热门AI模型一站整合DeepSeek/GLM/Claude 随心用限时 5 折。 点击领海量免费额度超市管理系统是 Java Web 开发中一个非常经典的实战项目它综合了数据库设计、前后端交互、业务逻辑处理、权限控制等多个核心知识点。很多初学者在尝试独立完成时常常卡在项目结构搭建、数据库连接、前后端数据流转这些环节导致项目无法运行或功能残缺。本文将以一个典型的超市管理系统为例从零开始详细讲解如何设计数据库、搭建项目结构、编写核心业务代码并最终部署运行。整个过程不仅会展示“怎么做”更会解释“为什么这么做”帮助你理解一个完整 Java Web 项目的骨架和脉络。本文适合有一定 Java 和 Web 基础希望动手实践一个完整项目来巩固技能的开发者。你将学习到如何使用 Servlet JSP JDBC 这一经典组合或理解其原理构建一个包含商品管理、员工管理、销售记录、库存查询等功能的系统。我们将从环境准备开始逐步完成数据库设计、项目创建、核心功能编码、页面制作直到项目在 Tomcat 上成功运行。文章末尾会提供关键代码片段和完整的项目结构说明并附上常见问题的排查路径。1. 理解超市管理系统的核心模块与数据流转在动手编码之前必须先理清系统要做什么以及数据如何在各个模块间流动。一个基础的超市管理系统通常包含以下几个核心模块商品管理模块负责商品的增删改查CRUD包括商品编号、名称、分类、进价、售价、库存数量等字段。员工/用户管理模块管理系统的操作员涉及登录验证和权限控制如普通收银员与管理员权限不同。销售/收银模块这是系统的核心业务模拟购物车、生成销售单、计算总价、更新库存。库存管理模块实时反映商品库存变化通常与销售、采购入库模块联动。报表查询模块提供按日、月或商品查询销售记录、利润统计等功能。这些模块背后是紧密关联的数据库表。数据流转的典型路径是用户在 JSP 页面发起请求如添加商品到购物车 - 请求被 Servlet 接收 - Servlet 调用 JavaBean或 Service 层处理业务逻辑 - JavaBean 通过 DAOData Access Object层操作数据库 - 数据库返回结果沿原路返回最终由 JSP 页面渲染展示给用户。理解这条“请求-响应”链路是调试和排查问题的关键。2. 环境准备与项目初始化一个可运行的 Java Web 项目离不开正确的环境。我们将基于最经典和稳定的组合进行搭建这有助于你理解 Web 项目的底层机制。2.1 开发环境清单请确保你的开发机已安装并配置好以下环境环境/工具推荐版本作用说明验证命令JDK1.8 或 11 (LTS版本)Java 程序运行和编译环境java -versionIDEEclipse IDE for Enterprise Java Developers 或 IntelliJ IDEA Ultimate集成开发环境简化 Web 项目管理启动 IDE 查看欢迎页Web 服务器Apache Tomcat 9.xServlet/JSP 容器用于部署和运行项目访问http://localhost:8080看到 Tomcat 主页数据库MySQL 5.7 或 8.0存储系统所有业务数据mysql -u root -p登录数据库客户端MySQL Workbench 或 Navicat图形化操作数据库方便建表和测试连接本地 MySQL 服务构建工具(可选) Maven 3.6管理项目依赖规范项目结构mvn -v注意版本兼容性很重要。Tomcat 9 对 JDK 8 和 11 支持良好。如果使用更高版本的 JDK如 17请确认你的 IDE 和 Tomcat 已配置相应的运行环境。2.2 创建动态 Web 项目这里以 Eclipse 为例演示如何创建一个标准的 Dynamic Web Project。打开 Eclipse选择File - New - Dynamic Web Project。填写项目信息Project name:SupermarketMS(名称可自定义但建议有意义且无空格)。Target runtime: 点击New Runtime...选择你已安装的 Apache Tomcat 9.0并指定其安装目录。Dynamic web module version: 选择 4.0 (对应 Servlet 4.0)。这是 Tomcat 9 支持的标准。Configuration: 选择Default Configuration for Apache Tomcat。点击 Next在后续页面中确保Context root为/SupermarketMS这是项目访问路径并勾选Generate web.xml deployment descriptor。这个web.xml文件是早期 Web 项目的核心配置文件虽然 Servlet 3.0 后支持注解但保留它有助于理解配置原理。点击 Finish。Eclipse 会自动生成一个符合 Java EE 标准的 Web 项目结构。生成的项目结构应类似如下SupermarketMS ├── src/ // 存放Java源代码Servlet, Bean, DAO等 ├── WebContent/ // Web应用根目录 │ ├── META-INF/ // META-INF目录 │ ├── WEB-INF/ // 受保护目录客户端无法直接访问 │ │ ├── lib/ // 存放项目依赖的JAR包如数据库驱动 │ │ └── web.xml // Web应用部署描述符 │ └── index.jsp // 默认欢迎页面 └── build/classes/ (Eclipse编译输出目录通常隐藏)2.3 导入必要的 JAR 包Web 项目需要依赖一些外部库。最基本的两个是MySQL 数据库驱动(mysql-connector-java-8.0.xx.jar): 用于连接 MySQL。JSTL 标签库(javax.servlet.jsp.jstl.jar和taglibs-standard-impl-1.2.5.jar等): 简化 JSP 页面中的 Java 代码避免在页面中写大量% %脚本。操作步骤下载上述 JAR 包可从 Maven 仓库或官网获取。在项目资源管理器中找到WebContent/WEB-INF/lib/目录。将下载的 JAR 文件直接复制到lib文件夹中。Eclipse 会自动将其添加到项目的构建路径Build Path。3. 数据库设计与建表数据库是系统的基石。设计良好的表结构能极大简化后续的编码工作。3.1 数据库概念模型我们设计四张核心表它们之间的关系构成了系统的主干用户表 (user): 存储登录系统的员工信息。商品表 (product): 存储所有商品信息。销售主表 (sale_order): 记录每一笔销售订单的概要信息如订单号、时间、总金额、操作员。销售明细表 (sale_item): 记录每一笔订单中包含的具体商品、数量、单价。它与销售主表是一对多的关系。3.2 SQL 建表语句在你的 MySQL 客户端中创建一个名为supermarket_db的数据库然后执行以下 SQL 语句。-- 创建数据库 CREATE DATABASE IF NOT EXISTS supermarket_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE supermarket_db; -- 1. 用户表 CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 用户ID, username VARCHAR(50) NOT NULL UNIQUE COMMENT 用户名, password VARCHAR(255) NOT NULL COMMENT 密码建议存储加密后的值, real_name VARCHAR(50) COMMENT 真实姓名, role VARCHAR(20) DEFAULT cashier COMMENT 角色admin(管理员), cashier(收银员), create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT系统用户表; -- 插入初始管理员用户密码示例为明文‘123456’实际项目务必加密 INSERT INTO user (username, password, real_name, role) VALUES (admin, 123456, 系统管理员, admin), (cashier1, 123456, 收银员张三, cashier); -- 2. 商品表 CREATE TABLE product ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 商品ID, product_no VARCHAR(20) NOT NULL UNIQUE COMMENT 商品编号, name VARCHAR(100) NOT NULL COMMENT 商品名称, category VARCHAR(50) COMMENT 分类, purchase_price DECIMAL(10,2) NOT NULL COMMENT 进价, sale_price DECIMAL(10,2) NOT NULL COMMENT 售价, stock INT NOT NULL DEFAULT 0 COMMENT 库存数量, status TINYINT DEFAULT 1 COMMENT 状态1-正常0-下架, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品信息表; -- 插入示例商品数据 INSERT INTO product (product_no, name, category, purchase_price, sale_price, stock) VALUES (P001, 可口可乐 500ml, 饮料, 2.00, 3.00, 100), (P002, 康师傅红烧牛肉面, 食品, 2.50, 4.00, 80), (P003, 清风抽纸 3层*100抽, 日用品, 8.00, 12.00, 50); -- 3. 销售订单主表 CREATE TABLE sale_order ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 订单ID, order_no VARCHAR(30) NOT NULL UNIQUE COMMENT 订单编号可按规则生成, total_amount DECIMAL(10,2) NOT NULL COMMENT 订单总金额, operator_id INT NOT NULL COMMENT 操作员ID关联user.id, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 下单时间, FOREIGN KEY (operator_id) REFERENCES user(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT销售订单主表; -- 4. 销售订单明细表 CREATE TABLE sale_item ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 明细ID, order_id INT NOT NULL COMMENT 所属订单ID关联sale_order.id, product_id INT NOT NULL COMMENT 商品ID关联product.id, product_no VARCHAR(20) COMMENT 商品编号冗余方便查询, product_name VARCHAR(100) COMMENT 商品名称冗余方便查询, quantity INT NOT NULL COMMENT 销售数量, unit_price DECIMAL(10,2) NOT NULL COMMENT 销售单价, subtotal DECIMAL(10,2) NOT NULL COMMENT 小计金额, FOREIGN KEY (order_id) REFERENCES sale_order(id) ON DELETE CASCADE, FOREIGN KEY (product_id) REFERENCES product(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT销售订单明细表;关键设计解释密码存储示例中使用了明文密码这是极不安全的。实际项目中必须对密码进行哈希加盐处理如使用 BCrypt。字段冗余在sale_item表中我们冗余存储了product_no和product_name。这是因为商品信息可能变更如改名而销售记录作为历史数据必须保持原样这种设计是业务上的常见做法。外键约束使用FOREIGN KEY确保了数据的引用完整性。例如不能插入一个不存在的operator_id到sale_order表中。字符集使用utf8mb4以支持存储 Emoji 等所有 Unicode 字符。4. 搭建项目数据访问层DAO数据访问层负责封装所有数据库操作为上层的业务逻辑提供干净的接口。我们采用经典的“接口-实现类”模式并使用 JDBC 进行数据库连接。4.1 数据库连接工具类首先创建一个工具类来管理数据库连接避免在每个 DAO 方法中重复编写连接代码。我们将数据库配置信息放在一个属性文件中。创建配置文件在src目录下新建文件db.properties。# db.properties jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/supermarket_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse jdbc.usernameroot jdbc.passwordyour_password # 替换为你的MySQL密码注意serverTimezone参数对于高版本 MySQL 驱动非常重要可以避免时区错误。useSSLfalse在本地开发环境通常需要设置。创建数据库连接工具类DBUtil.javapackage com.supermarket.util; import java.io.InputStream; import java.sql.*; import java.util.Properties; public class DBUtil { private static String driver; private static String url; private static String username; private static String password; // 静态代码块在类加载时读取配置 static { try { InputStream in DBUtil.class.getClassLoader().getResourceAsStream(db.properties); Properties prop new Properties(); prop.load(in); driver prop.getProperty(jdbc.driver); url prop.getProperty(jdbc.url); username prop.getProperty(jdbc.username); password prop.getProperty(jdbc.password); Class.forName(driver); // 加载驱动 } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(数据库配置初始化失败, e); } } // 获取数据库连接 public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, username, password); } // 关闭资源 public static void close(Connection conn, Statement stmt, ResultSet rs) { try { if (rs ! null) rs.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (stmt ! null) stmt.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn ! null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }4.2 创建实体类JavaBean实体类与数据库表一一对应用于在 Java 程序中承载数据。User.java:package com.supermarket.entity; import java.util.Date; public class User { private Integer id; private String username; private String password; private String realName; private String role; private Date createTime; // 省略 Getter 和 Setter 方法必须生成 }Product.java:package com.supermarket.entity; import java.math.BigDecimal; import java.util.Date; public class Product { private Integer id; private String productNo; private String name; private String category; private BigDecimal purchasePrice; private BigDecimal salePrice; private Integer stock; private Integer status; private Date createTime; // 省略 Getter 和 Setter 方法 }注意金额字段务必使用BigDecimal类型而不是float或double以避免精度丢失问题。4.3 实现商品 DAO 接口和类我们以商品管理为例展示完整的 DAO 层实现。创建接口ProductDao.java:package com.supermarket.dao; import com.supermarket.entity.Product; import java.util.List; public interface ProductDao { // 新增商品 int insert(Product product); // 根据ID删除商品 int deleteById(Integer id); // 更新商品信息 int update(Product product); // 根据ID查询商品 Product selectById(Integer id); // 根据商品编号查询 Product selectByProductNo(String productNo); // 查询所有商品可分页此处简化为查询全部 ListProduct selectAll(); // 根据名称模糊查询 ListProduct selectByName(String name); // 更新库存销售或入库时调用 int updateStock(Integer productId, Integer changeAmount); }创建实现类ProductDaoImpl.java:package com.supermarket.dao.impl; import com.supermarket.dao.ProductDao; import com.supermarket.entity.Product; import com.supermarket.util.DBUtil; import java.sql.*; import java.util.ArrayList; import java.util.List; public class ProductDaoImpl implements ProductDao { Override public int insert(Product product) { Connection conn null; PreparedStatement pstmt null; ResultSet rs null; int generatedId -1; String sql INSERT INTO product(product_no, name, category, purchase_price, sale_price, stock, status) VALUES(?,?,?,?,?,?,?); try { conn DBUtil.getConnection(); // 设置返回自增主键 pstmt conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); pstmt.setString(1, product.getProductNo()); pstmt.setString(2, product.getName()); pstmt.setString(3, product.getCategory()); pstmt.setBigDecimal(4, product.getPurchasePrice()); pstmt.setBigDecimal(5, product.getSalePrice()); pstmt.setInt(6, product.getStock()); pstmt.setInt(7, product.getStatus()); pstmt.executeUpdate(); // 获取生成的主键 rs pstmt.getGeneratedKeys(); if (rs.next()) { generatedId rs.getInt(1); } } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(conn, pstmt, rs); } return generatedId; // 返回插入后生成的ID } Override public Product selectById(Integer id) { Product product null; String sql SELECT * FROM product WHERE id ?; try (Connection conn DBUtil.getConnection(); PreparedStatement pstmt conn.prepareStatement(sql)) { pstmt.setInt(1, id); ResultSet rs pstmt.executeQuery(); if (rs.next()) { product mapResultSetToProduct(rs); } } catch (SQLException e) { e.printStackTrace(); } return product; } Override public ListProduct selectAll() { ListProduct list new ArrayList(); String sql SELECT * FROM product WHERE status 1 ORDER BY id DESC; try (Connection conn DBUtil.getConnection(); PreparedStatement pstmt conn.prepareStatement(sql); ResultSet rs pstmt.executeQuery()) { while (rs.next()) { list.add(mapResultSetToProduct(rs)); } } catch (SQLException e) { e.printStackTrace(); } return list; } Override public int updateStock(Integer productId, Integer changeAmount) { // changeAmount 为正表示入库为负表示出库 String sql UPDATE product SET stock stock ? WHERE id ? AND stock ? 0; try (Connection conn DBUtil.getConnection(); PreparedStatement pstmt conn.prepareStatement(sql)) { pstmt.setInt(1, changeAmount); pstmt.setInt(2, productId); pstmt.setInt(3, changeAmount); // 再次用于库存校验 return pstmt.executeUpdate(); // 返回受影响的行数 } catch (SQLException e) { e.printStackTrace(); return 0; } } // 其他方法deleteById, update, selectByName等实现逻辑类似此处省略... // 辅助方法将ResultSet映射为Product对象 private Product mapResultSetToProduct(ResultSet rs) throws SQLException { Product product new Product(); product.setId(rs.getInt(id)); product.setProductNo(rs.getString(product_no)); product.setName(rs.getString(name)); product.setCategory(rs.getString(category)); product.setPurchasePrice(rs.getBigDecimal(purchase_price)); product.setSalePrice(rs.getBigDecimal(sale_price)); product.setStock(rs.getInt(stock)); product.setStatus(rs.getInt(status)); product.setCreateTime(rs.getTimestamp(create_time)); return product; } }关键点解析使用 PreparedStatement有效防止 SQL 注入攻击务必使用?占位符而不是拼接字符串。资源管理使用 try-with-resources 语法Java 7或 finally 块确保ConnectionStatementResultSet被正确关闭防止内存泄漏。事务处理在更复杂的业务中如销售需要同时更新订单表和库存需要在 Service 层使用Connection的setAutoCommit(false)和commit()/rollback()来管理事务。本例 DAO 层不处理事务。5. 实现业务逻辑层Service与控制器ServletService 层负责处理复杂的业务规则并协调多个 DAO 的操作。Servlet 作为控制器Controller接收 HTTP 请求调用 Service然后转发请求或重定向到 JSP 视图。5.1 创建商品服务类ProductService.java封装商品相关的业务逻辑。package com.supermarket.service; import com.supermarket.dao.ProductDao; import com.supermarket.dao.impl.ProductDaoImpl; import com.supermarket.entity.Product; import java.util.List; public class ProductService { private ProductDao productDao new ProductDaoImpl(); public boolean addProduct(Product product) { // 业务校验检查商品编号是否已存在 if (productDao.selectByProductNo(product.getProductNo()) ! null) { return false; // 编号重复 } return productDao.insert(product) 0; } public ListProduct getAllProducts() { return productDao.selectAll(); } public Product getProductById(Integer id) { return productDao.selectById(id); } // 销售时扣减库存 public boolean reduceStock(Integer productId, Integer quantity) { // 注意quantity 传入正数但库存变化是负数 int rows productDao.updateStock(productId, -quantity); return rows 0; // rows0 表示更新成功 } // 其他业务方法... }5.2 创建商品列表展示的 ServletProductListServlet.java处理查看商品列表的请求。package com.supermarket.controller; import com.supermarket.entity.Product; import com.supermarket.service.ProductService; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; // 使用注解配置Servlet映射路径替代web.xml配置 WebServlet(/product/list) public class ProductListServlet extends HttpServlet { private ProductService productService new ProductService(); Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 调用Service获取数据 ListProduct productList productService.getAllProducts(); // 2. 将数据存入请求作用域供JSP页面使用 request.setAttribute(productList, productList); // 3. 请求转发到JSP页面进行渲染 request.getRequestDispatcher(/WEB-INF/jsp/product_list.jsp).forward(request, response); } }注意JSP 文件放在/WEB-INF/jsp/目录下是一种安全实践客户端无法直接通过 URL 访问这些 JSP必须通过 Servlet 转发。5.3 创建商品列表 JSP 页面在WebContent/WEB-INF/下创建jsp文件夹然后新建product_list.jsp。% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % !DOCTYPE html html head title商品管理/title link relstylesheet hrefhttps://cdn.jsdelivr.net/npm/bootstrap4.6.0/dist/css/bootstrap.min.css /head body div classcontainer mt-4 h2商品列表/h2 a href/SupermarketMS/product/add classbtn btn-primary mb-3新增商品/a table classtable table-bordered table-hover thead classthead-light tr thID/th th商品编号/th th商品名称/th th分类/th th售价/th th库存/th th操作/th /tr /thead tbody %-- 使用JSTL遍历productList --% c:forEach varproduct items${productList} tr td${product.id}/td td${product.productNo}/td td${product.name}/td td${product.category}/td td${product.salePrice}/td td span classbadge ${product.stock 10 ? badge-success : badge-warning} ${product.stock} /span /td td a href/SupermarketMS/product/edit?id${product.id} classbtn btn-sm btn-info编辑/a a href/SupermarketMS/product/delete?id${product.id} classbtn btn-sm btn-danger onclickreturn confirm(确定删除吗)删除/a /td /tr /c:forEach c:if test${empty productList} trtd colspan7 classtext-center暂无商品数据/td/tr /c:if /tbody /table /div /body /html页面技术点JSTL 标签% taglib %引入 JSTL 核心库c:forEach用于循环${}是 EL 表达式用于从请求、会话等作用域获取数据。这比在 JSP 中写 Java 脚本 (% %) 更清晰、安全。Bootstrap通过 CDN 引入 Bootstrap CSS快速美化页面。数据判断使用 EL 表达式和 JSTL 进行简单逻辑判断如${empty productList}和${product.stock 10 ? ...}。6. 配置、运行与验证6.1 配置部署与运行将项目部署到 Tomcat在 Eclipse 中右键点击项目 -Run As-Run on Server。选择已配置好的 Tomcat 9 服务器点击 Finish。Eclipse 会自动将项目打包并部署到 Tomcat 的webapps目录下并启动服务器。访问应用打开浏览器输入http://localhost:8080/SupermarketMS/。如果看到默认的index.jsp页面说明项目部署成功。访问商品列表页面http://localhost:8080/SupermarketMS/product/list。如果一切正常你应该能看到之前插入数据库的示例商品数据以表格形式展示出来。6.2 功能验证清单完成基础搭建后请按以下清单验证核心流程是否通畅验证环节操作预期结果常见问题数据库连接启动项目访问任意调用DAO的页面如商品列表。页面正常显示数据无数据库连接错误。1.ClassNotFoundException: 驱动JAR未放入WEB-INF/lib。2.Communications link failure: 数据库服务未启动或URL、端口、密码错误。3.The server time zone value...: 需要在JDBC URL中指定serverTimezone。Servlet 映射在浏览器直接输入Servlet配置的URL路径如/product/list。正确跳转并显示JSP页面内容。1. 404错误检查WebServlet注解路径或web.xml配置是否正确。2. 500错误查看Tomcat控制台日志通常是Servlet类编译错误或内部异常。JSP 页面渲染访问JSP页面查看表格、样式、数据。页面布局正常数据正确绑定CSS/JS加载无误。1. EL表达式不显示检查JSP页面头部的isELIgnored是否设为false默认是启用的。2. JSTL标签无效确认jstl.jar和standard.jar已放入lib。数据增删改查通过页面或构造请求测试商品的添加、删除、修改功能。数据库对应表记录随之变化页面反馈操作成功或失败。1. 中文乱码确保数据库、连接字符串、JSP页面、Servlet请求/响应编码均为UTF-8。2. 删除/更新失败检查外键约束或SQL语句的WHERE条件。7. 常见问题排查与进阶思考7.1 典型问题与解决方案在开发过程中你几乎一定会遇到以下问题问题现象可能原因排查步骤访问页面报 4041. URL 路径错误。2. 项目未成功部署。3.web.xml配置错误或缺失。1. 检查浏览器地址栏路径对比项目Context Root。2. 查看 Tomcatwebapps目录下是否有你的项目文件夹。3. 检查WEB-INF/web.xml中 Servlet 和 Filter 的映射配置。访问页面报 500 (内部服务器错误)1. JSP/Servlet 编译错误。2. 运行时异常空指针、数据库异常等。3. 依赖 JAR 包冲突或缺失。这是最有价值的调试信息源立即查看 Tomcat 控制台或logs/catalina.out打印的完整异常堆栈信息。堆栈会明确指出错误发生在哪一行代码。页面显示乱码1. 数据库字符集非 UTF-8。2. JSP 页面编码设置错误。3. Servlet 未设置请求/响应编码。4. Tomcat 服务器编码未配置。1. 确保数据库、表、字段字符集为utf8mb4。2. JSP 页面顶部设置% page pageEncodingUTF-8%和contentType。3. 在 Servlet 的doGet/doPost方法最开始调用request.setCharacterEncoding(UTF-8)和response.setCharacterEncoding(UTF-8)。4. 在 Tomcat 的server.xml的 Connector 标签中添加URIEncodingUTF-8。数据库连接失败1. MySQL 服务未启动。2.db.properties配置错误。3. 驱动版本与 MySQL 版本不匹配。4. 网络或防火墙问题。1. 命令行执行mysql -u root -p测试连接。2. 检查db.properties中的用户名、密码、数据库名。3. MySQL 8.0 需要使用com.mysql.cj.jdbc.Driver和新的连接字符串格式含时区。4. 确认 MySQL 允许远程连接如果非本地。JSTL 标签不生效1. 未导入 JSTL 标签库。2. JAR 包未放在正确位置。3. JSP 页面声明有误。1. 确认javax.servlet.jsp.jstl.jar和taglibs-standard-impl-x.x.x.jar在WEB-INF/lib下。2. 检查 JSP 页面顶部的% taglib %指令的 URI 是否正确。7.2 项目扩展与优化方向完成基础版本后可以从以下方向深化项目使其更接近生产水平引入分层架构明确区分 Controller (Servlet), Service, DAO 各层职责。Service 层处理业务逻辑和事务DAO 层只负责数据访问。添加登录与权限控制实现LoginServlet验证用户名密码将用户信息存入HttpSession。创建过滤器 (Filter)对需要登录的请求路径如/product/*,/sale/*进行拦截检查 Session 中是否存在用户信息。根据user.role字段在页面或后台接口实现不同权限控制。实现销售收银功能前端使用 JavaScript 维护一个购物车列表。提交时SaleServlet接收商品ID和数量列表。关键在 Service 方法中使用事务。在一个数据库事务内先插入sale_order记录再循环插入sale_item记录并调用productDao.updateStock扣减库存。任何一步失败整个事务回滚。使用连接池替换简单的DBUtil使用如 HikariCP、Druid 等数据库连接池大幅提升数据库连接管理效率和性能。前端优化使用 AJAX 实现无刷新添加商品、查询库存提升用户体验。日志记录引入 Log4j2 或 SLF4J在关键业务节点登录、销售、异常记录日志便于线上排查。密码加密使用 BCryptPasswordEncoder 等工具对用户密码进行哈希加盐存储绝对禁止明文密码。超市管理系统作为一个综合性的实战项目其价值不在于功能的复杂而在于通过它你能将 Java Web 开发中零散的知识点JDBC、Servlet、JSP、MVC、事务、数据库设计串联成一个有机的整体。当你能够独立调试通所有模块并理解其中每一行代码的作用和数据流动的每一个环节时你对 Java Web 开发的理解就真正上了一个台阶。接下来尝试去实现销售模块和权限控制你会遇到并解决更多真实开发中的问题这才是项目实战的核心意义。 30款热门AI模型一站整合DeepSeek/GLM/Claude 随心用限时 5 折。 点击领海量免费额度