# program03-Product **Repository Path**: lsq-eternally/program03-product ## Basic Information - **Project Name**: program03-Product - **Description**: 模拟在线商城的库存管理业务,尤其针对商品库存的高并发更新场景。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-03-16 - **Last Updated**: 2025-03-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 一、项目需求 ​ 模拟在线商城的库存管理业务,尤其针对商品库存的高并发更新场景。具体要求如下: - 创建商品实体(Product),包含商品编号、商品名、库存数量。 - 在商品购买操作时,使用乐观锁机制(@Version)避免库存超卖问题。 - 提供悲观锁的另一种方案以应对秒杀场景。 - 设计并执行压力测试,观察两种锁机制下数据库数据的一致性与性能表现。 ## 二、项目核心功能 - 商品信息的保存和更新。 - 使用乐观锁机制进行商品购买操作,避免库存超卖。 - 使用悲观锁机制进行商品购买操作,应对高并发的秒杀场景。 ## 三、项目环境 - **项目名称**:Program03 - **开发语言**:Java - **项目类型**:Maven - **项目组**:com.example - **项目 SDK**:JDK 11 - **数据库**:MySQL 8.0 - **开发工具**:IntelliJ IDEA ## 四、项目依赖 - Spring Boot Starter Web - Spring Boot Starter Data JPA - Lombok - MySQL Driver - Spring Boot Maven Plugin ## 五、项目结构 ```plaintext ├─main │ ├─java │ │ └─com │ │ └─example │ │ │ Application.java │ │ │ │ │ ├─controller │ │ │ ProductController.java │ │ │ │ │ ├─entity │ │ │ Product.java │ │ │ │ │ ├─repository │ │ │ ProductRepository.java │ │ │ │ │ └─service │ │ ProductService.java │ │ │ └─resources │ application.properties │ └─test └─java ``` ## 六、数据库设计 **商品表(product)** | 字段名 | 类型 | 是否为空 | 描述 | | ------- | ------------ | -------- | -------------- | | id | bigint | 否 | 商品编号,主键 | | name | varchar(255) | 否 | 商品名 | | stock | int | 否 | 库存数量 | | version | int | 否 | 乐观锁版本号 | ## 七、 API调用 ### 1、乐观锁购买商品 - **请求 URL**:`POST /products/{id}/purchase-optimistic` - 请求参数: - `id`:商品编号,路径参数 - `quantity`:购买数量,查询参数 - 响应数据: - 成功:`Purchase successful (Optimistic Lock)` - 失败:`Purchase failed: Not enough stock (Optimistic Lock)` ### 2、悲观锁购买商品 - **请求 URL**:`POST /products/{id}/purchase-pessimistic` - 请求参数: - `id`:商品编号,路径参数 - `quantity`:购买数量,查询参数 - 响应数据: - 成功:`Purchase successful (Pessimistic Lock)` - 失败:`Purchase failed: Not enough stock (Pessimistic Lock)` ## 八、测试用例(使用 Postman) **1、乐观锁购买商品测试** (1)打开 Postman,选择`POST`请求方式。 (2)输入请求 URL:`http://localhost:8080/products/1/purchase-optimistic`。 (3)在`Params`中添加参数`quantity`,值为`1`。 (4)点击`Send`发送请求,查看响应结果。 **2、悲观锁购买商品测试** (1)打开 Postman,选择`POST`请求方式。 (2)输入请求 URL:`http://localhost:8080/products/1/purchase-pessimistic`。 (3)在`Params`中添加参数`quantity`,值为`1`。 (4)点击`Send`发送请求,查看响应结果。 **3、压力测试** 使用Jmeter进行压力测试 - **线程数**:设置模拟的并发用户数量 100,表示同时有 100 个用户发起请求。 - **准备时长**:设置线程启动的时间间隔10 秒,意味着 100 个线程将在 10 秒内均匀启动。 - **循环次数**:设置每个线程发送请求的次数为 10,表示每个线程将发送 10 次请求。 ![image-20250321140635910](https://gitee.com/lsq-eternally/image01/raw/master/20250321140635984.png) ## 九、核心业务代码展示 **控制器类** ```java @RestController @RequestMapping("/products") public class ProductController { @Autowired private ProductService productService; @PostMapping("/{id}/purchase-optimistic") public String purchaseProductOptimistic(@PathVariable Long id, @RequestParam int quantity) { boolean success = productService.purchaseProductOptimistic(id, quantity); return success ? "Purchase successful (Optimistic Lock)" : "Purchase failed: Not enough stock (Optimistic Lock)"; } @PostMapping("/{id}/purchase-pessimistic") public String purchaseProductPessimistic(@PathVariable Long id, @RequestParam int quantity) { boolean success = productService.purchaseProductPessimistic(id, quantity); return success ? "Purchase successful (Pessimistic Lock)" : "Purchase failed: Not enough stock (Pessimistic Lock)"; } } ``` **仓库类** ```java package com.example.repository; import com.example.entity.Product; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Lock; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import javax.persistence.LockModeType; import java.util.Optional; public interface ProductRepository extends JpaRepository { @Override S save(S entity); @Lock(LockModeType.PESSIMISTIC_WRITE) @Query("SELECT p FROM Product p WHERE p.id = ?1") Optional findByIdWithPessimisticLock(@Param("id") Long id); } ```