Java Web超市管理系统实战:半小时搭建完整项目,掌握CRUD与三层架构
2026/7/4 2:03:40 网站建设 项目流程

大家好,我是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环境变量。
  • 集成开发环境(IDE):Eclipse IDE for Enterprise Java Developers 或 IntelliJ IDEA(社区版即可)。本文演示以Eclipse为主,IDEA操作逻辑类似。
  • Web服务器:Apache Tomcat 9.x。下载后解压,并在Eclipse中配置好Server Runtime。
  • 数据库:MySQL 5.7 或 8.0。你需要安装MySQL服务器,并准备好一个图形化管理工具,如MySQL WorkbenchNavicat
  • 数据库驱动:MySQL Connector/J(即JDBC驱动)的JAR包(如mysql-connector-java-8.0.xx.jar)。

2.2 创建动态Web项目

  1. 打开Eclipse,选择File -> New -> Dynamic Web Project
  2. 输入项目名称,例如SupermarketManagementSystem
  3. Target runtime选择你已配置好的Apache Tomcat 9.0。
  4. Dynamic web module version选择 3.1 或 4.0(取决于你的Tomcat版本,Tomcat 9支持3.1/4.0)。
  5. 勾选Generate web.xml deployment descriptor(这将为我们生成关键的web.xml配置文件)。
  6. 点击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 数据库概念模型

我们设计四个核心表,满足基本的管理需求:

  1. 商品表 (goods):存储商品信息。
  2. 员工表 (employee):存储系统操作员信息。
  3. 供应商表 (supplier):存储商品供应商信息。
  4. 销售记录表 (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?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/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 List<Goods> getAllGoods() { List<Goods> 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获取数据 List<Goods> 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 contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>商品管理</title> <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/css/style.css"> </head> <body> <div class="container"> <h1>商品列表</h1> <a href="addGoods.jsp" class="btn">添加新商品</a> <table border="1" cellspacing="0" width="100%"> <tr> <th>ID</th> <th>商品名称</th> <th>单价</th> <th>库存</th> <th>供应商</th> <th>创建时间</th> <th>操作</th> </tr> <%-- 使用JSTL遍历request中传来的goodsList --%> <c:forEach var="goods" 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 href="editGoods.jsp?id=${goods.id}">编辑</a> | <a href="javascript:if(confirm('确定删除吗?')) location.href='deleteGoods?id=${goods.id}'">删除</a> </td> </tr> </c:forEach> </table> </div> </body> </html>

添加商品页面 (addGoods.jsp)

<%-- 文件路径:WebContent/addGoods.jsp --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>添加商品</title> </head> <body> <h2>添加新商品</h2> <%-- 显示错误信息 --%> <c:if test="${not empty errorMsg}"> <p style="color:red;">${errorMsg}</p> </c:if> <form action="addGoods" method="post"> <label>商品名称:</label><input type="text" name="goodsName" required><br><br> <label>单价:</label><input type="number" step="0.01" name="price" required><br><br> <label>库存:</label><input type="number" name="stock" required><br><br> <label>供应商:</label> <select name="supplierId"> <option value="">--请选择--</option> <%-- 这里应该从数据库动态加载供应商列表,为简化示例,先写死 --%> <option value="1">农夫山泉股份有限公司</option> <option value="2">康师傅控股有限公司</option> </select><br><br> <input type="submit" value="提交"> <input type="button" value="返回" onclick="history.back()"> </form> </body> </html>

4.5 配置与运行

  1. 配置web.xml:虽然我们使用了@WebServlet注解,但web.xml中仍需配置欢迎页和字符编码过滤器(解决POST请求乱码)。
    <!-- 文件路径:WebContent/WEB-INF/web.xml --> <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 欢迎页面 --> <welcome-file-list> <welcome-file>login.jsp</welcome-file> <!-- 可以先做一个登录页 --> </welcome-file-list> <!-- 字符编码过滤器 --> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceRequestEncoding</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>forceResponseEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
  2. 添加JSTL支持:为了在JSP中使用<c:forEach>等标签,需要将JSTL的JAR包(如javax.servlet.jsp.jstl.jarjavax.servlet.jsp.jstl-api.jar)也放入WEB-INF/lib目录。
  3. 部署运行:在Eclipse中,右键项目 ->Run As->Run on Server,选择配置好的Tomcat服务器。访问http://localhost:8080/SupermarketManagementSystem/goodsList即可看到商品列表。

5. 功能扩展与模块完善

按照上述商品管理的模式,你可以举一反三,完成其他模块:

  • 员工管理模块:实现员工登录(Session管理)、员工信息的增删改查。登录时,将用户输入的密码进行MD5加密后与数据库存储的加密密码比对。
  • 供应商管理模块:独立的CRUD操作。
  • 销售管理模块:这是业务核心。销售时,需要先查询商品库存,足够则生成销售记录,并同步更新商品库存这里必须使用数据库事务来保证“扣减库存”和“生成记录”两个操作要么都成功,要么都失败,防止数据不一致。
  • 统计报表模块:编写复杂的SQL语句,实现按日、按月统计销售额,或统计畅销商品。

6. 常见问题与排查思路

在开发过程中,你可能会遇到以下典型问题:

问题现象可能原因排查步骤与解决方案
404错误:页面找不到1. URL路径错误。
2. Servlet未正确配置@WebServletweb.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中包含characterEncoding=utf8
3. 确保JSP页面和Servlet请求/响应编码设置为UTF-8
JSP页面无法解析<c:forEach>标签未导入JSTL标签库或缺少JAR包。1. 检查JSP页面头部是否有<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
2. 检查WEB-INF/lib下是否有JSTL相关的JAR包。
表单提交后,Servlet获取的参数为null1. 表单inputname属性与Servlet中request.getParameter()的参数名不匹配。
2. 表单提交方式(GET/POST)与Servlet处理的方法(doGet/doPost)不匹配。
1. 仔细核对表单字段的name和Servlet中获取的参数名。
2. 表单是method="post",则Servlet必须重写doPost方法。
删除或更新操作影响多行数据SQL语句的WHERE条件不精确,通常是因为忘记指定主键id这是严重错误!在执行DELETE或UPDATE前,务必在控制台打印出最终要执行的SQL语句,确认WHERE条件能唯一锁定一行数据。

7. 项目优化与最佳实践建议

完成基础功能后,可以从以下方面提升项目质量,这更贴近企业级开发:

  1. 引入Maven/Gradle进行依赖管理:手动管理JAR包非常繁琐。使用Maven,只需在pom.xml中声明依赖(如Servlet API、JSTL、MySQL驱动、连接池等),构建工具会自动下载和管理,极大提升开发效率。
  2. 使用数据库连接池:像上面DBUtil那样每次操作都新建连接,性能极差。应使用如HikariCP,Druid等连接池。在Maven项目中引入依赖,并配置数据源。
  3. 采用成熟框架
    • 控制层:用Spring MVC替代原生Servlet,注解驱动更简洁。
    • 数据层:用MyBatisSpring Data JPA替代原生JDBC,能自动处理参数映射、结果集映射,减少大量模板代码。
    • 视图层:可以考虑使用Thymeleaf等现代模板引擎替代JSP,它们与Spring Boot集成更好,功能更强大。
  4. 实现分层解耦:在DAO和Servlet之间引入Service(业务逻辑层)。Servlet只负责接收请求和响应,具体的业务逻辑(如销售时的库存检查、事务管理)放在Service层,使代码结构更清晰,职责更明确。
  5. 增强安全性
    • 密码加密:不要使用MD5,应使用BCryptPasswordEncoder等加盐哈希算法。
    • SQL注入防护:坚持使用PreparedStatement(我们已用),切勿拼接SQL字符串。
    • XSS防护:对用户输入进行过滤或转义,或在JSP中使用<c:out value="${value}" />输出。
    • 会话管理:对需要登录的页面,在Servlet或过滤器中检查Session中是否存在用户信息。
  6. 加入日志:使用SLF4J + Logback记录系统运行日志、错误日志和操作日志,便于线上问题排查。
  7. 前端优化:使用Bootstrap等CSS框架快速构建美观的响应式界面。使用jQueryVue.js实现更流畅的交互,如异步加载数据、表单验证等。

这个超市管理系统项目麻雀虽小,五脏俱全。从环境搭建、数据库设计、到后端逻辑编写、前端页面展示,你完整地走通了一个Java Web应用的生命周期。理解了这个流程,你再学习Spring Boot等高级框架时,就会明白它们本质上是在简化这个流程中的各个步骤。建议你不仅完成代码复制,更要尝试自己添加“销售统计”、“用户权限控制”等功能,遇到问题就去搜索、调试,这才是能力提升的关键。项目的完整源码可以在文末的链接中获取,希望能为你后续的学习和面试带来实实在在的帮助。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询