java项目下订单扣库存代码实现(乐观锁 + 防重复下单 + 重试)

1.1 表结构(必加 version)

ALTER TABLE product ADD COLUMN version INT DEFAULT 1 NOT NULL COMMENT '乐观锁版本号';

 唯一索引防止同一用户重复下单。

1.2 订单表加唯一索引

ALTER TABLE `order` ADD UNIQUE INDEX uk_user_product (user_id, product_id);

2. 实体类

import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;

@Data
public class Product {
    private Long id;
    private Integer stock;    // 库存

    @Version   // MyBatis-Plus 乐观锁标识
    private Integer version;
}

3. MyBatis-Plus 乐观锁配置

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}

4. 最终完整业务代码

@Service
public class OrderService {

    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private OrderMapper orderMapper;

    private static final int MAX_RETRY = 3;

    @Transactional
    public void realCreateOrder(Long userId, Long productId) {
        int retryCount = 0;
        boolean success = false;

        while (retryCount < MAX_RETRY && !success) {
            try {
                // 1. 查询库存
                Product product = productMapper.selectById(productId);
                if (product.getStock() <= 0) {
                    throw new RuntimeException("已售罄");
                }

                // 2. 扣库存(乐观锁)
                product.setStock(product.getStock() - 1);
                int rows = productMapper.updateById(product);
                if (rows == 0) {
                    retryCount++;
                    Thread.sleep(10);
                    continue;
                }

                // 3. 创建订单(唯一索引防重复)
                Order order = new Order();
                order.setUserId(userId);
                order.setProductId(productId);
                orderMapper.insert(order);

                success = true;

            } catch (DuplicateKeyException e) {
                // 唯一索引冲突 → 重复下单
                throw new RuntimeException("请勿重复提交订单");

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("系统异常");
            }
        }

        if (!success) {
            throw new RuntimeException("当前人数过多,请稍后再试");
        }
    }
}

5.总结

  1. @Version → 自动拼接 version = ? 条件
  2. 更新返回 0 行 → 并发冲突
  3. 重试机制 → 提高成功率
  4. 唯一索引 → 防重复下单
  5. 全程无 synchronized → 高并发、集群安全、性能强
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值