在实际企业级应用开发中仓库管理系统Warehouse Management System, WMS是连接采购、销售、库存和财务的核心业务系统。一个设计良好的WMS不仅能实现基础的出入库管理更需要处理复杂的库存批次、多仓库调拨、库存预警和报表分析。对于Java开发者而言使用SpringBoot快速构建一个稳定、可扩展的WMS后端服务是检验全栈工程能力的一个典型场景。本文将以一个基于SpringBoot 2.7.x、MyBatis-Plus、Thymeleaf和MySQL的仓库管理系统为例从零开始详细拆解其技术选型、环境搭建、核心模块实现、数据库设计、前后端交互以及生产环境部署的完整链路。无论你是希望学习SpringBoot整合主流技术栈还是需要为课程设计或中小型企业寻找一个可落地的WMS解决方案本文都将提供一个清晰、可复现的实践指南。1. 理解仓库管理系统的核心业务与技术栈选型在动手编码之前必须明确系统要解决的核心业务问题这直接决定了数据库表结构设计和接口的复杂度。一个基础的仓库管理系统通常包含以下几个核心模块用户与权限管理、商品/物料主数据管理、仓库与库位管理、采购入库、销售出库、库存调拨、库存盘点以及多维度的报表统计。1.1 核心业务实体与关系系统的核心是“库存”它由多个实体关系共同维护商品Product库存的客体具有SKU、名称、规格等属性。仓库Warehouse与库位Storage库存的位置支持多级管理。库存记录Inventory核心实体记录某个商品在某个库位的实时数量。单据Order驱动库存变化的业务凭证如采购单、销售单、调拨单。每张单据会关联多条单据明细OrderItem。库存数量的变化遵循一个基本原则任何实物移动都必须有单据凭证。入库增加库存出库减少库存调拨则是一个仓库减少另一个仓库增加。1.2 技术栈选型与职责划分根据输入材料中提到的技术关键词我们采用以下成熟稳定的技术组合SpringBoot 2.7.18作为项目基石提供自动配置、内嵌Web容器和便捷的依赖管理极大简化了传统SSM框架的繁琐配置。Maven项目构建与依赖管理工具。MyBatis-Plus在MyBatis基础上增强提供通用的CRUD操作、分页插件、条件构造器等能显著减少单表操作的SQL编写工作量。MySQL 8.0关系型数据库用于存储业务数据。其事务特性ACID对保证库存数据的一致性至关重要。Thymeleaf服务器端Java模板引擎用于渲染后端返回的HTML页面。它语法自然能与SpringBoot无缝集成适合快速开发管理后台。Ajax异步JavaScript和XML技术用于实现页面的局部刷新提升用户体验如表单提交、数据搜索等操作无需重载整个页面。Lombok通过注解自动生成Java Bean的getter、setter、构造方法等代码保持代码简洁。这个技术栈的特点是“约定大于配置”开发者可以更专注于业务逻辑而非框架整合。2. 项目初始化与环境准备一个清晰的起点是成功的一半。我们将严格按照企业级项目的标准来初始化工程结构。2.1 使用Spring Initializr创建项目推荐使用IntelliJ IDEA内置的Spring Initializr它比手动配置更高效。打开IDEA选择File - New - Project。选择Spring Initializr选择合适的JDK版本推荐JDK 8或11。填写项目元数据Group:com.yourcompanyArtifact:warehouse-managementType:MavenJava Version:8或11Packaging:Jar在Dependencies中搜索并添加以下依赖Spring Web(构建Web应用)Thymeleaf(模板引擎)MyBatis Framework(或直接添加MyBatis-Plus的依赖稍后手动配置)MySQL Driver(数据库驱动)Lombok(简化代码)点击FinishIDEA会自动生成项目并下载依赖。2.2 手动添加关键依赖与配置Spring Initializr可能没有MyBatis-Plus我们需要在pom.xml中手动添加。同时为了处理JSON和分页我们还需要添加其他依赖。!-- 在 pom.xml 的 dependencies 节点内添加 -- !-- MyBatis-Plus 启动器 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency !-- MyBatis-Plus 代码生成器 (可选用于开发阶段快速生成代码) -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-generator/artifactId version3.5.3.1/version scopetest/scope /dependency !-- HikariCP 连接池 (SpringBoot 2.x默认确保存在) -- dependency groupIdcom.zaxxer/groupId artifactIdHikariCP/artifactId /dependency !-- Jackson 用于JSON处理 (通常Spring Web已包含) -- dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId /dependency !-- 参数校验 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency2.3 数据库配置与初始化在src/main/resources/application.yml(或application.properties) 中配置数据库连接和MyBatis-Plus。# application.yml spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/warehouse_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: yourpassword hikari: connection-timeout: 30000 maximum-pool-size: 20 minimum-idle: 5 thymeleaf: cache: false # 开发阶段关闭缓存修改HTML立即生效 mode: HTML encoding: UTF-8 # MyBatis-Plus 配置 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印SQL生产环境关闭 map-underscore-to-camel-case: true # 自动将下划线字段映射为驼峰属性 global-config: db-config: id-type: auto # 主键策略数据库自增 logic-delete-field: deleted # 全局逻辑删除字段名如果使用 logic-delete-value: 1 # 逻辑已删除值 logic-not-delete-value: 0 # 逻辑未删除值 mapper-locations: classpath:mapper/*.xml # XML映射文件位置使用MySQL客户端如MySQL Workbench或命令行创建数据库CREATE DATABASE IF NOT EXISTS warehouse_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;3. 数据库设计与核心实体映射数据库设计是系统的骨架。我们设计一个简化但核心功能完整的表结构。3.1 核心表结构SQL以下是关键表的创建语句包含了必要的字段、索引和注释。-- 用户表 CREATE TABLE sys_user ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键, username varchar(50) NOT NULL COMMENT 用户名, password varchar(100) NOT NULL COMMENT 密码加密后, real_name varchar(50) DEFAULT NULL COMMENT 真实姓名, phone varchar(20) DEFAULT NULL COMMENT 电话, role varchar(20) DEFAULT operator COMMENT 角色admin, manager, operator, status tinyint DEFAULT 1 COMMENT 状态0-禁用1-启用, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT系统用户表; -- 商品表 CREATE TABLE product ( id bigint NOT NULL AUTO_INCREMENT, product_code varchar(50) NOT NULL COMMENT 商品编码/SKU, product_name varchar(200) NOT NULL COMMENT 商品名称, spec varchar(200) DEFAULT NULL COMMENT 规格型号, unit varchar(20) DEFAULT NULL COMMENT 单位如个箱, category varchar(100) DEFAULT NULL COMMENT 分类, remark varchar(500) DEFAULT NULL COMMENT 备注, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_product_code (product_code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品信息表; -- 仓库表 CREATE TABLE warehouse ( id bigint NOT NULL AUTO_INCREMENT, warehouse_code varchar(50) NOT NULL COMMENT 仓库编码, warehouse_name varchar(200) NOT NULL COMMENT 仓库名称, location varchar(500) DEFAULT NULL COMMENT 仓库地址, manager varchar(50) DEFAULT NULL COMMENT 负责人, status tinyint DEFAULT 1 COMMENT 状态0-停用1-启用, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_warehouse_code (warehouse_code) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT仓库表; -- 库存表 (核心) CREATE TABLE inventory ( id bigint NOT NULL AUTO_INCREMENT, product_id bigint NOT NULL COMMENT 商品ID, warehouse_id bigint NOT NULL COMMENT 仓库ID, quantity decimal(15,3) NOT NULL DEFAULT 0.000 COMMENT 当前数量, lock_quantity decimal(15,3) NOT NULL DEFAULT 0.000 COMMENT 锁定数量如已下单未出库, alert_quantity decimal(15,3) DEFAULT 0.000 COMMENT 库存预警数量, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_product_warehouse (product_id,warehouse_id), -- 唯一约束一个商品在一个仓库只有一条记录 KEY idx_warehouse_id (warehouse_id), CONSTRAINT fk_inventory_product FOREIGN KEY (product_id) REFERENCES product (id), CONSTRAINT fk_inventory_warehouse FOREIGN KEY (warehouse_id) REFERENCES warehouse (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT库存表; -- 单据主表 CREATE TABLE warehouse_order ( id bigint NOT NULL AUTO_INCREMENT, order_no varchar(50) NOT NULL COMMENT 单据编号, order_type varchar(20) NOT NULL COMMENT 单据类型purchase-采购入库sale-销售出库transfer-调拨, from_warehouse_id bigint DEFAULT NULL COMMENT 源仓库ID调拨、出库时有值, to_warehouse_id bigint DEFAULT NULL COMMENT 目标仓库ID调拨、入库时有值, total_amount decimal(15,2) DEFAULT NULL COMMENT 总金额, status varchar(20) DEFAULT draft COMMENT 状态draft-草稿confirmed-已确认completed-已完成cancelled-已取消, create_user_id bigint DEFAULT NULL COMMENT 制单人, confirm_user_id bigint DEFAULT NULL COMMENT 确认人, remark varchar(500) DEFAULT NULL COMMENT 备注, create_time datetime DEFAULT CURRENT_TIMESTAMP, confirm_time datetime DEFAULT NULL, complete_time datetime DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_order_type (order_type), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT仓库单据主表; -- 单据明细表 CREATE TABLE order_item ( id bigint NOT NULL AUTO_INCREMENT, order_id bigint NOT NULL COMMENT 单据ID, product_id bigint NOT NULL COMMENT 商品ID, quantity decimal(15,3) NOT NULL COMMENT 数量, unit_price decimal(15,2) DEFAULT NULL COMMENT 单价, total_price decimal(15,2) DEFAULT NULL COMMENT 总价, remark varchar(200) DEFAULT NULL COMMENT 备注, PRIMARY KEY (id), KEY idx_order_id (order_id), CONSTRAINT fk_item_order FOREIGN KEY (order_id) REFERENCES warehouse_order (id), CONSTRAINT fk_item_product FOREIGN KEY (product_id) REFERENCES product (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT单据明细表;3.2 实体类与MyBatis-Plus映射使用Lombok和MyBatis-Plus注解创建对应的Java实体类。以Inventory实体为例package com.yourcompany.warehouse.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; Data TableName(inventory) // 指定表名 public class Inventory { TableId(type IdType.AUTO) // 主键自增 private Long id; private Long productId; private Long warehouseId; private BigDecimal quantity; // 使用BigDecimal处理精确小数 private BigDecimal lockQuantity; private BigDecimal alertQuantity; private LocalDateTime updateTime; }注意金额和数量字段务必使用BigDecimal类型避免使用float或double导致精度丢失这是金融和仓储系统的基本要求。4. 核心业务逻辑实现与分层架构我们采用经典的三层架构Controller控制层、Service业务逻辑层、Mapper数据访问层。这里以库存查询和创建采购入库单两个核心流程为例。4.1 数据访问层Mapper与XMLMyBatis-Plus提供了强大的BaseMapper对于单表CRUD只需继承它即可获得通用方法。对于复杂联表查询可以编写XML映射文件。首先创建InventoryMapper接口package com.yourcompany.warehouse.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.yourcompany.warehouse.entity.Inventory; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import java.util.List; import java.util.Map; public interface InventoryMapper extends BaseMapperInventory { // 复杂查询查询某个仓库下所有商品的库存及商品信息 Select(SELECT i.*, p.product_code, p.product_name, p.spec, p.unit FROM inventory i LEFT JOIN product p ON i.product_id p.id WHERE i.warehouse_id #{warehouseId}) ListMapString, Object selectInventoryDetailByWarehouse(Param(warehouseId) Long warehouseId); }4.2 业务逻辑层Service与事务管理业务层负责处理核心逻辑如库存数量的增减必须在一个事务内完成保证数据一致性。创建InventoryService接口及其实现package com.yourcompany.warehouse.service; import com.baomidou.mybatisplus.extension.service.IService; import com.yourcompany.warehouse.entity.Inventory; import java.math.BigDecimal; public interface InventoryService extends IServiceInventory { /** * 增加库存采购入库、调拨入库 * param productId 商品ID * param warehouseId 仓库ID * param quantity 增加的数量 * return 是否成功 */ boolean increaseInventory(Long productId, Long warehouseId, BigDecimal quantity); /** * 减少库存销售出库、调拨出库 * param productId 商品ID * param warehouseId 仓库ID * param quantity 减少的数量 * return 是否成功库存不足则失败 */ boolean decreaseInventory(Long productId, Long warehouseId, BigDecimal quantity); }package com.yourcompany.warehouse.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yourcompany.warehouse.entity.Inventory; import com.yourcompany.warehouse.mapper.InventoryMapper; import com.yourcompany.warehouse.service.InventoryService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.time.LocalDateTime; Slf4j Service public class InventoryServiceImpl extends ServiceImplInventoryMapper, Inventory implements InventoryService { Override Transactional(rollbackFor Exception.class) // 声明式事务异常回滚 public boolean increaseInventory(Long productId, Long warehouseId, BigDecimal quantity) { if (quantity.compareTo(BigDecimal.ZERO) 0) { throw new IllegalArgumentException(入库数量必须大于0); } LambdaQueryWrapperInventory wrapper new LambdaQueryWrapper(); wrapper.eq(Inventory::getProductId, productId) .eq(Inventory::getWarehouseId, warehouseId); Inventory inventory this.getOne(wrapper); if (inventory null) { // 如果该商品在该仓库尚无库存记录则创建一条 inventory new Inventory(); inventory.setProductId(productId); inventory.setWarehouseId(warehouseId); inventory.setQuantity(quantity); inventory.setLockQuantity(BigDecimal.ZERO); inventory.setAlertQuantity(BigDecimal.ZERO); inventory.setUpdateTime(LocalDateTime.now()); return this.save(inventory); } else { // 已有记录则增加数量 BigDecimal newQuantity inventory.getQuantity().add(quantity); inventory.setQuantity(newQuantity); inventory.setUpdateTime(LocalDateTime.now()); return this.updateById(inventory); } } Override Transactional(rollbackFor Exception.class) public boolean decreaseInventory(Long productId, Long warehouseId, BigDecimal quantity) { if (quantity.compareTo(BigDecimal.ZERO) 0) { throw new IllegalArgumentException(出库数量必须大于0); } LambdaQueryWrapperInventory wrapper new LambdaQueryWrapper(); wrapper.eq(Inventory::getProductId, productId) .eq(Inventory::getWarehouseId, warehouseId); Inventory inventory this.getOne(wrapper, false); // 这里不加锁高并发场景下需用悲观锁或乐观锁 if (inventory null) { log.error(商品ID:{}在仓库ID:{}中无库存记录, productId, warehouseId); return false; } BigDecimal availableQuantity inventory.getQuantity().subtract(inventory.getLockQuantity()); if (availableQuantity.compareTo(quantity) 0) { log.error(商品ID:{}在仓库ID:{}中可用库存不足。可用:{}, 需求:{}, productId, warehouseId, availableQuantity, quantity); return false; } BigDecimal newQuantity inventory.getQuantity().subtract(quantity); inventory.setQuantity(newQuantity); inventory.setUpdateTime(LocalDateTime.now()); return this.updateById(inventory); } }关键点Transactional注解确保了库存增减操作的原子性。在高并发场景下上述代码存在超卖风险多个线程同时读取到相同的可用库存并都认为足够。生产环境必须使用悲观锁SELECT ... FOR UPDATE或乐观锁版本号字段来解决。4.3 控制层RESTful API与页面跳转Controller层接收前端请求调用Service并返回结果。我们同时支持返回JSON供Ajax调用和跳转Thymeleaf页面。package com.yourcompany.warehouse.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.yourcompany.warehouse.entity.WarehouseOrder; import com.yourcompany.warehouse.service.WarehouseOrderService; import com.yourcompany.warehouse.vo.OrderQueryVO; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.util.HashMap; import java.util.Map; Slf4j Controller RequestMapping(/order) public class WarehouseOrderController { Autowired private WarehouseOrderService orderService; // 跳转到采购单列表页面 GetMapping(/purchase/list) public String purchaseListPage(Model model) { // 可以在这里初始化一些页面数据如仓库下拉列表 return order/purchase_list; // 对应 src/main/resources/templates/order/purchase_list.html } // 处理Ajax请求返回采购单分页数据 (JSON格式) PostMapping(/purchase/data) ResponseBody // 表示返回JSON而不是视图名 public MapString, Object getPurchaseOrderData(RequestBody OrderQueryVO queryVO) { PageWarehouseOrder page orderService.queryPurchaseOrderPage(queryVO); MapString, Object result new HashMap(); result.put(code, 0); result.put(msg, success); result.put(count, page.getTotal()); result.put(data, page.getRecords()); return result; } // 创建采购单 (接收JSON请求体) PostMapping(/purchase/create) ResponseBody public MapString, Object createPurchaseOrder(Valid RequestBody WarehouseOrder order) { MapString, Object result new HashMap(); try { boolean success orderService.createPurchaseOrder(order); if (success) { result.put(code, 200); result.put(msg, 采购单创建成功); } else { result.put(code, 500); result.put(msg, 采购单创建失败请检查库存或数据); } } catch (Exception e) { log.error(创建采购单异常, e); result.put(code, 500); result.put(msg, 系统错误: e.getMessage()); } return result; } }5. 前端页面与Thymeleaf、Ajax集成Thymeleaf模板与Ajax的结合能构建出交互良好的管理后台。5.1 Thymeleaf页面基础结构在src/main/resources/templates/order/purchase_list.html中!DOCTYPE html html langzh xmlns:thhttp://www.thymeleaf.org head meta charsetUTF-8 title采购单管理/title !-- 引入Bootstrap等CSS -- link relstylesheet th:href{/webjars/bootstrap/5.1.3/css/bootstrap.min.css} !-- 引入Layui等前端组件库CSS (示例) -- link relstylesheet th:href{/static/layui/css/layui.css} /head body div classcontainer-fluid h2采购单列表/h2 !-- 查询条件表单 -- form classform-inline layui-form idsearchForm div classform-group label单据编号/label input typetext nameorderNo classform-control placeholder请输入单据编号 /div div classform-group label状态/label select namestatus classform-control option value全部/option option valuedraft草稿/option option valueconfirmed已确认/option /select /div button typebutton classbtn btn-primary onclickloadTableData()搜索/button button typebutton classbtn btn-success onclickshowCreateModal()新建采购单/button /form !-- 数据表格 -- table idorderTable classlayui-table lay-filterorderTable/table /div !-- 引入jQuery、Layui等JS -- script th:src{/webjars/jquery/3.6.0/jquery.min.js}/script script th:src{/static/layui/layui.js}/script script // 使用Layui的table模块 layui.use(table, function(){ var table layui.table; // 渲染表格 window.loadTableData function() { var formData $(#searchForm).serializeJSON(); // 需要引入serializeJSON插件或手动拼接 table.render({ elem: #orderTable, url: /order/purchase/data, // Ajax请求地址 method: post, contentType: application/json, where: JSON.stringify(formData), // 传递查询条件 page: true, // 开启分页 cols: [[ {field: orderNo, title: 单据编号, width: 180}, {field: orderType, title: 类型, width: 100}, {field: totalAmount, title: 总金额, width: 120}, {field: status, title: 状态, width: 100, templet: function(d){ var statusMap {draft:草稿,confirmed:已确认,completed:已完成}; return statusMap[d.status] || d.status; }}, {field: createTime, title: 创建时间, width: 180}, {title: 操作, width: 150, toolbar: #operateBar} ]] }); }; // 初始加载数据 loadTableData(); }); // 显示创建采购单的模态框 window.showCreateModal function() { // 这里可以通过layer.open打开一个包含表单的弹出层 layer.open({ type: 2, title: 新建采购单, area: [800px, 600px], content: /order/purchase/create/page // 另一个Controller方法返回创建页面的HTML片段 }); }; /script !-- 操作列模板 -- script typetext/html idoperateBar a classlayui-btn layui-btn-xs lay-eventdetail查看/a {{# if(d.status draft){ }} a classlayui-btn layui-btn-xs layui-btn-warm lay-eventconfirm确认/a a classlayui-btn layui-btn-xs layui-btn-danger lay-eventdelete删除/a {{# } }} /script /body /html5.2 Ajax提交与后端交互创建采购单的Ajax请求示例// 在创建采购单的模态框表单提交事件中 function submitPurchaseOrder() { var formData collectFormData(); // 收集表单数据 $.ajax({ url: /order/purchase/create, type: POST, contentType: application/json, data: JSON.stringify(formData), success: function(res) { if (res.code 200) { layer.msg(创建成功); layer.closeAll(); // 关闭弹出层 loadTableData(); // 刷新列表 } else { layer.msg(创建失败 res.msg); } }, error: function() { layer.msg(网络请求失败); } }); }6. 项目运行、验证与常见问题排查6.1 启动与访问找到主启动类WarehouseManagementApplication通常位于src/main/java/com/yourcompany/warehouse下运行其main方法。控制台看到Started ... in X seconds表示启动成功。打开浏览器访问http://localhost:8080。你需要编写一个简单的IndexController来跳转到登录页或仪表盘。按照业务逻辑依次测试用户登录、商品管理、仓库管理、创建采购单、审核采购单、查询库存变化。6.2 关键验证点数据库连接启动时无DataSource相关错误。页面渲染能正常访问.html页面Thymeleaf标签被正确解析。Ajax交互浏览器开发者工具F12的Network标签中能看到Ajax请求成功状态码200并返回预期的JSON数据。业务逻辑创建采购单后对应商品的库存数量应增加。创建销售单后库存应减少且不能减为负数。事务测试在入库Service方法中故意抛出异常查看数据库数据是否回滚。6.3 常见问题与排查路径在开发过程中你几乎一定会遇到以下问题。请按此清单排查问题现象可能原因检查方式处理建议启动报错Failed to configure a DataSource1.application.yml中数据库配置错误。2. MySQL服务未启动。3. 依赖未正确引入。1. 检查yml文件缩进和冒号后的空格。2. 检查MySQL服务状态和端口。3. 检查pom.xml中mysql-connector-java依赖。1. 确保配置格式正确。2. 启动MySQL服务。3. 执行mvn clean compile。访问页面报4041. Controller请求路径映射错误。2. 静态资源未放行。3. Thymeleaf模板文件位置错误。1. 检查Controller的RequestMapping和方法上的GetMapping/PostMapping。2. 检查SpringBoot静态资源默认目录 (static,public)。3. 检查HTML文件是否在templates目录下。1. 使用IDEA的“Find Usages”功能检查路径。2. 自定义WebMvcConfigurer放行资源。3. 确保路径与Controller返回的字符串一致。Ajax请求报405 (Method Not Allowed)Controller方法请求方式不匹配如前端用POST后端是GET。查看浏览器Network面板中请求的Method并与后端RequestMapping的method属性或PostMapping等注解对比。统一前后端请求方式。页面显示${...}原样文本Thymeleaf未正确解析。1. 检查HTML文件开头是否有xmlns:thhttp://www.thymeleaf.org。2. 检查是否错误地访问了.html静态文件而非经过Controller跳转。1. 确保命名空间正确。2. 页面必须通过Controller的return pageName;来访问。MyBatis-Plus查询不到数据1. 实体类字段名与数据库列名映射失败。2. 查询条件构造错误。3. 数据本身不存在。1. 开启SQL日志 (mybatis-plus.configuration.log-impl)查看实际执行的SQL。2. 检查TableField注解或全局的驼峰转换配置。3. 直接在数据库客户端执行打印出的SQL。1. 确认map-underscore-to-camel-case: true已配置。2. 使用LambdaQueryWrapper避免字段名拼写错误。事务不生效1. 方法不是public。2. 异常被捕获未抛出。3. 方法在同一个类内部调用未经过代理。1. 检查方法访问修饰符。2. 检查Transactional的rollbackFor属性。3. 检查调用方式应通过Spring代理对象调用。1. 确保方法是public。2. 设置Transactional(rollbackFor Exception.class)。3. 将事务方法放到另一个Service中通过Autowired注入调用。7. 生产环境部署与优化建议将学习项目推向生产环境需要考虑更多因素。7.1 应用打包与部署打包使用Maven命令mvn clean package -DskipTests生成可执行的JAR文件位于target/目录下。分离配置将application.yml中的敏感信息数据库密码和与环境相关的配置服务器端口提取到外部的application-prod.yml并通过启动参数--spring.config.location指定。java -jar warehouse-management.jar --spring.config.locationfile:/path/to/application-prod.yml进程管理使用systemd(Linux) 或nssm(Windows) 将JAR包注册为系统服务实现开机自启和故障重启。7.2 数据库优化索引为高频查询条件如order_no,status,create_time和关联字段外键建立索引。连接池调整HikariCP参数如maximum-pool-size根据数据库和服务器的CPU、内存调整通常建议在10-50之间。慢查询日志开启MySQL慢查询日志定期分析并优化执行时间长的SQL。7.3 应用性能与安全关闭开发配置生产环境务必关闭mybatis-plus.configuration.log-impl的SQL打印和thymeleaf.cache。API限流与鉴权为关键业务接口如库存扣减添加限流如使用Sentinel防止恶意请求。实现完整的基于角色的访问控制RBAC。输入校验在Controller层使用Valid注解配合JSR-303校验注解如NotBlank,Min并在全局异常处理器中处理MethodArgumentNotValidException返回友好的错误信息。全局异常处理使用ControllerAdvice和ExceptionHandler统一处理业务异常和系统异常避免将堆栈信息直接暴露给前端。库存并发控制将decreaseInventory方法中的查询和更新操作放在一个数据库事务中并使用悲观锁SELECT ... FOR UPDATE或为inventory表增加version字段实现乐观锁这是防止超卖的关键。7.4 监控与日志日志配置logback-spring.xml将日志按级别INFO, ERROR输出到不同文件并设置合理的滚动策略和保存天数。健康检查Spring Boot Actuator 提供/actuator/health端点可用于监控应用状态。APM考虑集成SkyWalking、Pinpoint等应用性能监控工具追踪请求链路和SQL性能。通过以上步骤你不仅能够搭建并运行一个基础的SpringBoot仓库管理系统更能理解从数据库设计、业务编码到生产部署的完整闭环。这个项目可以作为你深入理解SpringBoot生态和复杂业务系统设计的起点后续可以在此基础上扩展更复杂的特性如多级审批流、序列号管理、财务结算集成等。