我有两个具有共享倒计时闩锁的交易。每个事务在单独的线程中执行。我需要这种机制来重现“脏读”情况。 第一个事务确实更新了“目标实体”并刷新了更改而没有提交,然后将线程设置为等待状态(countDownLatch)。
在第二个事务中,我获取“目标实体”并将脏字段复制到另一个实体并进行保存。保存操作后,我使countDown继续第一个事务并回滚它。
下面的事务方法的代码示例。 第一个:
@Transactional
public void updatePrice() {
log.info("Start price updating. Thread '{}'", Thread.currentThread().getName());
Collection<Product> products = productRepository.findAll();
products.forEach(this::updatePrice);
productRepository.saveAll(products);
entityManager.flush();
log.info("Flash changes and wait for order transaction");
try {
latch.await();
} catch (InterruptedException e) {
log.error("Something wrong");
Thread.currentThread().interrupt();
}
log.error("Rollback changes");
throw new RuntimeException("Unexpected exception");
}
第二个:我有受事务和包装器服务限制的主服务,以递减锁存器。
包装器:
public void doOrder(Long productId, Long amount) {
log.info("Start do order. Thread '{}'", Thread.currentThread().getName());
orderService.doOrder(productId, amount);
log.info("Order transaction committed");
latch.countDown();
log.info("Finish order process");
}
主要服务:
@Transactional
public void doOrder(Long productId, Long amount) {
Product product = productRepository.findById(productId).get();
log.info("Get product");
Order order = Order.builder().price(product.getPrice()).amount(amount).productId(new Long(productId)).build();
orderRepository.save(order);
log.info("Save order");
}
所以,在行
orderRepository.save(order);
线程已冻结。我在日志中仅看到插入语句。
但是,如果我删除“订单”和“产品”之间的关系,则插入内容不会冻结。 我猜在'product'和countDownLatch上有死锁。但是,仅在mysql jdbc中会发生此问题。如果我切换到postgres jdbc,则那里没有问题。
PS: 第一个事务执行以下查询:
select * from product;
update product set price = ...
第二个执行:
select * from product where id = ...;
insert into product_order(product_id, amount, price) values (...)