spring @Transactional事务未提交导致的并发问题

该博客讨论了一个Java并发编程的问题,即在@Transactional注解下可能出现的并发问题。代码示例中展示了当多个线程尝试创建订单时,由于事务在方法执行完毕后立即释放锁,可能导致数据不一致。为了解决这个问题,提出了不使用@Transactional注解,而是通过注入PlatformTransactionManager和TransactionDefinition来手动控制事务的提交和回滚。这种方式确保了在更新库存和创建订单等操作之间的一致性,避免了并发冲突。

1、以下代码存在并发问题,原因是@Transactional开启事务后,执行完createOrder()方法后已经释放锁了,但是事务还没提交,此时另外一个线程获取到锁开始执行createOrder方法导致的

public class ChaoMaiConcurrencyService {

    public static final int purchaseProductId = 1;

    public static final int purchaseProductNum =1;

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderItemMapper orderItemMapper;

    @Transactional(rollbackFor = Exception.class)
    public synchronized Integer createOrder() throws Exception {
        Product product = productMapper.selectByPrimaryKey(purchaseProductId);

        if (product == null) {
            throw new Exception("购买商品:"+purchaseProductId+"不存在");
        }

        Integer currentCount = product.getCount();
        //校验库存
        if (purchaseProductNum > currentCount) {
            throw new Exception("商品"+purchaseProductId+"仅剩"+currentCount+"件,无法购买");
        }
        //更新库存
        productMapper.updateProductCount(purchaseProductNum,product.getId());

        //新增订单
        Order order = new Order();
        order.setOrderAmount(product.getPrice().multiply(new BigDecimal(purchaseProductNum)));
        order.setOrderStatus(1);
        order.setReceiverName("xxx");
        orderMapper.insertSelective(order);

        //新增订单明细
        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(order.getId());
        orderItem.setProductId(product.getId());
        orderItem.setPurchasePrice(product.getPrice());
        orderItem.setPurchaseNum(purchaseProductNum);
        orderItemMapper.insertSelective(orderItem);

        return order.getId();
    }
}

测试类代码:
@RunWith(SpringRunner.class)
@SpringBootTest
public class ChaoMaiConcurrencyTest {

    @Autowired
    private ChaoMaiConcurrencyService chaoMaiConcurrencyService;

    @Test
    public void testChaoMai() throws InterruptedException {
        CountDownLatch cdl = new CountDownLatch(5);
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5);

        ExecutorService es = Executors.newFixedThreadPool(5);
        for (int i=0;i<5;i++) {
            es.execute(()->{
               try {
                   cyclicBarrier.await();
                   Integer orderId = chaoMaiConcurrencyService.createOrder();
                   System.out.println("订单id:"+orderId);
               } catch (Exception e) {
                   e.printStackTrace();
               } finally {
                   cdl.countDown();
               }
            });
        }
    }

}

2、解决方案:不使用@Transactional注解,注入PlatformTransactionManagerTransactionDefinition来手动提交事务
代码如下:
public class ChaoMaiConcurrencyService {

    public static final int purchaseProductId = 1;

    public static final int purchaseProductNum =1;

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderItemMapper orderItemMapper;

    @Autowired
    private PlatformTransactionManager platformTransactionManager;

    @Autowired
    private TransactionDefinition transactionDefinition;
    
    public synchronized Integer createOrder() throws Exception {
        TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);
        Product product = productMapper.selectByPrimaryKey(purchaseProductId);

        if (product == null) {
            //手动回滚
            platformTransactionManager.rollback(transaction);
            throw new Exception("购买商品:"+purchaseProductId+"不存在");
        }

        Integer currentCount = product.getCount();
        //校验库存
        if (purchaseProductNum > currentCount) {
            //手动回滚
            platformTransactionManager.rollback(transaction);
            throw new Exception("商品"+purchaseProductId+"仅剩"+currentCount+"件,无法购买");
        }
        //更新库存
        productMapper.updateProductCount(purchaseProductNum,product.getId());

        //新增订单
        Order order = new Order();
        order.setOrderAmount(product.getPrice().multiply(new BigDecimal(purchaseProductNum)));
        order.setOrderStatus(1);
        order.setReceiverName("xxx");
        orderMapper.insertSelective(order);

        //新增订单明细
        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(order.getId());
        orderItem.setProductId(product.getId());
        orderItem.setPurchasePrice(product.getPrice());
        orderItem.setPurchaseNum(purchaseProductNum);
        orderItemMapper.insertSelective(orderItem);

        //手动提交
        platformTransactionManager.commit(transaction);
        
        return order.getId();
    }
}
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值