Spring数据 - Concurrent create-if-not-exists导致重复条目

时间:2013-04-04 22:40:50

标签: java jpa concurrency spring-data

我有以下代码,它在多个线程中运行:

@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,则应该阻止重复。

但是,我想这是因为代码是事务性的,并且事务的边界位于方法(而不是同步块),即在事务持续之前释放锁,允许重复。

这必须是一个相当常见的模式(“如果不存在则创建”)。以并发方式执行此操作的正确方法是什么?

1 个答案:

答案 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);
   }   
}