基于JSP+Servlet的在线仓库库存管理系统 - 源码深度解析

JavaJavaScriptHTMLCSSMySQLJSP+Servlet
2026-03-225 浏览

文章摘要

本项目是一款基于JSP+Servlet技术栈构建的在线仓库库存管理系统,旨在为中小型仓库或商贸企业提供一套轻量级、易部署的核心库存管控解决方案。其核心业务价值在于解决了传统纸质或Excel表格管理库存方式带来的数据更新滞后、信息查询困难、易出错等核心痛点,通过将库存信息数字化和网络化,实现了库存状态...

在传统仓储与商贸企业的日常运营中,库存管理一直是核心且充满挑战的环节。依赖纸质单据或电子表格的记录方式,不仅效率低下,更易导致数据更新延迟、信息查询困难、人为差错频发等问题,进而引发超卖、缺货或资金占用不合理等经营风险。为解决这些痛点,一套基于B/S架构的智能仓储管控平台应运而生。该系统采用经典的JSP+Servlet技术栈,为企业提供了一套轻量级、高内聚、易部署的库存管理解决方案,实现了库存数据的实时化、可视化与精准化控制。

系统架构与技术栈解析

本平台严格遵循J2EE体系下的MVC设计模式,实现了展示层、控制层与业务逻辑层的清晰分离,确保了代码的可维护性和可扩展性。

  • 视图层:使用JSP技术构建用户界面,并集成JSTL标签库与EL表达式。这种组合极大地简化了页面的动态数据渲染逻辑,避免了在JSP中嵌入过多的Java代码,使前端展示逻辑更加清晰。例如,通过<c:forEach>标签可以优雅地遍历商品列表,而EL表达式${product.name}则能直接输出JavaBean的属性值。
  • 控制层:Servlet扮演了系统中枢神经的角色。它负责拦截所有前端发起的HTTP请求,进行参数解析、会话管理,并根据请求路径将任务分发给相应的业务逻辑组件进行处理。处理完毕后,Servlet会选择下一个要展示的JSP视图,并转发请求,完成一次完整的请求-响应周期。
  • 模型层:由一系列封装了业务数据和规则的JavaBean构成,如ProductInventoryInOutStock等。这些实体对象与数据库表结构相对应,并通过DAO模式进行持久化操作。业务逻辑(如库存数量的增减、出入库流水记录的产生)被封装在Service层,确保了业务规则的集中管理和数据操作的原子性。
  • 数据持久层:采用JDBC直接连接MySQL数据库。通过编写高度抽象的DAO接口及其实现类,将SQL操作封装起来,为上层的业务逻辑提供统一的数据访问接口。这种方式在保证性能的同时,也使得未来迁移至ORM框架(如MyBatis)成为可能。

核心数据库表设计剖析

一个健壮的库存管理系统,其灵魂在于严谨的数据库设计。以下选取三个核心表进行深度分析,揭示其设计精妙之处。

1. 商品信息表

商品表是系统最基础的数据基石,其设计直接影响到后续所有业务的复杂度和准确性。

CREATE TABLE `product` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '商品ID',
  `name` varchar(255) NOT NULL COMMENT '商品名称',
  `category_id` int(11) NOT NULL COMMENT '商品分类ID',
  `spec` varchar(255) DEFAULT NULL COMMENT '商品规格',
  `unit` varchar(50) DEFAULT NULL COMMENT '单位',
  `purchase_price` decimal(10,2) DEFAULT NULL COMMENT '采购价',
  `suggested_price` decimal(10,2) DEFAULT NULL COMMENT '建议售价',
  `status` int(11) DEFAULT '1' COMMENT '状态',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_name_spec` (`name`,`spec`),
  KEY `idx_category_id` (`category_id`),
  KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品信息表';
  • 设计亮点
    • 唯一性约束:通过联合唯一键uk_name_spec (name, spec),确保了同一规格的同名商品不会重复录入,这是库存准确性的重要前提。
    • 价格字段精度purchase_pricesuggested_price使用decimal(10,2)类型,精确到分,完全符合金融计算的要求,避免了浮点数计算可能带来的精度损失。
    • 自动化时间戳create_timeupdate_time字段利用MySQL的特性自动管理,update_time在记录更新时自动设置为当前时间,极大方便了数据追踪和审计。
    • 索引优化:对category_idstatus字段建立了索引,显著提升了按分类查询和按状态筛选商品时的查询性能。

2. 库存记录表

库存表是系统的核心,它动态反映了每个商品的实时结存数量。

CREATE TABLE `inventory` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '记录ID',
  `product_id` int(11) NOT NULL COMMENT '商品ID',
  `quantity` int(11) NOT NULL DEFAULT '0' COMMENT '库存数量',
  `lock_quantity` int(11) NOT NULL DEFAULT '0' COMMENT '锁定数量',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_product_id` (`product_id`),
  KEY `idx_update_time` (`update_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='库存记录表';
  • 设计亮点
    • 库存与锁定库存分离quantity表示实际可用库存,lock_quantity表示已被销售订单占用但尚未出库的锁定库存。这种设计有效解决了并发下的超卖问题。业务逻辑在创建销售订单时先增加锁定库存,出库时再减少锁定库存和总库存,确保了数据的一致性。
    • 商品级唯一性:通过uk_product_id唯一键约束,确保一种商品在库存表中只有一条记录,简化了库存查询和更新的逻辑。
    • 更新追踪update_time索引有助于快速定位最近发生变动的库存记录,便于进行库存动态分析。

3. 出入库流水表

流水表记录了每一笔库存变动的明细,是进行库存对账、追溯问题根源的关键。

CREATE TABLE `in_out_stock` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '流水ID',
  `product_id` int(11) NOT NULL COMMENT '商品ID',
  `type` int(11) NOT NULL COMMENT '类型',
  `quantity` int(11) NOT NULL COMMENT '数量',
  `related_order` varchar(255) DEFAULT NULL COMMENT '关联单号',
  `operator` varchar(255) NOT NULL COMMENT '操作员',
  `operate_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',
  `notes` varchar(500) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`),
  KEY `idx_product_id` (`product_id`),
  KEY `idx_operate_time` (`operate_time`),
  KEY `idx_related_order` (`related_order`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='出入库流水表';
  • 设计亮点
    • 全链路可追溯:通过related_order字段关联到原始的采购单或销售单号,任何库存数量的变化都能追溯到具体的业务源头。
    • 操作审计完备:记录了operator(操作员)和operate_time(操作时间),满足了企业内部审计和责任追溯的要求。
    • 多维度查询索引:对product_idoperate_timerelated_order都建立了索引,支持按商品查流水、按时间范围查变动、按单号查明细等多种高效查询场景。

核心功能实现深度解析

1. 商品分类与信息管理

商品管理是库存系统的基础。系统提供了完整的商品分类体系和商品信息的CRUD操作。

商品分类管理

后端Servlet控制器代码示例

// ProductAddServlet.java
@WebServlet("/admin/product/add")
public class ProductAddServlet extends HttpServlet {
    private ProductService productService = new ProductServiceImpl();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        // 1. 获取并验证前端提交的表单数据
        String name = request.getParameter("name");
        String categoryIdStr = request.getParameter("categoryId");
        String spec = request.getParameter("spec");
        String unit = request.getParameter("unit");
        String purchasePriceStr = request.getParameter("purchasePrice");
        String suggestedPriceStr = request.getParameter("suggestedPrice");

        // 参数校验逻辑(略)
        if (name == null || name.trim().isEmpty()) {
            // 返回错误信息
            response.sendError(400, "商品名称不能为空");
            return;
        }

        try {
            // 2. 数据类型转换与封装
            Integer categoryId = Integer.valueOf(categoryIdStr);
            BigDecimal purchasePrice = new BigDecimal(purchasePriceStr);
            BigDecimal suggestedPrice = new BigDecimal(suggestedPriceStr);

            Product product = new Product();
            product.setName(name.trim());
            product.setCategoryId(categoryId);
            product.setSpec(spec);
            product.setUnit(unit);
            product.setPurchasePrice(purchasePrice);
            product.setSuggestedPrice(suggestedPrice);
            product.setStatus(1); // 默认启用状态

            // 3. 调用Service层执行添加业务逻辑
            boolean success = productService.addProduct(product);
            if (success) {
                // 4. 添加成功,重定向到商品列表页,避免表单重复提交
                response.sendRedirect(request.getContextPath() + "/admin/product/list");
            } else {
                // 添加失败(如名称规格重复),返回错误页面
                request.setAttribute("errorMsg", "添加商品失败,可能已存在同名同规格商品");
                request.getRequestDispatcher("/admin/product-add.jsp").forward(request, response);
            }
        } catch (Exception e) {
            e.printStackTrace();
            response.sendError(500, "服务器内部错误");
        }
    }
}

前端JSP页面片段(使用JSTL遍历商品列表)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<table>
  <thead>
    <tr>
      <th>ID</th>
      <th>商品名称</th>
      <th>分类</th>
      <th>规格</th>
      <th>采购价</th>
      <th>操作</th>
    </tr>
  </thead>
  <tbody>
    <c:forEach var="product" items="${productList}">
      <tr>
        <td>${product.id}</td>
        <td>${product.name}</td>
        <td>${product.categoryName}</td> <%-- 通过Product对象扩展的属性 --%>
        <td>${product.spec}</td>
        <td>¥${product.purchasePrice}</td>
        <td>
          <a href="/admin/product/edit?id=${product.id}">编辑</a>
          <a href="javascript:void(0)" onclick="confirmDelete(${product.id})">删除</a>
        </td>
      </tr>
    </c:forEach>
  </tbody>
</table>

2. 采购入库与库存更新联动

采购入库是增加库存的核心业务。该操作不仅是简单的数据录入,更涉及库存数量的原子性更新和流水记录的产生。

采购信息管理

入库业务的核心Service层代码

// InOutStockServiceImpl.java
public class InOutStockServiceImpl implements InOutStockService {
    private InOutStockDAO inOutStockDAO = new InOutStockDAOImpl();
    private InventoryDAO inventoryDAO = new InventoryDAOImpl();

    @Override
    public boolean purchaseInStock(Integer productId, Integer quantity, String purchaseOrderNo, String operator) {
        Connection conn = null;
        try {
            conn = DBUtil.getConnection();
            conn.setAutoCommit(false); // 开启事务

            // 1. 生成入库流水记录
            InOutStock record = new InOutStock();
            record.setProductId(productId);
            record.setType(1); // 1代表入库
            record.setQuantity(quantity);
            record.setRelatedOrder(purchaseOrderNo);
            record.setOperator(operator);
            record.setOperateTime(new Date());

            int affectRows = inOutStockDAO.insert(conn, record);

            if (affectRows == 0) {
                conn.rollback();
                return false;
            }

            // 2. 更新库存记录
            // 先查询是否存在该商品的库存记录
            Inventory inventory = inventoryDAO.selectByProductId(conn, productId);
            if (inventory == null) {
                // 不存在则创建
                inventory = new Inventory();
                inventory.setProductId(productId);
                inventory.setQuantity(quantity);
                affectRows = inventoryDAO.insert(conn, inventory);
            } else {
                // 存在则更新(增加)
                affectRows = inventoryDAO.updateQuantity(conn, productId, inventory.getQuantity() + quantity);
            }

            if (affectRows > 0) {
                conn.commit(); // 提交事务
                return true;
            } else {
                conn.rollback(); // 回滚事务
                return false;
            }
        } catch (SQLException e) {
            e.printStackTrace();
            if (conn != null) {
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            return false;
        } finally {
            DBUtil.closeConnection(conn);
        }
    }
}

3. 实时库存查询与预警

系统提供多维度的库存查询功能,并可根据预设阈值进行库存预警。

入库出库管理

带查询条件的库存DAO层代码

// InventoryDAOImpl.java
public class InventoryDAOImpl implements InventoryDAO {
    @Override
    public List<InventoryVO> selectInventoryWithProduct(Connection conn, String productName, Integer categoryId, Integer lowStockFlag) throws SQLException {
        List<InventoryVO> list = new ArrayList<>();
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT i.*, p.name as product_name, p.category_id, p.spec, p.unit, c.name as category_name ");
        sql.append("FROM inventory i ");
        sql.append("INNER JOIN product p ON i.product_id = p.id ");
        sql.append("INNER JOIN category c ON p.category_id = c.id ");
        sql.append("WHERE p.status = 1 "); // 只查询启用状态的商品

        List<Object> params = new ArrayList<>();

        if (productName != null && !productName.trim().isEmpty()) {
            sql.append("AND p.name LIKE ? ");
            params.add("%" + productName.trim() + "%");
        }
        if (categoryId != null && categoryId > 0) {
            sql.append("AND p.category_id = ? ");
            params.add(categoryId);
        }
        if (lowStockFlag != null && lowStockFlag == 1) {
            // 假设低库存阈值为10,这里可以扩展为从配置表读取
            sql.append("AND i.quantity <= 10 ");
        }
        sql.append("ORDER BY i.update_time DESC");

        PreparedStatement pstmt = conn.prepareStatement(sql.toString());
        for (int i = 0; i < params.size(); i++) {
            pstmt.setObject(i + 1, params.get(i));
        }

        ResultSet rs = pstmt.executeQuery();
        while (rs.next()) {
            InventoryVO vo = new InventoryVO();
            vo.setId(rs.getInt("id"));
            vo.setProductId(rs.getInt("product_id"));
            vo.setQuantity(rs.getInt("quantity"));
            vo.setProductName(rs.getString("product_name"));
            vo.setSpec(rs.getString("spec"));
            vo.setUnit(rs.getString("unit"));
            vo.setCategoryName(rs.getString("category_name"));
            list.add(vo);
        }
        DBUtil.closeResultSet(rs);
        DBUtil.closeStatement(pstmt);
        return list;
    }
}

4. 财务信息看板

系统通过聚合数据,为管理者提供一个直观的财务信息看板,辅助决策。

财务信息管理

财务数据统计的Service层代码

// FinancialService.java
public class FinancialServiceImpl implements FinancialService {
    @Override
    public FinancialSummary getFinancialSummary(Date startDate, Date endDate) {
        FinancialSummary summary = new FinancialSummary();
        Connection conn = null;
        try {
            conn = DBUtil.getConnection();

            // 计算指定时间段内的总采购金额
            String purchaseSql = "SELECT SUM(ios.quantity * p.purchase_price) as total_purchase_amount " +
                                "FROM in_out_stock ios " +
                                "INNER JOIN product p ON ios.product_id = p.id " +
                                "WHERE ios.type = 1 AND ios.operate_time BETWEEN ? AND ?";
            BigDecimal totalPurchase = executeScalar(conn, purchaseSql, startDate, endDate);
            summary.setTotalPurchaseAmount(totalPurchase != null ? totalPurchase : BigDecimal.ZERO);

            // 计算总销售额(示例,假设type=2为出库)
            String salesSql = "SELECT SUM(ios.quantity * p.suggested_price) as total_sales_amount " +
                             "FROM in_out_stock ios " +
                             "INNER JOIN product p ON ios.product_id = p.id " +
                             "WHERE ios.type = 2 AND ios.operate_time BETWEEN ? AND ?";
            BigDecimal totalSales = executeScalar(conn, salesSql, startDate, endDate);
            summary.setTotalSalesAmount(totalSales != null ? totalSales : BigDecimal.ZERO);

            // 计算当前总库存价值
            String inventoryValueSql = "SELECT SUM(i.quantity * p.purchase_price) as total_inventory_value " +
                                      "FROM inventory i " +
                                      "INNER JOIN product p ON i.product_id = p.id";
            BigDecimal totalInventoryValue = executeScalar(conn, inventoryValueSql);
            summary.setTotalInventoryValue(totalInventoryValue != null ? totalInventoryValue : BigDecimal.ZERO);

        } catch (SQLException e) {
            e.printStackTrace();
       
本文关键词
JSPServlet库存管理仓库管理系统源码解析

上下篇

上一篇
没有更多文章
下一篇
没有更多文章