image.png
image.png
image.png
数据库
orders、order_item、product
image.png
image.png
image.png

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>2.1.3.RELEASE</version>
  5. <relativePath/> <!-- lookup parent from repository -->
  6. </parent>
  7. <dependencies>
  8. <dependency>
  9. <groupId>org.springframework.boot</groupId>
  10. <artifactId>spring-boot-starter-web</artifactId>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.springframework.boot</groupId>
  14. <artifactId>spring-boot-starter-data-jpa</artifactId>
  15. </dependency>
  16. <dependency>
  17. <groupId>mysql</groupId>
  18. <artifactId>mysql-connector-java</artifactId>
  19. </dependency>
  20. <dependency>
  21. <groupId>org.projectlombok</groupId>
  22. <artifactId>lombok</artifactId>
  23. <scope>provided</scope>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.springframework.boot</groupId>
  27. <artifactId>spring-boot-starter-test</artifactId>
  28. <scope>test</scope>
  29. </dependency>
  30. </dependencies>
  1. @Data
  2. @NoArgsConstructor
  3. @AllArgsConstructor
  4. @Entity
  5. @Table(name = "orders", schema = "fzs")
  6. public class OrdersEntity {
  7. @GeneratedValue(strategy = GenerationType.IDENTITY)
  8. @Id
  9. @Column(name = "iid")
  10. private int iid;
  11. @Basic
  12. @Column(name = "order_status")
  13. private int orderStatus;
  14. @Basic
  15. @Column(name = "receiver_name")
  16. private String receiverName;
  17. @Basic
  18. @Column(name = "receiver_mobile")
  19. private String receiverMobile;
  20. @Basic
  21. @Column(name = "order_amount")
  22. private BigDecimal orderAmount;
  23. @Basic
  24. @Column(name = "create_time")
  25. private Date createTime;
  26. @Basic
  27. @Column(name = "create_user")
  28. private String createUser;
  29. @Basic
  30. @Column(name = "update_time")
  31. private Date updateTime;
  32. }
  1. @Data
  2. @NoArgsConstructor
  3. @AllArgsConstructor
  4. @Entity
  5. @Table(name = "order_item")
  6. public class OrderItemEntity {
  7. @GeneratedValue(strategy = GenerationType.IDENTITY)
  8. @Id
  9. @Column(name = "iid")
  10. private int iid;
  11. @Basic
  12. @Column(name = "order_id")
  13. private int orderId;
  14. @Basic
  15. @Column(name = "product_id")
  16. private int productId;
  17. @Basic
  18. @Column(name = "purchase_price")
  19. private BigDecimal purchasePrice;
  20. @Basic
  21. @Column(name = "purchase_num")
  22. private int purchaseNum;
  23. @Basic
  24. @Column(name = "create_time")
  25. private Date createTime;
  26. @Basic
  27. @Column(name = "create_user")
  28. private String createUser;
  29. @Basic
  30. @Column(name = "update_time")
  31. private Date updateTime;
  32. }
  1. @Data
  2. @NoArgsConstructor
  3. @AllArgsConstructor
  4. @Entity
  5. @Table(name = "product")
  6. public class ProductEntity {
  7. @GeneratedValue(strategy = GenerationType.IDENTITY)
  8. @Id
  9. @Column(name = "id")
  10. private int id;
  11. @Basic
  12. @Column(name = "product_name")
  13. private String productName;
  14. @Basic
  15. @Column(name = "price")
  16. private BigDecimal price;
  17. @Basic
  18. @Column(name = "count")
  19. private int count;
  20. @Basic
  21. @Column(name = "product_desc")
  22. private String productDesc;
  23. @Basic
  24. @Column(name = "create_time")
  25. private Date createTime;
  26. @Basic
  27. @Column(name = "create_user")
  28. private String createUser;
  29. @Basic
  30. @Column(name = "update_time")
  31. private Date updateTime;
  32. }
  1. @Repository
  2. public interface OrderItemRepository extends JpaRepository<OrderItemEntity, Integer> {
  3. }
  1. @Repository
  2. public interface OrderRepository extends JpaRepository<OrdersEntity, Integer> {
  3. }
  1. @Repository
  2. public interface ProductRepository extends JpaRepository<ProductEntity, Integer> {
  3. /**
  4. * 总数减去orderCount
  5. * @param orderCount
  6. * @param orderId
  7. */
  8. @Modifying
  9. @Query(value = "update product set count = count - :orderCount, update_time =:update_time where id = :orderId", nativeQuery = true)
  10. void updateProductCount(@Param("orderCount") Integer orderCount,
  11. @Param("update_time") Date updateTime,
  12. @Param("orderId") Integer orderId);
  13. }

模拟实现

  1. public interface OrderService {
  2. int orderCreate();
  3. }
  1. @Service
  2. public class OrderServiceImpl implements OrderService {
  3. @Autowired
  4. private ProductRepository productRepository;
  5. @Resource
  6. private OrderRepository orderRepository;
  7. @Resource
  8. private OrderItemRepository orderItemRepository;
  9. private int purchaseProductId = 100100;
  10. private int purchaseProductNum = 1;
  11. @Override
  12. @Transactional(rollbackOn = Exception.class)
  13. public int orderCreate() {
  14. ProductEntity productEntity = productRepository.findById(purchaseProductId).orElseGet(ProductEntity::new);
  15. if (productEntity == null) {
  16. throw new RuntimeException("购买商品:" + purchaseProductId + "不存在");
  17. }
  18. //商品库存
  19. int count = productEntity.getCount();
  20. if (purchaseProductNum > count) {
  21. throw new RuntimeException("商品" + purchaseProductId + "库存不足,当前库存:" + count);
  22. }
  23. // 计算剩余库存
  24. Integer leftCount = count - purchaseProductNum;
  25. // 更新商品库存
  26. productEntity.setCount(leftCount);
  27. productEntity.setUpdateTime(new Date());
  28. productRepository.save(productEntity);
  29. // 下单
  30. //productRepository.updateProductCount(purchaseProductNum, new Date(), productEntity.getId());
  31. //创建订单
  32. BigDecimal multiply = productEntity.getPrice().multiply(new BigDecimal(purchaseProductNum));
  33. OrdersEntity orderEntity = new OrdersEntity();
  34. // 待处理 订单状态
  35. orderEntity.setOrderStatus(1);
  36. orderEntity.setReceiverName("测试收货人");
  37. orderEntity.setReceiverMobile("13888888888");
  38. orderEntity.setOrderAmount(multiply);
  39. orderEntity.setCreateTime(new Date());
  40. orderEntity.setCreateUser("测试用户1");
  41. orderEntity.setUpdateTime(new Date());
  42. orderRepository.save(orderEntity);
  43. // 创建订单明细
  44. OrderItemEntity orderItemEntity = new OrderItemEntity();
  45. orderItemEntity.setOrderId(orderEntity.getIid());
  46. orderItemEntity.setProductId(productEntity.getId());
  47. orderItemEntity.setPurchasePrice(new BigDecimal("0"));
  48. orderItemEntity.setPurchaseNum(purchaseProductNum);
  49. orderItemEntity.setCreateTime(new Date());
  50. orderItemEntity.setCreateUser("测试用户1");
  51. orderItemEntity.setUpdateTime(new Date());
  52. orderItemRepository.save(orderItemEntity);
  53. System.out.println("订单创建成功");
  54. return orderEntity.getIid();
  55. }
  56. }

测试

  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest
  3. public class JpaTest {
  4. @Autowired
  5. private ProductRepository productRepository;
  6. @Resource
  7. private OrderService orderService;
  8. @Autowired
  9. private OrderRepository orderRepository;
  10. private int purchaseProductId = 100100;
  11. private int purchaseProductNum = 1;
  12. @Test
  13. public void testOrderAdd() {
  14. ProductEntity productEntity = new ProductEntity();
  15. productEntity.setId(purchaseProductId);
  16. productEntity.setProductName("测试商品");
  17. productEntity.setPrice(new BigDecimal(100));
  18. productEntity.setCount(purchaseProductNum);
  19. productEntity.setProductDesc("测试商品描述");
  20. productEntity.setCreateTime(new Date());
  21. productEntity.setCreateUser("测试用户");
  22. productEntity.setUpdateTime(new Date());
  23. productRepository.save(productEntity);
  24. }
  25. @Test
  26. public void testOrder(){
  27. OrdersEntity orderEntity = new OrdersEntity();
  28. // 待处理 订单状态
  29. orderEntity.setOrderStatus(1);
  30. orderEntity.setReceiverName("测试收货人");
  31. orderEntity.setReceiverMobile("13888888888");
  32. orderEntity.setOrderAmount(new BigDecimal(purchaseProductNum));
  33. orderEntity.setCreateTime(new Date());
  34. orderEntity.setCreateUser("测试用户1");
  35. orderEntity.setUpdateTime(new Date());
  36. orderRepository.save(orderEntity);
  37. }
  38. @Test
  39. public void concurrentOrder() {
  40. CountDownLatch count = new CountDownLatch(5);
  41. CyclicBarrier barrier = new CyclicBarrier(5);
  42. ExecutorService pool = Executors.newFixedThreadPool(5);
  43. for (int i = 0; i < 5; i++) {
  44. pool.execute(() -> {
  45. try {
  46. barrier.await();
  47. System.out.println("线程" + Thread.currentThread().getName() + "开始执行");
  48. int orderId = orderService.orderCreate();
  49. System.out.println("订单生成成功,订单号:" + orderId);
  50. } catch (InterruptedException e) {
  51. e.printStackTrace();
  52. } catch (BrokenBarrierException e) {
  53. e.printStackTrace();
  54. }finally {
  55. count.countDown();
  56. }
  57. });
  58. }
  59. try {
  60. count.await();
  61. } catch (InterruptedException e) {
  62. e.printStackTrace();
  63. }
  64. pool.shutdown();
  65. }
  66. }

image.png
image.png
image.pngimage.png
在多线程情况下,产生了超卖问题。

改用这个下面update 库存减一
但还是出现了库存变为负数。
image.png
这个方法还不能解决问题,同时下5个单,商品会变成负数。

超卖现象举例2

商品变成负数

产生原因

并发监测库存,造成库存充足的假象。
update更新库存,导致库存变成负数。

图解

image.png

方法1(抛出异常+synchronized):

可以先使用最简单的方法,在更新商品数量后,再校验商品的库存,如果是负数,抛出异常。
image.png
但是在多线程中,还是没用。

方法2: 加锁 synchronized

在校验库存和扣减库存统一加锁,使之成为原子性操作,并发的时候,只有获取到锁的线程才能校验扣减库存。
在扣减库存结束后释放锁,确保库存不会变成负数。

  1. 可以使用synchronized关键字解决超卖问题,这是最原始的锁哦。

如果在创建订单的方法上加synchronized关键字,这样能够解决问题吗?

答案是不能的,因为锁住的是当前方法,但是事务没有被锁住,线程1的锁释放后,线程2进入该方法,事务可能还没提交,这就导致了查询库存的时候,还是原来的库存

  1. 那么如何控制事务呢? ```java @Service public class OrderServiceImpl implements OrderService {

    @Resource private ProductRepository productRepository; @Resource private OrderRepository orderRepository; @Resource private OrderItemRepository orderItemRepository; @Autowired private PlatformTransactionManager transactionManager; @Autowired private TransactionDefinition definition;

    private int purchaseProductId = 100100; private int purchaseProductNum = 1;

    @Override //@Transactional(rollbackOn = Exception.class) public synchronized int orderCreate() {

    1. TransactionStatus transaction = transactionManager.getTransaction(definition);
    2. ProductEntity productEntity = productRepository.findById(purchaseProductId).orElseGet(ProductEntity::new);
    3. if (productEntity == null) {
    4. transactionManager.rollback(transaction);
    5. throw new RuntimeException("购买商品:" + purchaseProductId + "不存在");
    6. }
    7. //商品库存
    8. int count = productEntity.getCount();
    9. if (purchaseProductNum > count) {
    10. transactionManager.rollback(transaction);
    11. throw new RuntimeException("商品" + purchaseProductId + "库存不足,当前库存:" + count);
    12. }
    13. // 计算剩余库存
    14. //Integer leftCount = count - purchaseProductNum;
    15. //// 更新商品库存
    16. //productEntity.setCount(leftCount);
    17. //productEntity.setUpdateTime(new Date());
    18. //productRepository.save(productEntity);
    19. // 下单
    20. productRepository.updateProductCount(purchaseProductNum, new Date(), productEntity.getId());
    21. // 当检索商品的库存为负数,抛出异常
    22. //ProductEntity product = productRepository.findById(purchaseProductId).orElseGet(ProductEntity::new);
    23. //int sk_count = product.getCount();
    24. //if (sk_count < 0) {
    25. // throw new RuntimeException("商品" + purchaseProductId + "库存不足,当前库存:" + sk_count);
    26. //}
  1. //创建订单
  2. BigDecimal multiply = productEntity.getPrice().multiply(new BigDecimal(purchaseProductNum));
  3. OrdersEntity orderEntity = new OrdersEntity();
  4. // 待处理 订单状态
  5. orderEntity.setOrderStatus(1);
  6. orderEntity.setReceiverName("测试收货人");
  7. orderEntity.setReceiverMobile("13888888888");
  8. orderEntity.setOrderAmount(multiply);
  9. orderEntity.setCreateTime(new Date());
  10. orderEntity.setCreateUser("测试用户1");
  11. orderEntity.setUpdateTime(new Date());
  12. orderRepository.save(orderEntity);
  13. // 创建订单明细
  14. OrderItemEntity orderItemEntity = new OrderItemEntity();
  15. orderItemEntity.setOrderId(orderEntity.getIid());
  16. orderItemEntity.setProductId(productEntity.getId());
  17. orderItemEntity.setPurchasePrice(new BigDecimal("0"));
  18. orderItemEntity.setPurchaseNum(20);
  19. orderItemEntity.setCreateTime(new Date());
  20. orderItemEntity.setCreateUser("测试用户1");
  21. orderItemEntity.setUpdateTime(new Date());
  22. orderItemRepository.save(orderItemEntity);
  23. System.out.println("订单创建成功");
  24. transactionManager.commit(transaction);
  25. return orderEntity.getIid();
  26. }

}

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/22478710/1650118912014-6910cf9f-3bbb-45ff-b7bc-26e6c88325c6.png#clientId=u8656b4c4-9032-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=363&id=ud4dfe313&margin=%5Bobject%20Object%5D&name=image.png&originHeight=572&originWidth=1462&originalType=binary&ratio=1&rotation=0&showTitle=false&size=75315&status=done&style=none&taskId=ud8e72f35-275a-49f1-bcf5-102566d8892&title=&width=928.2540104088258)<br />synchronized不同的写法<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22478710/1650120015838-d9caf186-e2be-426e-abad-787c3e79d302.png#clientId=u8656b4c4-9032-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=1063&id=u303d36b4&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1675&originWidth=2357&originalType=binary&ratio=1&rotation=0&showTitle=false&size=365582&status=done&style=none&taskId=uf5e7ab60-7c52-45d2-8e47-289c2d0e417&title=&width=1496.5080044689482)
  2. <a name="GtYy4"></a>
  3. ### 方法3: 加锁 reentrantlock
  4. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/22478710/1650119874442-88424b01-50fa-48b5-aa90-4dc76ca0a003.png#clientId=u8656b4c4-9032-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=1008&id=uc236e639&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1588&originWidth=2419&originalType=binary&ratio=1&rotation=0&showTitle=false&size=386535&status=done&style=none&taskId=u37d9a221-2146-4dd8-89d0-74dd98740d6&title=&width=1535.8730856217167)
  5. ```java
  6. @Service
  7. public class OrderServiceImpl implements OrderService {
  8. @Resource
  9. private ProductRepository productRepository;
  10. @Resource
  11. private OrderRepository orderRepository;
  12. @Resource
  13. private OrderItemRepository orderItemRepository;
  14. @Autowired
  15. private PlatformTransactionManager transactionManager;
  16. @Autowired
  17. private TransactionDefinition definition;
  18. //private Lock lock = new ReentrantLock();
  19. private int purchaseProductId = 100100;
  20. private int purchaseProductNum = 1;
  21. @Override
  22. //@Transactional(rollbackOn = Exception.class)
  23. public int orderCreate() {
  24. ProductEntity productEntity = null;
  25. //try {
  26. //lock.lock();
  27. synchronized (this) {
  28. TransactionStatus transaction = transactionManager.getTransaction(definition);
  29. productEntity = productRepository.findById(purchaseProductId).orElseGet(ProductEntity::new);
  30. if (productEntity == null) {
  31. transactionManager.rollback(transaction);
  32. throw new RuntimeException("购买商品:" + purchaseProductId + "不存在");
  33. }
  34. //商品库存
  35. int count = productEntity.getCount();
  36. if (purchaseProductNum > count) {
  37. transactionManager.rollback(transaction);
  38. throw new RuntimeException("商品" + purchaseProductId + "库存不足,当前库存:" + count);
  39. }
  40. // 计算剩余库存
  41. //Integer leftCount = count - purchaseProductNum;
  42. //// 更新商品库存
  43. //productEntity.setCount(leftCount);
  44. //productEntity.setUpdateTime(new Date());
  45. //productRepository.save(productEntity);
  46. // 下单
  47. productRepository.updateProductCount(purchaseProductNum, new Date(), productEntity.getId());
  48. transactionManager.commit(transaction);
  49. }
  50. //} finally {
  51. // lock.unlock();
  52. //}
  53. // 当检索商品的库存为负数,抛出异常
  54. //ProductEntity product = productRepository.findById(purchaseProductId).orElseGet(ProductEntity::new);
  55. //int sk_count = product.getCount();
  56. //if (sk_count < 0) {
  57. // throw new RuntimeException("商品" + purchaseProductId + "库存不足,当前库存:" + sk_count);
  58. //}
  59. TransactionStatus transaction2 = transactionManager.getTransaction(definition);
  60. //创建订单
  61. BigDecimal multiply = productEntity.getPrice().multiply(new BigDecimal(purchaseProductNum));
  62. OrdersEntity orderEntity = new OrdersEntity();
  63. // 待处理 订单状态
  64. orderEntity.setOrderStatus(1);
  65. orderEntity.setReceiverName("测试收货人");
  66. orderEntity.setReceiverMobile("13888888888");
  67. orderEntity.setOrderAmount(multiply);
  68. orderEntity.setCreateTime(new Date());
  69. orderEntity.setCreateUser("测试用户1");
  70. orderEntity.setUpdateTime(new Date());
  71. orderRepository.save(orderEntity);
  72. // 创建订单明细
  73. OrderItemEntity orderItemEntity = new OrderItemEntity();
  74. orderItemEntity.setOrderId(orderEntity.getIid());
  75. orderItemEntity.setProductId(productEntity.getId());
  76. orderItemEntity.setPurchasePrice(new BigDecimal("0"));
  77. orderItemEntity.setPurchaseNum(20);
  78. orderItemEntity.setCreateTime(new Date());
  79. orderItemEntity.setCreateUser("测试用户1");
  80. orderItemEntity.setUpdateTime(new Date());
  81. orderItemRepository.save(orderItemEntity);
  82. System.out.println("订单创建成功");
  83. transactionManager.commit(transaction2);
  84. return orderEntity.getIid();
  85. }
  86. }

结果
image.png