Используя Spring Data JPA, у меня есть объект Hibernate с уникальным ограничением на name
и context
:
@Entity
public class MyEntity {
@Id
private String name;
@Id
private String context;
// setter, getter, ID class, etc. omitted for brevity
}
, чьи логики создания c выглядят примерно так:
private final MyRepository repository;
@Transactional
public void createOrUpdate(String name, String context) {
MyEntity entity = repository.findByNameAndContext(name, context)
.orElseGet(() -> new MyEntity(name, context));
entity.doSomething();
repository.save(entity);
}
и где метод репозитория использует блокировку pessimisti c:
public interface MyRepository extends CrudRepository<...> {
@Lock(PESSIMISTIC_WRITE)
MyEntity findByNameAndContext(String name, String context);
}
В большинстве случаев это работает нормально, пока два отдельных потока не попытаются создать одну и ту же сущность одновременно, выполнив оба в orElseGet
. По завершении первой транзакции можно будет создать новую строку в базе данных, но вторая не удастся из-за DataIntegrityViolationException
.
Я понимаю, почему это не удается, но я не могу найти элегантный способ справиться с этим. Единственное решение, которое я нашел, - это повторить транзакцию при DataIntegrityViolationException
.