关于商品超卖(持续更新)

1.如果 你是用下面两个步骤,查库存和判断库存,如果有多个线程,那么肯定是不行的

// 1. 查询库存
//        int stock = 10;

        // 2. 判断库存,是否能够减少到0以下
//        if (stock - buyCounts < 0) {
        // 提示用户库存不够
//            10 - 3 -3 - 5 = -1
//        }

2.我们学过多线程后,会使用 synchronized(可以解决问题),但是在不推荐使用,因为在集群下无用,性能低下

3. 锁数据库: 不推荐,因为会导致数据库性能低下

4. 推荐使用 分布式锁 zookeeper redis,但是我现在不会(以后会使用)

5.那么 我只能使用乐观锁了(现在是单体项目):mybatis的xml文件

=============================================

<update id="decreaseItemSpecStock">

    update
        items_spec
    set
        stock = stock - #{pendingCounts}
    where
        id = #{specId}
    and
        stock >= #{pendingCounts}

</update>

=============================================

对应的mapper:

public int decreaseItemSpecStock(@Param("specId") String specId,
                                 @Param("pendingCounts") int pendingCounts);

=============================================

扫描二维码关注公众号,回复: 10290736 查看本文章

如果 stock  不大于或等于  pendingCounts ,就执行不成功,然后 会返回 0(成功返回 1),然后就会回滚(一定要加事务):

对应的impl 

 @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void decreaseItemSpecStock(String specId, int buyCounts) {

        int result = itemsMapperCustom.decreaseItemSpecStock(specId, buyCounts);
        if (result != 1) {
            throw new RuntimeException("订单创建失败,原因:库存不足!");
        }
    }

=============================================

在创建订单的impl类上也加上事务,所以 如果 stock  不大于或等于  pendingCounts,整个 impl就会回滚,订单创建失败,

就不会超卖了

===================================================

测试一下:

订单是 3件

数据库是2件:

数据库中的订单表已全部删除:

 

并且该页面没有进行跳转;

看一下创建订单的impl:(主要看2.4 ,注意:不能再2.4上创建try..catch有任何异常会从业务方法中抛出,全被捕获并“吞掉”,导致spring异常抛出触发事务回滚策略失效)

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public String createOrder(SubmitOrderBO submitOrderBO) {


        String userId = submitOrderBO.getUserId();
        String addressId = submitOrderBO.getAddressId();
        String itemSpecIds = submitOrderBO.getItemSpecIds();
        Integer payMethod = submitOrderBO.getPayMethod();
        String leftMsg = submitOrderBO.getLeftMsg();
        // 包邮费用设置为0
        Integer postAmount = 0;

        String orderId = sid.nextShort();

        UserAddress address = addressService.queryUserAddres(userId, addressId);

        // 1. 新订单数据保存
        Orders newOrder = new Orders();
        newOrder.setId(orderId);
        newOrder.setUserId(userId);

        newOrder.setReceiverName(address.getReceiver());
        newOrder.setReceiverMobile(address.getMobile());
        newOrder.setReceiverAddress(address.getProvince() + " "
                + address.getCity() + " "
                + address.getDistrict() + " "
                + address.getDetail());

//        newOrder.setTotalAmount();
//        newOrder.setRealPayAmount();
        newOrder.setPostAmount(postAmount);

        newOrder.setPayMethod(payMethod);
        newOrder.setLeftMsg(leftMsg);

        newOrder.setIsComment(YesOrNo.NO.type);
        newOrder.setIsDelete(YesOrNo.NO.type);
        newOrder.setCreatedTime(new Date());
        newOrder.setUpdatedTime(new Date());


        // 2. 循环根据itemSpecIds保存订单商品信息表
        String itemSpecIdArr[] = itemSpecIds.split(",");
        Integer totalAmount = 0;    // 商品原价累计
        Integer realPayAmount = 0;  // 优惠后的实际支付价格累计
        for (String itemSpecId : itemSpecIdArr) {

            // TODO 整合redis后,商品购买的数量重新从redis的购物车中获取
            int buyCounts = 1;

            // 2.1 根据规格id,查询规格的具体信息,主要获取价格
            ItemsSpec itemSpec = itemService.queryItemSpecById(itemSpecId);
            totalAmount += itemSpec.getPriceNormal() * buyCounts;
            realPayAmount += itemSpec.getPriceDiscount() * buyCounts;

            // 2.2 根据商品id,获得商品信息以及商品图片
            String itemId = itemSpec.getItemId();
            Items item = itemService.queryItemById(itemId);
            String imgUrl = itemService.queryItemMainImgById(itemId);

            // 2.3 循环保存子订单数据到数据库
            String subOrderId = sid.nextShort();
            OrderItems subOrderItem = new OrderItems();
            subOrderItem.setId(subOrderId);
            subOrderItem.setOrderId(orderId);
            subOrderItem.setItemId(itemId);
            subOrderItem.setItemName(item.getItemName());
            subOrderItem.setItemImg(imgUrl);
            subOrderItem.setBuyCounts(buyCounts);
            subOrderItem.setItemSpecId(itemSpecId);
            subOrderItem.setItemSpecName(itemSpec.getName());
            subOrderItem.setPrice(itemSpec.getPriceDiscount());
            orderItemsMapper.insert(subOrderItem);

            // 2.4 在用户提交订单以后,规格表中需要扣除库存
            itemService.decreaseItemSpecStock(itemSpecId, buyCounts);
        }

        newOrder.setTotalAmount(totalAmount);
        newOrder.setRealPayAmount(realPayAmount);
        ordersMapper.insert(newOrder);

        // 3. 保存订单状态表
        OrderStatus waitPayOrderStatus = new OrderStatus();
        waitPayOrderStatus.setOrderId(orderId);
        waitPayOrderStatus.setOrderStatus(OrderStatusEnum.WAIT_PAY.type);
        waitPayOrderStatus.setCreatedTime(new Date());
        orderStatusMapper.insert(waitPayOrderStatus);
        return orderId;
    }
发布了55 篇原创文章 · 获赞 5 · 访问量 6046

猜你喜欢

转载自blog.csdn.net/weixin_42528855/article/details/103580272