


数据库
orders、order_item、product


<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.3.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>provided</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>
@Data@NoArgsConstructor@AllArgsConstructor@Entity@Table(name = "orders", schema = "fzs")public class OrdersEntity {@GeneratedValue(strategy = GenerationType.IDENTITY)@Id@Column(name = "iid")private int iid;@Basic@Column(name = "order_status")private int orderStatus;@Basic@Column(name = "receiver_name")private String receiverName;@Basic@Column(name = "receiver_mobile")private String receiverMobile;@Basic@Column(name = "order_amount")private BigDecimal orderAmount;@Basic@Column(name = "create_time")private Date createTime;@Basic@Column(name = "create_user")private String createUser;@Basic@Column(name = "update_time")private Date updateTime;}
@Data@NoArgsConstructor@AllArgsConstructor@Entity@Table(name = "order_item")public class OrderItemEntity {@GeneratedValue(strategy = GenerationType.IDENTITY)@Id@Column(name = "iid")private int iid;@Basic@Column(name = "order_id")private int orderId;@Basic@Column(name = "product_id")private int productId;@Basic@Column(name = "purchase_price")private BigDecimal purchasePrice;@Basic@Column(name = "purchase_num")private int purchaseNum;@Basic@Column(name = "create_time")private Date createTime;@Basic@Column(name = "create_user")private String createUser;@Basic@Column(name = "update_time")private Date updateTime;}
@Data@NoArgsConstructor@AllArgsConstructor@Entity@Table(name = "product")public class ProductEntity {@GeneratedValue(strategy = GenerationType.IDENTITY)@Id@Column(name = "id")private int id;@Basic@Column(name = "product_name")private String productName;@Basic@Column(name = "price")private BigDecimal price;@Basic@Column(name = "count")private int count;@Basic@Column(name = "product_desc")private String productDesc;@Basic@Column(name = "create_time")private Date createTime;@Basic@Column(name = "create_user")private String createUser;@Basic@Column(name = "update_time")private Date updateTime;}
@Repositorypublic interface OrderItemRepository extends JpaRepository<OrderItemEntity, Integer> {}
@Repositorypublic interface OrderRepository extends JpaRepository<OrdersEntity, Integer> {}
@Repositorypublic interface ProductRepository extends JpaRepository<ProductEntity, Integer> {/*** 总数减去orderCount* @param orderCount* @param orderId*/@Modifying@Query(value = "update product set count = count - :orderCount, update_time =:update_time where id = :orderId", nativeQuery = true)void updateProductCount(@Param("orderCount") Integer orderCount,@Param("update_time") Date updateTime,@Param("orderId") Integer orderId);}
模拟实现
public interface OrderService {int orderCreate();}
@Servicepublic class OrderServiceImpl implements OrderService {@Autowiredprivate ProductRepository productRepository;@Resourceprivate OrderRepository orderRepository;@Resourceprivate OrderItemRepository orderItemRepository;private int purchaseProductId = 100100;private int purchaseProductNum = 1;@Override@Transactional(rollbackOn = Exception.class)public int orderCreate() {ProductEntity productEntity = productRepository.findById(purchaseProductId).orElseGet(ProductEntity::new);if (productEntity == null) {throw new RuntimeException("购买商品:" + purchaseProductId + "不存在");}//商品库存int count = productEntity.getCount();if (purchaseProductNum > count) {throw new RuntimeException("商品" + purchaseProductId + "库存不足,当前库存:" + count);}// 计算剩余库存Integer leftCount = count - purchaseProductNum;// 更新商品库存productEntity.setCount(leftCount);productEntity.setUpdateTime(new Date());productRepository.save(productEntity);// 下单//productRepository.updateProductCount(purchaseProductNum, new Date(), productEntity.getId());//创建订单BigDecimal multiply = productEntity.getPrice().multiply(new BigDecimal(purchaseProductNum));OrdersEntity orderEntity = new OrdersEntity();// 待处理 订单状态orderEntity.setOrderStatus(1);orderEntity.setReceiverName("测试收货人");orderEntity.setReceiverMobile("13888888888");orderEntity.setOrderAmount(multiply);orderEntity.setCreateTime(new Date());orderEntity.setCreateUser("测试用户1");orderEntity.setUpdateTime(new Date());orderRepository.save(orderEntity);// 创建订单明细OrderItemEntity orderItemEntity = new OrderItemEntity();orderItemEntity.setOrderId(orderEntity.getIid());orderItemEntity.setProductId(productEntity.getId());orderItemEntity.setPurchasePrice(new BigDecimal("0"));orderItemEntity.setPurchaseNum(purchaseProductNum);orderItemEntity.setCreateTime(new Date());orderItemEntity.setCreateUser("测试用户1");orderItemEntity.setUpdateTime(new Date());orderItemRepository.save(orderItemEntity);System.out.println("订单创建成功");return orderEntity.getIid();}}
测试
@RunWith(SpringRunner.class)@SpringBootTestpublic class JpaTest {@Autowiredprivate ProductRepository productRepository;@Resourceprivate OrderService orderService;@Autowiredprivate OrderRepository orderRepository;private int purchaseProductId = 100100;private int purchaseProductNum = 1;@Testpublic void testOrderAdd() {ProductEntity productEntity = new ProductEntity();productEntity.setId(purchaseProductId);productEntity.setProductName("测试商品");productEntity.setPrice(new BigDecimal(100));productEntity.setCount(purchaseProductNum);productEntity.setProductDesc("测试商品描述");productEntity.setCreateTime(new Date());productEntity.setCreateUser("测试用户");productEntity.setUpdateTime(new Date());productRepository.save(productEntity);}@Testpublic void testOrder(){OrdersEntity orderEntity = new OrdersEntity();// 待处理 订单状态orderEntity.setOrderStatus(1);orderEntity.setReceiverName("测试收货人");orderEntity.setReceiverMobile("13888888888");orderEntity.setOrderAmount(new BigDecimal(purchaseProductNum));orderEntity.setCreateTime(new Date());orderEntity.setCreateUser("测试用户1");orderEntity.setUpdateTime(new Date());orderRepository.save(orderEntity);}@Testpublic void concurrentOrder() {CountDownLatch count = new CountDownLatch(5);CyclicBarrier barrier = new CyclicBarrier(5);ExecutorService pool = Executors.newFixedThreadPool(5);for (int i = 0; i < 5; i++) {pool.execute(() -> {try {barrier.await();System.out.println("线程" + Thread.currentThread().getName() + "开始执行");int orderId = orderService.orderCreate();System.out.println("订单生成成功,订单号:" + orderId);} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}finally {count.countDown();}});}try {count.await();} catch (InterruptedException e) {e.printStackTrace();}pool.shutdown();}}




在多线程情况下,产生了超卖问题。
改用这个下面update 库存减一
但还是出现了库存变为负数。
这个方法还不能解决问题,同时下5个单,商品会变成负数。
超卖现象举例2
产生原因
并发监测库存,造成库存充足的假象。
update更新库存,导致库存变成负数。
图解
方法1(抛出异常+synchronized):
可以先使用最简单的方法,在更新商品数量后,再校验商品的库存,如果是负数,抛出异常。
但是在多线程中,还是没用。
方法2: 加锁 synchronized
在校验库存和扣减库存统一加锁,使之成为原子性操作,并发的时候,只有获取到锁的线程才能校验扣减库存。
在扣减库存结束后释放锁,确保库存不会变成负数。
- 可以使用synchronized关键字解决超卖问题,这是最原始的锁哦。
如果在创建订单的方法上加synchronized关键字,这样能够解决问题吗?
答案是不能的,因为锁住的是当前方法,但是事务没有被锁住,线程1的锁释放后,线程2进入该方法,事务可能还没提交,这就导致了查询库存的时候,还是原来的库存
那么如何控制事务呢? ```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() {
TransactionStatus transaction = transactionManager.getTransaction(definition);ProductEntity productEntity = productRepository.findById(purchaseProductId).orElseGet(ProductEntity::new);if (productEntity == null) {transactionManager.rollback(transaction);throw new RuntimeException("购买商品:" + purchaseProductId + "不存在");}//商品库存int count = productEntity.getCount();if (purchaseProductNum > count) {transactionManager.rollback(transaction);throw new RuntimeException("商品" + purchaseProductId + "库存不足,当前库存:" + count);}// 计算剩余库存//Integer leftCount = count - purchaseProductNum;//// 更新商品库存//productEntity.setCount(leftCount);//productEntity.setUpdateTime(new Date());//productRepository.save(productEntity);// 下单productRepository.updateProductCount(purchaseProductNum, new Date(), productEntity.getId());// 当检索商品的库存为负数,抛出异常//ProductEntity product = productRepository.findById(purchaseProductId).orElseGet(ProductEntity::new);//int sk_count = product.getCount();//if (sk_count < 0) {// throw new RuntimeException("商品" + purchaseProductId + "库存不足,当前库存:" + sk_count);//}
//创建订单BigDecimal multiply = productEntity.getPrice().multiply(new BigDecimal(purchaseProductNum));OrdersEntity orderEntity = new OrdersEntity();// 待处理 订单状态orderEntity.setOrderStatus(1);orderEntity.setReceiverName("测试收货人");orderEntity.setReceiverMobile("13888888888");orderEntity.setOrderAmount(multiply);orderEntity.setCreateTime(new Date());orderEntity.setCreateUser("测试用户1");orderEntity.setUpdateTime(new Date());orderRepository.save(orderEntity);// 创建订单明细OrderItemEntity orderItemEntity = new OrderItemEntity();orderItemEntity.setOrderId(orderEntity.getIid());orderItemEntity.setProductId(productEntity.getId());orderItemEntity.setPurchasePrice(new BigDecimal("0"));orderItemEntity.setPurchaseNum(20);orderItemEntity.setCreateTime(new Date());orderItemEntity.setCreateUser("测试用户1");orderItemEntity.setUpdateTime(new Date());orderItemRepository.save(orderItemEntity);System.out.println("订单创建成功");transactionManager.commit(transaction);return orderEntity.getIid();}
}
<br />synchronized不同的写法<br /><a name="GtYy4"></a>### 方法3: 加锁 reentrantlock```java@Servicepublic class OrderServiceImpl implements OrderService {@Resourceprivate ProductRepository productRepository;@Resourceprivate OrderRepository orderRepository;@Resourceprivate OrderItemRepository orderItemRepository;@Autowiredprivate PlatformTransactionManager transactionManager;@Autowiredprivate TransactionDefinition definition;//private Lock lock = new ReentrantLock();private int purchaseProductId = 100100;private int purchaseProductNum = 1;@Override//@Transactional(rollbackOn = Exception.class)public int orderCreate() {ProductEntity productEntity = null;//try {//lock.lock();synchronized (this) {TransactionStatus transaction = transactionManager.getTransaction(definition);productEntity = productRepository.findById(purchaseProductId).orElseGet(ProductEntity::new);if (productEntity == null) {transactionManager.rollback(transaction);throw new RuntimeException("购买商品:" + purchaseProductId + "不存在");}//商品库存int count = productEntity.getCount();if (purchaseProductNum > count) {transactionManager.rollback(transaction);throw new RuntimeException("商品" + purchaseProductId + "库存不足,当前库存:" + count);}// 计算剩余库存//Integer leftCount = count - purchaseProductNum;//// 更新商品库存//productEntity.setCount(leftCount);//productEntity.setUpdateTime(new Date());//productRepository.save(productEntity);// 下单productRepository.updateProductCount(purchaseProductNum, new Date(), productEntity.getId());transactionManager.commit(transaction);}//} finally {// lock.unlock();//}// 当检索商品的库存为负数,抛出异常//ProductEntity product = productRepository.findById(purchaseProductId).orElseGet(ProductEntity::new);//int sk_count = product.getCount();//if (sk_count < 0) {// throw new RuntimeException("商品" + purchaseProductId + "库存不足,当前库存:" + sk_count);//}TransactionStatus transaction2 = transactionManager.getTransaction(definition);//创建订单BigDecimal multiply = productEntity.getPrice().multiply(new BigDecimal(purchaseProductNum));OrdersEntity orderEntity = new OrdersEntity();// 待处理 订单状态orderEntity.setOrderStatus(1);orderEntity.setReceiverName("测试收货人");orderEntity.setReceiverMobile("13888888888");orderEntity.setOrderAmount(multiply);orderEntity.setCreateTime(new Date());orderEntity.setCreateUser("测试用户1");orderEntity.setUpdateTime(new Date());orderRepository.save(orderEntity);// 创建订单明细OrderItemEntity orderItemEntity = new OrderItemEntity();orderItemEntity.setOrderId(orderEntity.getIid());orderItemEntity.setProductId(productEntity.getId());orderItemEntity.setPurchasePrice(new BigDecimal("0"));orderItemEntity.setPurchaseNum(20);orderItemEntity.setCreateTime(new Date());orderItemEntity.setCreateUser("测试用户1");orderItemEntity.setUpdateTime(new Date());orderItemRepository.save(orderItemEntity);System.out.println("订单创建成功");transactionManager.commit(transaction2);return orderEntity.getIid();}}
结果
