Java Web超市管理系统实战:半小时搭建完整项目,掌握CRUD与三层架构
大家好我是CSDN的一名技术博主。很多Java初学者在学完基础语法和Web开发后常常苦于找不到一个合适的项目来串联知识点将理论转化为实战能力。超市管理系统就是一个经典的练手项目它涵盖了从数据库设计、后端逻辑到前端展示的完整流程非常适合用来巩固Java Web技术栈。本文将手把手带你从零开始在半小时内快速搭建一个功能完整的超市管理系统并提供完整的源码和清晰的实现思路让你不仅“做得出来”更能“理解透彻”。1. 项目背景与核心价值超市管理系统是一个典型的信息管理系统MIS其核心目标是实现对超市日常运营中商品、员工、供应商、销售等核心业务数据的信息化、规范化管理。对于开发者而言这类项目具有极高的学习价值技术栈全面它几乎覆盖了Java Web开发的所有基础环节Servlet/JSP或Spring MVC、JDBC或MyBatis、MySQL数据库、HTML/CSS/JavaScript前端、Tomcat服务器等。业务逻辑典型包含了对数据的增删改查CRUD操作这是所有业务系统的基石。你将实际处理表单提交、数据验证、数据库交互、结果展示等一系列连贯操作。贴近实际应用虽然作为学习项目进行了简化但其模块划分如商品管理、员工管理、销售统计与实际商业系统高度相似有助于你建立业务建模的思维。通过完成这个项目你将能系统性地掌握如何将一个业务需求分解为数据库表结构再通过Java代码实现后台逻辑最终用网页呈现给用户的完整开发流程。这远比孤立地学习某个框架或语法更有意义。2. 环境准备与项目结构在开始编码前我们需要准备好“战场”。请确保你的开发环境包含以下组件版本无需完全一致但建议使用主流稳定版以避免兼容性问题。2.1 开发环境清单操作系统Windows 10/11 macOS 或 Linux 均可。Java开发工具包JDK版本 8 或 11推荐JDK 8兼容性最广。安装后请配置好JAVA_HOME环境变量。集成开发环境IDEEclipse IDE for Enterprise Java Developers 或 IntelliJ IDEA社区版即可。本文演示以Eclipse为主IDEA操作逻辑类似。Web服务器Apache Tomcat 9.x。下载后解压并在Eclipse中配置好Server Runtime。数据库MySQL 5.7 或 8.0。你需要安装MySQL服务器并准备好一个图形化管理工具如MySQL Workbench或Navicat。数据库驱动MySQL Connector/J即JDBC驱动的JAR包如mysql-connector-java-8.0.xx.jar。2.2 创建动态Web项目打开Eclipse选择File - New - Dynamic Web Project。输入项目名称例如SupermarketManagementSystem。Target runtime选择你已配置好的Apache Tomcat 9.0。Dynamic web module version选择 3.1 或 4.0取决于你的Tomcat版本Tomcat 9支持3.1/4.0。勾选Generate web.xml deployment descriptor这将为我们生成关键的web.xml配置文件。点击Finish完成创建。2.3 项目目录结构说明创建完成后你的项目结构应类似于以下形式。清晰的结构是项目可维护性的第一步。SupermarketManagementSystem/ ├── src/ # Java源代码目录 │ └── (你的包路径如 com.supermarket.dao, com.supermarket.model等) ├── WebContent/ # Web资源目录 (Eclipse Dynamic Web Project标准) │ ├── META-INF/ │ ├── WEB-INF/ │ │ ├── lib/ # 存放第三方JAR包如MySQL驱动 │ │ └── web.xml # Web应用部署描述文件 │ ├── css/ # 样式表文件 │ ├── js/ | JavaScript文件 │ ├── images/ # 图片资源 │ └── *.jsp # JSP页面文件如 index.jsp, goodsList.jsp └── build/classes/ # 编译后的class文件Eclipse自动管理关键一步将下载好的mysql-connector-java-xxx.jar文件复制到WebContent/WEB-INF/lib/目录下。这样项目在部署时才能正确加载数据库驱动。3. 数据库设计与实现任何管理系统的核心都是数据。我们先设计数据库这是整个项目的“地基”。3.1 数据库概念模型我们设计四个核心表满足基本的管理需求商品表 (goods)存储商品信息。员工表 (employee)存储系统操作员信息。供应商表 (supplier)存储商品供应商信息。销售记录表 (sale_record)存储每一笔销售流水。3.2 SQL建表语句在你的MySQL数据库中创建一个名为supermarket_db的数据库然后执行以下SQL语句。-- 创建数据库 CREATE DATABASE IF NOT EXISTS supermarket_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE supermarket_db; -- 1. 商品表 CREATE TABLE goods ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 商品ID, goods_name VARCHAR(100) NOT NULL COMMENT 商品名称, price DECIMAL(10, 2) NOT NULL COMMENT 单价, stock INT NOT NULL DEFAULT 0 COMMENT 库存数量, supplier_id INT COMMENT 供应商ID, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, INDEX idx_supplier (supplier_id), INDEX idx_name (goods_name) ) COMMENT商品信息表; -- 2. 供应商表 CREATE TABLE supplier ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 供应商ID, supplier_name VARCHAR(100) NOT NULL COMMENT 供应商名称, contact_person VARCHAR(50) COMMENT 联系人, phone VARCHAR(20) COMMENT 联系电话, address VARCHAR(200) COMMENT 地址, INDEX idx_name (supplier_name) ) COMMENT供应商信息表; -- 3. 员工表系统用户 CREATE TABLE employee ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 员工ID, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录用户名, password VARCHAR(100) NOT NULL COMMENT 登录密码存储加密后的, real_name VARCHAR(50) NOT NULL COMMENT 真实姓名, role VARCHAR(20) DEFAULT staff COMMENT 角色admin(管理员), staff(员工), INDEX idx_username (username) ) COMMENT员工/用户表; -- 4. 销售记录表 CREATE TABLE sale_record ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 记录ID, goods_id INT NOT NULL COMMENT 商品ID, quantity INT NOT NULL COMMENT 销售数量, total_price DECIMAL(10, 2) NOT NULL COMMENT 销售总价, employee_id INT NOT NULL COMMENT 操作员ID, sale_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 销售时间, INDEX idx_goods (goods_id), INDEX idx_employee (employee_id), INDEX idx_time (sale_time) ) COMMENT销售记录表; -- 添加外键约束确保数据完整性 ALTER TABLE goods ADD CONSTRAINT fk_goods_supplier FOREIGN KEY (supplier_id) REFERENCES supplier(id) ON DELETE SET NULL; ALTER TABLE sale_record ADD CONSTRAINT fk_sale_goods FOREIGN KEY (goods_id) REFERENCES goods(id) ON DELETE CASCADE; ALTER TABLE sale_record ADD CONSTRAINT fk_sale_employee FOREIGN KEY (employee_id) REFERENCES employee(id); -- 插入初始测试数据 INSERT INTO employee (username, password, real_name, role) VALUES (admin, MD5(123456), 系统管理员, admin), (zhangsan, MD5(123456), 张三, staff); INSERT INTO supplier (supplier_name, contact_person, phone, address) VALUES (农夫山泉股份有限公司, 李经理, 13800138001, 浙江省杭州市西湖区), (康师傅控股有限公司, 王主任, 13900139002, 天津市经济技术开发区); INSERT INTO goods (goods_name, price, stock, supplier_id) VALUES (农夫山泉550ml, 2.00, 100, 1), (康师傅红烧牛肉面, 4.50, 80, 2), (可口可乐330ml, 3.00, 150, NULL);设计要点说明主键与自增每个表都有一个id作为主键并设置为自增 (AUTO_INCREMENT)方便管理和关联。字段注释使用COMMENT为每个字段添加注释这在团队协作和后期维护时非常有用。索引优化对经常用于查询条件的字段如goods_name,username,sale_time创建了索引 (INDEX)可以大幅提升查询速度。外键约束通过FOREIGN KEY建立了表之间的关联保证了数据的一致性和完整性。例如不能销售一个不存在的商品。密码加密在插入员工数据时我们使用了MySQL的MD5()函数对密码进行简单的加密存储。注意在生产环境中应使用更安全的加密方式如BCrypt。字符集使用utf8mb4字符集以支持存储Emoji等所有Unicode字符。4. 核心功能模块实现Java Web三层架构我们将采用经典的JSP Servlet JavaBean (Model)模式也可以理解为一种简化的三层架构表示层JSP、控制层Servlet、模型层JavaBean和DAO。4.1 模型层 (Model) - 实体类与数据库工具首先创建与数据库表对应的Java实体类JavaBean。1. 数据库连接工具类 (DBUtil.java)这个类负责加载驱动、获取连接、释放资源是所有数据库操作的基础。// 文件路径src/com/supermarket/util/DBUtil.java package com.supermarket.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/supermarket_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai; private static final String USER root; // 你的数据库用户名 private static final String PASSWORD yourpassword; // 你的数据库密码 static { try { Class.forName(com.mysql.cj.jdbc.Driver); // 加载驱动 } catch (ClassNotFoundException e) { e.printStackTrace(); throw new RuntimeException(找不到数据库驱动); } } // 获取数据库连接 public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } // 关闭资源 public static void close(Connection conn, PreparedStatement pstmt, ResultSet rs) { try { if (rs ! null) rs.close(); if (pstmt ! null) pstmt.close(); if (conn ! null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }2. 商品实体类 (Goods.java)// 文件路径src/com/supermarket/model/Goods.java package com.supermarket.model; import java.math.BigDecimal; import java.util.Date; public class Goods { private Integer id; private String goodsName; private BigDecimal price; // 使用BigDecimal处理金额避免精度丢失 private Integer stock; private Integer supplierId; private Date createTime; // 关联的供应商名称非数据库字段用于页面显示 private String supplierName; // 无参构造器、全参构造器、Getter和Setter方法 public Goods() {} public Goods(Integer id, String goodsName, BigDecimal price, Integer stock, Integer supplierId, Date createTime) { this.id id; this.goodsName goodsName; this.price price; this.stock stock; this.supplierId supplierId; this.createTime createTime; } // 此处省略所有属性的 getter 和 setter 方法实际开发中请使用IDE生成。 // 例如public Integer getId() { return id; } public void setId(Integer id) { this.id id; } }按照同样的模式创建Employee.java,Supplier.java,SaleRecord.java等实体类。4.2 数据访问层 (DAO) - 商品管理示例DAO层封装了所有对数据库的操作。这里以商品管理为例。// 文件路径src/com/supermarket/dao/GoodsDao.java package com.supermarket.dao; import com.supermarket.model.Goods; import com.supermarket.util.DBUtil; import java.sql.*; import java.util.ArrayList; import java.util.List; public class GoodsDao { // 1. 查询所有商品带供应商名称 public ListGoods getAllGoods() { ListGoods list new ArrayList(); Connection conn null; PreparedStatement pstmt null; ResultSet rs null; String sql SELECT g.*, s.supplier_name FROM goods g LEFT JOIN supplier s ON g.supplier_id s.id ORDER BY g.id DESC; try { conn DBUtil.getConnection(); pstmt conn.prepareStatement(sql); rs pstmt.executeQuery(); while (rs.next()) { Goods goods new Goods(); goods.setId(rs.getInt(id)); goods.setGoodsName(rs.getString(goods_name)); goods.setPrice(rs.getBigDecimal(price)); goods.setStock(rs.getInt(stock)); goods.setSupplierId(rs.getInt(supplier_id)); goods.setCreateTime(rs.getTimestamp(create_time)); goods.setSupplierName(rs.getString(supplier_name)); // 设置关联的供应商名 list.add(goods); } } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(conn, pstmt, rs); } return list; } // 2. 根据ID查询单个商品 public Goods getGoodsById(int id) { Goods goods null; Connection conn null; PreparedStatement pstmt null; ResultSet rs null; String sql SELECT * FROM goods WHERE id ?; try { conn DBUtil.getConnection(); pstmt conn.prepareStatement(sql); pstmt.setInt(1, id); rs pstmt.executeQuery(); if (rs.next()) { goods new Goods(); goods.setId(rs.getInt(id)); goods.setGoodsName(rs.getString(goods_name)); goods.setPrice(rs.getBigDecimal(price)); goods.setStock(rs.getInt(stock)); goods.setSupplierId(rs.getInt(supplier_id)); goods.setCreateTime(rs.getTimestamp(create_time)); } } catch (SQLException e) { e.printStackTrace(); } finally { DBUtil.close(conn, pstmt, rs); } return goods; } // 3. 新增商品 public boolean addGoods(Goods goods) { Connection conn null; PreparedStatement pstmt null; String sql INSERT INTO goods(goods_name, price, stock, supplier_id) VALUES(?, ?, ?, ?); try { conn DBUtil.getConnection(); pstmt conn.prepareStatement(sql); pstmt.setString(1, goods.getGoodsName()); pstmt.setBigDecimal(2, goods.getPrice()); pstmt.setInt(3, goods.getStock()); if (goods.getSupplierId() ! null) { pstmt.setInt(4, goods.getSupplierId()); } else { pstmt.setNull(4, Types.INTEGER); } int rows pstmt.executeUpdate(); return rows 0; } catch (SQLException e) { e.printStackTrace(); return false; } finally { DBUtil.close(conn, pstmt, null); } } // 4. 更新商品信息 public boolean updateGoods(Goods goods) { Connection conn null; PreparedStatement pstmt null; String sql UPDATE goods SET goods_name?, price?, stock?, supplier_id? WHERE id?; try { conn DBUtil.getConnection(); pstmt conn.prepareStatement(sql); pstmt.setString(1, goods.getGoodsName()); pstmt.setBigDecimal(2, goods.getPrice()); pstmt.setInt(3, goods.getStock()); if (goods.getSupplierId() ! null) { pstmt.setInt(4, goods.getSupplierId()); } else { pstmt.setNull(4, Types.INTEGER); } pstmt.setInt(5, goods.getId()); int rows pstmt.executeUpdate(); return rows 0; } catch (SQLException e) { e.printStackTrace(); return false; } finally { DBUtil.close(conn, pstmt, null); } } // 5. 根据ID删除商品 public boolean deleteGoods(int id) { Connection conn null; PreparedStatement pstmt null; String sql DELETE FROM goods WHERE id ?; try { conn DBUtil.getConnection(); pstmt conn.prepareStatement(sql); pstmt.setInt(1, id); int rows pstmt.executeUpdate(); return rows 0; } catch (SQLException e) { e.printStackTrace(); return false; } finally { DBUtil.close(conn, pstmt, null); } } }4.3 控制层 (Servlet) - 商品列表与添加Servlet作为控制器接收页面请求调用DAO处理业务并转发到对应的JSP页面。1. 商品列表展示Servlet (GoodsListServlet.java)// 文件路径src/com/supermarket/servlet/GoodsListServlet.java package com.supermarket.servlet; import com.supermarket.dao.GoodsDao; import com.supermarket.model.Goods; 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; WebServlet(/goodsList) // 定义访问路径 public class GoodsListServlet extends HttpServlet { private GoodsDao goodsDao new GoodsDao(); Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 调用DAO获取数据 ListGoods goodsList goodsDao.getAllGoods(); // 2. 将数据存入request作用域供JSP页面使用 request.setAttribute(goodsList, goodsList); // 3. 转发到商品列表JSP页面 request.getRequestDispatcher(/goodsList.jsp).forward(request, response); } }2. 添加商品Servlet (AddGoodsServlet.java)// 文件路径src/com/supermarket/servlet/AddGoodsServlet.java package com.supermarket.servlet; import com.supermarket.dao.GoodsDao; import com.supermarket.model.Goods; 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.math.BigDecimal; WebServlet(/addGoods) public class AddGoodsServlet extends HttpServlet { private GoodsDao goodsDao new GoodsDao(); Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 设置请求编码防止中文乱码 request.setCharacterEncoding(UTF-8); // 2. 获取表单参数 String name request.getParameter(goodsName); String priceStr request.getParameter(price); String stockStr request.getParameter(stock); String supplierIdStr request.getParameter(supplierId); // 3. 参数校验简单示例 if (name null || name.trim().isEmpty()) { request.setAttribute(errorMsg, 商品名称不能为空); request.getRequestDispatcher(/addGoods.jsp).forward(request, response); return; } // 4. 封装数据到Goods对象 Goods goods new Goods(); goods.setGoodsName(name.trim()); try { goods.setPrice(new BigDecimal(priceStr)); goods.setStock(Integer.parseInt(stockStr)); if (supplierIdStr ! null !supplierIdStr.isEmpty()) { goods.setSupplierId(Integer.parseInt(supplierIdStr)); } } catch (NumberFormatException e) { request.setAttribute(errorMsg, 价格或库存格式错误); request.getRequestDispatcher(/addGoods.jsp).forward(request, response); return; } // 5. 调用DAO执行插入 boolean success goodsDao.addGoods(goods); if (success) { // 添加成功重定向到列表页防止表单重复提交 response.sendRedirect(request.getContextPath() /goodsList); } else { request.setAttribute(errorMsg, 添加商品失败请重试); request.getRequestDispatcher(/addGoods.jsp).forward(request, response); } } }4.4 表示层 (JSP) - 商品列表页面JSP页面负责数据的展示和用户交互。商品列表页面 (goodsList.jsp)%-- 文件路径WebContent/goodsList.jsp --% % page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html head title商品管理/title link relstylesheet typetext/css href${pageContext.request.contextPath}/css/style.css /head body div classcontainer h1商品列表/h1 a hrefaddGoods.jsp classbtn添加新商品/a table border1 cellspacing0 width100% tr thID/th th商品名称/th th单价/th th库存/th th供应商/th th创建时间/th th操作/th /tr %-- 使用JSTL遍历request中传来的goodsList --% c:forEach vargoods items${requestScope.goodsList} tr td${goods.id}/td td${goods.goodsName}/td td¥${goods.price}/td td${goods.stock}/td td${goods.supplierName}/td td${goods.createTime}/td td a hrefeditGoods.jsp?id${goods.id}编辑/a | a hrefjavascript:if(confirm(确定删除吗)) location.hrefdeleteGoods?id${goods.id}删除/a /td /tr /c:forEach /table /div /body /html添加商品页面 (addGoods.jsp)%-- 文件路径WebContent/addGoods.jsp --% % page contentTypetext/html;charsetUTF-8 languagejava % html head title添加商品/title /head body h2添加新商品/h2 %-- 显示错误信息 --% c:if test${not empty errorMsg} p stylecolor:red;${errorMsg}/p /c:if form actionaddGoods methodpost label商品名称/labelinput typetext namegoodsName requiredbrbr label单价/labelinput typenumber step0.01 nameprice requiredbrbr label库存/labelinput typenumber namestock requiredbrbr label供应商/label select namesupplierId option value--请选择--/option %-- 这里应该从数据库动态加载供应商列表为简化示例先写死 --% option value1农夫山泉股份有限公司/option option value2康师傅控股有限公司/option /selectbrbr input typesubmit value提交 input typebutton value返回 onclickhistory.back() /form /body /html4.5 配置与运行配置web.xml虽然我们使用了WebServlet注解但web.xml中仍需配置欢迎页和字符编码过滤器解决POST请求乱码。!-- 文件路径WebContent/WEB-INF/web.xml -- ?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0 !-- 欢迎页面 -- welcome-file-list welcome-filelogin.jsp/welcome-file !-- 可以先做一个登录页 -- /welcome-file-list !-- 字符编码过滤器 -- filter filter-nameCharacterEncodingFilter/filter-name filter-classorg.apache.catalina.filters.SetCharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param init-param param-nameforceRequestEncoding/param-name param-valuetrue/param-value /init-param init-param param-nameforceResponseEncoding/param-name param-valuetrue/param-value /init-param /filter filter-mapping filter-nameCharacterEncodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping /web-app添加JSTL支持为了在JSP中使用c:forEach等标签需要将JSTL的JAR包如javax.servlet.jsp.jstl.jar和javax.servlet.jsp.jstl-api.jar也放入WEB-INF/lib目录。部署运行在Eclipse中右键项目 -Run As-Run on Server选择配置好的Tomcat服务器。访问http://localhost:8080/SupermarketManagementSystem/goodsList即可看到商品列表。5. 功能扩展与模块完善按照上述商品管理的模式你可以举一反三完成其他模块员工管理模块实现员工登录Session管理、员工信息的增删改查。登录时将用户输入的密码进行MD5加密后与数据库存储的加密密码比对。供应商管理模块独立的CRUD操作。销售管理模块这是业务核心。销售时需要先查询商品库存足够则生成销售记录并同步更新商品库存。这里必须使用数据库事务来保证“扣减库存”和“生成记录”两个操作要么都成功要么都失败防止数据不一致。统计报表模块编写复杂的SQL语句实现按日、按月统计销售额或统计畅销商品。6. 常见问题与排查思路在开发过程中你可能会遇到以下典型问题问题现象可能原因排查步骤与解决方案404错误页面找不到1. URL路径错误。2. Servlet未正确配置WebServlet或web.xml。3. 项目未成功部署到Tomcat。1. 检查浏览器地址栏URL与Servlet注解路径是否一致。2. 检查Eclipse的Servers视图确认项目已发布。3. 清理Tomcat工作目录并重新发布。500错误服务器内部错误1. JSP/Servlet代码有语法或逻辑错误。2. 数据库连接失败。3. 空指针异常NPE。1. 查看Tomcat控制台Console输出的完整异常堆栈信息这是最重要的线索。2. 检查DBUtil.java中的数据库URL、用户名、密码是否正确。3. 检查MySQL服务是否启动。插入中文到数据库显示乱码数据库、连接、表字段的字符集不统一。1. 确保数据库、表、字段的字符集为utf8mb4。2. 确保JDBC连接URL中包含characterEncodingutf8。3. 确保JSP页面和Servlet请求/响应编码设置为UTF-8。JSP页面无法解析c:forEach标签未导入JSTL标签库或缺少JAR包。1. 检查JSP页面头部是否有% taglib prefixc urihttp://java.sun.com/jsp/jstl/core %。2. 检查WEB-INF/lib下是否有JSTL相关的JAR包。表单提交后Servlet获取的参数为null1. 表单input的name属性与Servlet中request.getParameter()的参数名不匹配。2. 表单提交方式GET/POST与Servlet处理的方法doGet/doPost不匹配。1. 仔细核对表单字段的name和Servlet中获取的参数名。2. 表单是methodpost则Servlet必须重写doPost方法。删除或更新操作影响多行数据SQL语句的WHERE条件不精确通常是因为忘记指定主键id。这是严重错误在执行DELETE或UPDATE前务必在控制台打印出最终要执行的SQL语句确认WHERE条件能唯一锁定一行数据。7. 项目优化与最佳实践建议完成基础功能后可以从以下方面提升项目质量这更贴近企业级开发引入Maven/Gradle进行依赖管理手动管理JAR包非常繁琐。使用Maven只需在pom.xml中声明依赖如Servlet API、JSTL、MySQL驱动、连接池等构建工具会自动下载和管理极大提升开发效率。使用数据库连接池像上面DBUtil那样每次操作都新建连接性能极差。应使用如HikariCP,Druid等连接池。在Maven项目中引入依赖并配置数据源。采用成熟框架控制层用Spring MVC替代原生Servlet注解驱动更简洁。数据层用MyBatis或Spring Data JPA替代原生JDBC能自动处理参数映射、结果集映射减少大量模板代码。视图层可以考虑使用Thymeleaf等现代模板引擎替代JSP它们与Spring Boot集成更好功能更强大。实现分层解耦在DAO和Servlet之间引入Service业务逻辑层。Servlet只负责接收请求和响应具体的业务逻辑如销售时的库存检查、事务管理放在Service层使代码结构更清晰职责更明确。增强安全性密码加密不要使用MD5应使用BCryptPasswordEncoder等加盐哈希算法。SQL注入防护坚持使用PreparedStatement我们已用切勿拼接SQL字符串。XSS防护对用户输入进行过滤或转义或在JSP中使用c:out value${value} /输出。会话管理对需要登录的页面在Servlet或过滤器中检查Session中是否存在用户信息。加入日志使用SLF4J Logback记录系统运行日志、错误日志和操作日志便于线上问题排查。前端优化使用Bootstrap等CSS框架快速构建美观的响应式界面。使用jQuery或Vue.js实现更流畅的交互如异步加载数据、表单验证等。这个超市管理系统项目麻雀虽小五脏俱全。从环境搭建、数据库设计、到后端逻辑编写、前端页面展示你完整地走通了一个Java Web应用的生命周期。理解了这个流程你再学习Spring Boot等高级框架时就会明白它们本质上是在简化这个流程中的各个步骤。建议你不仅完成代码复制更要尝试自己添加“销售统计”、“用户权限控制”等功能遇到问题就去搜索、调试这才是能力提升的关键。项目的完整源码可以在文末的链接中获取希望能为你后续的学习和面试带来实实在在的帮助。