跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

基于 Spring Boot 与 MySQL 的仓库管理系统设计与实现

介绍基于 Spring Boot、MyBatis 和 MySQL 构建仓库管理系统的方案。涵盖技术选型对比、四层架构设计(Entity/Mapper/Service/Controller)。重点解析库存扣减的并发控制,采用乐观锁加事务保证数据一致性。包含入库接口实现、安全性(SQL 注入、密码加密)、性能优化(索引、连接池)及生产环境注意事项。旨在提供高质量、可扩展的项目参考。

人间过客发布于 2026/3/29更新于 2026/9/1065 浏览

基于 Spring Boot 与 MySQL 的仓库管理系统设计与实现

在构建仓库管理系统时,技术选型至关重要。以下是一个使用 Spring Boot、MyBatis 和 MySQL 实现的系统架构详解。

项目结构示意图

1. 技术选型:Spring Boot + MyBatis + MySQL

在做技术选型时,常见对比如下:

  • Spring Boot vs 纯 Servlet/JSP:后者配置繁琐,需要手动处理大量 XML 和基础组件。Spring Boot 开箱即用,内嵌 Tomcat,一个 main 方法就能跑起来,能让你把精力集中在业务逻辑上。
  • MyBatis vs JPA/Hibernate:对于表结构相对固定、但复杂查询较多的场景,MyBatis 的半自动化特性更友好。SQL 写在 XML 里,清晰可控,方便做性能优化。
  • MySQL:开源、免费、资料多,是学习关系型数据库的首选。

这个组合既能体现对主流框架的掌握,又保证了项目的可完成性和代码的清晰度。

2. 清晰的分层架构

混乱的代码通常源于没有分层。我们采用经典的四层架构:

  1. Entity 层 (Pojo):纯数据对象,与数据库表字段一一对应。例如 Product 类,包含 id, name, stock, price 等属性。
  2. Mapper 层 (Dao):数据访问层,这里定义接口,对应的 XML 文件编写 SQL。MyBatis 会帮我们自动实现。
  3. Service 层:业务逻辑层,这是核心。所有业务规则(如库存不能为负、入库必须关联采购单)都在这里实现。事务注解 @Transactional 也加在这一层的方法上。
  4. Controller 层:控制层,接收 HTTP 请求,调用 Service,返回 JSON 数据。只做简单的参数校验和格式转换。

这样分层后,代码职责分明,后期修改库存逻辑,只需要动 Service,不会影响到 Controller 或 SQL。

3. 核心难点:库存扣减的并发与事务

这是仓库系统的灵魂,也是答辩时老师最爱问的点。直接 update product set stock = stock - #{quantity} where id = #{id} 在并发下会出大问题。

解决方案:乐观锁 + 事务。

首先,在 product 表增加一个版本号字段 version(或使用更新时间戳)。

// ProductEntity.java
public class Product {
    private Long id;
    private String name;
    private Integer stock;
    private Integer version; // 版本号
    // ... getters and setters
}

然后,在扣减库存时,将版本号作为条件。

<!-- ProductMapper.xml -->
<update>
    UPDATE product
    SET stock = stock - #{quantity}, version = version + 1
    WHERE id = #{id} AND stock >= #{quantity} AND version = #{version}
</update>

这条 SQL 的妙处在于:它同时判断了库存是否充足和版本号是否匹配。如果两个线程同时读到同一个 version,只有一个 UPDATE 会成功(因为成功后 version 变了),另一个会返回影响行数为 0。

在 Service 层,我们需要在一个事务里完成'查询当前库存和版本 -> 计算 -> 执行更新 -> 判断是否成功'这个流程。

// InventoryService.java
@Service
@Transactional(rollbackFor = Exception.class) // 声明式事务
public class InventoryService {
    @Autowired
    private ProductMapper productMapper;

    public boolean deductStock(Long productId, Integer quantity) {
        // 1. 查询商品信息(带版本号)
        Product product = productMapper.selectByIdForUpdate(productId);
        // 也可以用 selectForUpdate 加悲观锁,根据场景选
        if (product == null || product.getStock() < quantity) {
            throw new RuntimeException("商品不存在或库存不足");
        }
        // 2. 尝试乐观锁更新
        int rows = productMapper.deductStockWithVersion(productId, quantity, product.getVersion());
        // 3. 根据更新行数判断成功与否
        if (rows == 0) {
            // 更新失败,可能是并发冲突,可以重试或抛出异常
            throw new RuntimeException("库存扣减失败,请重试");
        }
        return true;
    }
}

@Transactional 注解保证了'查询'和'更新'要么全部成功,要么全部回滚,避免了数据不一致。这就是事务边界的合理划分——一个完整的业务操作在一个事务内。

4. 关键代码片段:商品入库接口

让我们看一个完整的入库接口实现,它包含了参数校验、业务逻辑和事务控制。

// StockInController.java
@RestController
@RequestMapping("/api/stock-in")
public class StockInController {
    @Autowired
    private StockInService stockInService;

    @PostMapping
    public ApiResponse<Void> stockIn(@RequestBody @Valid StockInRequest request) {
        // @Valid 会触发 StockInRequest 里定义的校验规则,如@NotNull
        stockInService.processStockIn(request);
        return ApiResponse.success();
    }
}
// StockInRequest.java
@Data
public class StockInRequest {
    @NotNull(message = "商品 ID 不能为空")
    private Long productId;
    @Min(value = 1, message = "入库数量必须大于 0")
    private Integer quantity;
    @NotBlank(message = "操作员不能为空")
    private String operator;
    private String remark;
}
// StockInService.java
@Service
@Slf4j // 使用 Lombok 简化日志声明
public class StockInService {
    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private StockFlowMapper stockFlowMapper; // 库存流水 Mapper

    @Transactional(rollbackFor = Exception.class)
    public void processStockIn(StockInRequest request) {
        // 1. 参数校验(Controller 层@Valid 已做基础校验,这里可做业务校验)
        Product product = productMapper.selectById(request.getProductId());
        if (product == null) {
            throw new BusinessException("商品不存在");
        }
        // 2. 更新库存(这里用了简单的更新,未加锁,根据实际并发情况选择策略)
        int updateRows = productMapper.increaseStock(request.getProductId(), request.getQuantity());
        if (updateRows == 0) {
            throw new BusinessException("更新库存失败");
        }
        // 3. 记录库存流水(用于追溯)
        StockFlow flow = new StockFlow();
        flow.setProductId(request.getProductId());
        flow.setQuantity(request.getQuantity());
        flow.setType(1); // 1 代表入库
        flow.setOperator(request.getOperator());
        flow.setRemark(request.getRemark());
        flow.setCreateTime(new Date());
        stockFlowMapper.insert(flow);
        log.info("商品 [{}] 入库{}件成功,操作员:{}", product.getName(), request.getQuantity(), request.getOperator());
        // 4. 这里还可以添加库存预警检查:如果库存超过安全上限,可以发消息通知
    }
}

5. 安全与性能:容易被忽略的细节

安全性:

  • SQL 注入:坚持使用 MyBatis 的 #{} 占位符,不要用字符串拼接 ${}(除非动态表名、列名等极少数情况)。
  • 密码加密:用户密码绝对不能明文存储。使用 BCryptPasswordEncoder 进行哈希加盐加密。
  • 接口防护:对关键接口(如出库、删除)添加权限注解 @PreAuthorize("hasRole('ADMIN')")。

性能:

  • 数据库索引:在 product 表的 id(主键)、name(查询)、stock(库存预警查询)等字段建立合适索引。在 stock_flow 表的 product_id 和 create_time 上建立联合索引,加速流水查询。
  • 连接池:在 application.yml 中配置 HikariCP 连接池参数,如最大连接数、超时时间,避免数据库连接耗尽。
  • 日志级别:生产环境将日志级别设为 WARN 或 ERROR,避免大量 INFO 日志拖慢性能。

6. 生产环境避坑指南

  1. MySQL 事务隔离级别:默认是 REPEATABLE READ。在极高并发扣库存场景,可以评估使用 READ COMMITTED 并结合乐观锁,能减少死锁概率。通过 @Transactional(isolation = Isolation.READ_COMMITTED) 设置。
  2. 空指针防御:使用 Optional 包装可能为 null 的对象,或用 Objects.requireNonNull 提前校验。Service 方法入参即使有 @Valid,内部使用前仍可做一次判空。
  3. 全局异常处理:使用 @ControllerAdvice 和 @ExceptionHandler 统一处理业务异常和系统异常,返回友好的 JSON 错误信息,而不是一堆栈轨迹。
  4. IDEA 连接池配置:本地开发时,别用默认的小连接数。在 application-dev.yml 里把 maximum-pool-size 调大点,避免测试时连接不够用。
  5. 字段设计考虑扩展:比如 status 字段用 tinyint,比直接用 varchar 存储'已审核'、'已完成'更高效,且便于加索引。

把这个项目做下来,你会发现,它不仅仅是一个'能跑通'的作业。你理解了分层架构的价值,实战了事务和并发控制,考虑了安全和性能。这已经远超一个普通课程设计的要求了。

扩展思考: 如果你的系统要支持多个仓库,库存数据该如何设计?是每个仓库一张库存表,还是所有仓库库存放在一张表里加 warehouse_id 字段?分布式环境下,如何保证跨仓库调拨库存的数据一致性?这就可以引申到分布式事务(如 Seata)或最终一致性方案(如消息队列)的探讨。

或者,你可以动手扩展一个'供应商管理'模块,建立商品与供应商的多对多关系,实现采购订单的完整生命周期管理。这会让你的系统更加丰满。

目录

  1. 基于 Spring Boot 与 MySQL 的仓库管理系统设计与实现
  2. 1. 技术选型:Spring Boot + MyBatis + MySQL
  3. 2. 清晰的分层架构
  4. 3. 核心难点:库存扣减的并发与事务
  5. 4. 关键代码片段:商品入库接口
  6. 5. 安全与性能:容易被忽略的细节
  7. 6. 生产环境避坑指南

更多推荐文章

查看全部
  • FANUC 机器人 PR 寄存器详解
  • 2023 年十大前景看好的编程语言
  • 基于 JavaAI 辅助生成 SpringBoot 图书借阅平台实战
  • Copilot 与 Codeium 等 AI 代码助手技术解析
  • 基于 Java、GeoTools 与 PostGIS 的对跖点求解实战
  • jQuery 4.0 正式发布:重构核心,拥抱现代 Web
  • XXHash64:非加密哈希算法的速度与架构解析
  • Gitee 推送报错 400/403 排查与 SSH 协议解决方案
  • npm ERR! code 128 未知 Git 错误处理方案
  • Python 爬取京东商品评论:API 接口与反爬策略解析
  • 三款主流云电脑部署 DeepSeek 模型实测对比
  • AI 原生应用开发:Llama 模型核心概念与应用基础
  • 2025 团体程序设计天梯赛 L1-L2 题解(C++)
  • 文心一言开源版测评:能力、易用性与价值解析
  • 开源 AI 编程工具选型对比:OpenCode 与 GitHub Copilot
  • 基于 SSM 框架的 Java 酒店管理系统设计与实现
  • Llama-Factory 中启用梯度裁剪保护训练稳定性
  • Spring Cloud 熔断降级详解:Sentinel 实战与原理类比
  • 飞书 CLI 开源:让 AI 真正接管你的飞书全流程
  • 顺序文件的基本概念与查找算法

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online