我有以下代码,它在多个线程中运行:
@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
,则应该阻止重复。
但是,我想这是因为代码是事务性的,并且事务的边界位于方法(而不是同步块),即在事务持续之前释放锁,允许重复。
这必须是一个相当常见的模式(“如果不存在则创建”)。以并发方式执行此操作的正确方法是什么?
答案 0 :(得分:1)
我最终重构了转移事务边界的方法:
public void createInstrument(String instrumentId) {
synchronized(instrumentId.intern()) {
persistInstrument(instrumentId);
}
}
@Transactional
protected void persistInstrument(String instrumentId) {
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);
}
}