Я хочу вставить объекты в базу данных из масштабируемого микросервиса. Я пытался @Lock(LockModeType.PESSIMISTIC_WRITE)
предотвратить двойные записи. Проблема в том, что у меня есть зависимости от моих сущностей.
Базовый пример:
TestEntity.java
public class TestEntity {
@GeneratedValue()
@Id
private Long id;
private String string;
@ManyToOne
private TestEntityParent testEntityParent;
}
TestEntityParent.java
public class TestEntityParent {
@GeneratedValue()
@Id
private Long id;
private String stringTwo;
@OneToMany(mappedBy = "testEntityParent")
private List<TestEntity> testEntities;
}
TestEnityRepository.java
public interface TestEnityRepository extends JpaRepository<TestEntity,Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
TestEntity saveAndFlush(TestEntity testEntity);
Optional<TestEntity> findByStringAndTestEntityParentStringTwo(String string, String stringTwo);
}
TestEntityParentRepository.java
public interface TestEntityParentRepository extends JpaRepository<TestEntityParent, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
TestEntityParent save(TestEntityParent testEntityParent);
Optional<TestEntityParent> findByStringTwo(String stringTwo);
}
AtomicDbService.java
@Service
public class AtomicDbService {
@Autowired
TestEnityRepository testEnityRepository;
@Autowired
TestEntityParentRepository testEntityParentRepository;
@Transactional
public TestEntity atomicInsert(TestEntity testEntity) {
TestEntityParent testEntityParent = testEntityParentRepository.findByStringTwo(testEntity.getTestEntityParent().getStringTwo())
.orElse(testEntityParentRepository.save(testEntity.getTestEntityParent()));
return testEnityRepository.findByStringAndTestEntityParentStringTwo(
testEntity.getString(), testEntity.getTestEntityParent().getStringTwo()
).orElse(testEnityRepository
.save(
TestEntity.builder()
.string(testEntity.getString())
.testEntityParent(testEntityParent)
.build()
)
);
}
}
Мой тестовый пример:
@Test
@Transactional
public void testAtomicInsert(){
TestEntityParent testEntityParent = TestEntityParent.builder().stringTwo("testTwo").build();
TestEntity testEntity = TestEntity.builder().string("test").testEntityParent(testEntityParent).build();
atomicDbService.atomicInsert(testEntity);
System.out.println(testEnityRepository.findAll());
atomicDbService.atomicInsert(testEntity);
System.out.println(testEnityRepository.findAll());
atomicDbService.atomicInsert(testEntity);
System.out.println(testEnityRepository.findAll());
System.out.println(testEnityRepository.findAll());
}
Я получаю следующий ответ:
[TestEntity(id=2, string=test, testEntityParent=TestEntityParent(id=1, stringTwo=testTwo, testEntities=null))]
[TestEntity(id=2, string=test, testEntityParent=TestEntityParent(id=1, stringTwo=testTwo, testEntities=null)), TestEntity(id=3, string=test, testEntityParent=TestEntityParent(id=1, stringTwo=testTwo, testEntities=null))]
и ошибка:
query did not return a unique result: 2;
Без зависимостей все работает нормально.
UPDATE:
Добавление @Lock(LockModeType.PESSIMISTIC_WRITE)
к методу поиска приводит к
Feature not supported: "MVCC=TRUE && FOR UPDATE && JOIN"; SQL statement:
... то же самое относится к
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT e from TestEntity e join e.testEntityParent p where e.string = :string and p.stringTwo = :stringTwo ")
Optional<TestEntity> findWhatever(@Param("string") String string, @Param("stringTwo") String stringTwo);
... поскольку for update
генерируется всегда.