我有以下代码,它在多个线程中运行:
@Component
public class CreateInstrumentTask {
@Autowired
InstrumentRepository repository; // Spring-data JPA repo interface
@Transactional
public void createInstrument(String instrumentId) {
synchronized(instrumentId.intern()) {
QInstrument $instrument = QInstrument.instrument;
Instrument instrument = repository.findOne($instrument.instrumentId.eq(instrumentId));
if (instrument == null) {
log.info("Thread {} creating instrument {}", Thread.currentThread().getName(), message.getInstrumentId());
instrument = createInstrument(instrumentId); // impl. ommitted
repository.saveAndFlush(instrument);
}
}
}
这会注销:
INFO [] Thread taskExecutor-1 creating instrument ABC
INFO [] Thread taskExecutor-17 creating instrument ABC
org.springframework.integration.MessageHandlingException:
org.springframework.dao.DataIntegrityViolationException: Duplicate entry 'ABC' for key 'instrumentId';
我预计,鉴于代码是synchronized
针对 的instrumentId
,应该防止重复。
但是,我猜是因为代码是事务性的,并且事务的边界在方法上(不是同步块),所以锁在事务持续之前被释放,允许重复。
这必须是一个相当普遍的模式(“如果不存在则创建”)。以并发方式执行此操作的正确方法是什么?