У меня есть система, которая взаимодействует со сторонней системой для создания и хранения данных автомобиля.Пользователь выбирает ThirdPartyCar
и использует его для создания Car
в моей системе.
Сервисный метод спасает автомобиль.Но его следует сохранять только в том случае, если кто-то еще не пытался сохранить автомобиль с помощью этого ThirdPatyCar
:
@Transactional(transactionManager="mySystemTransactionManager", isolation=Isolation.?)
public void saveNewCarAndMapToThirdPartyCar(Car car, Long thirdPartyCarId) {
// Mapping table tracks which ThirdPartyCar was used to create my Car.
// The thirdPartyCarId is the primary key of the table.
if (!thirdPartyCarMapRepo.existsById(thirdPartyCarId)) {
// sleep to help test concurrency issues
log.debug("sleep");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("awake");
Car car = carRepository.save(car);
thirdPartyCarMapRepo.save(new ThirdPartyCarMap(thirdPartyCarId, car));
}
}
Вот сценарий, который приводит к проблеме:
User A User B
existsById |
| |
| existsById
| |
| |
carRepo.save |
thirdPartyCarMapRepo.save |
| |
| |
| carRepo.save
| thirdPartyCarMapRepo.save
СИзоляция установлена на что-либо ниже, чем Isolation.SERIALIZABLE, похоже, что оба пользователя hasBBII возвращают false.Это приводит к созданию двух автомобилей, и только последний сохраненный автомобиль отображается на стороннем автомобиле.
Если я установлю Isolation.SERIALIZABLE, пользователь не сможет одновременно создавать автомобили даже из разных сторонних автомобилей.
Как я могу запретить пользователю B создавать автомобиль, если сторонний автомобиль такой же, как пользователь A, но все же позволяет одновременно создавать оба, когда автомобили сторонних производителей отличаются?
Обновление
Подумав и исследуя это немного больше, я полагаю, что это не может быть проблемой изоляции транзакции.
Вот таблица ThirdPartyCarMap:
thirdPartyCarId (PK, bigint, not null)
carId (FK, bigint, not null) /* reference my system's car table */
В качестве примера, используя приведенную выше схему сценария:
Пользователь A thirdPartyCarMapRepo.save
делает:
inserts into ThirdPartyCarMap (thirdPartyCarId, carId) values (45,1)
Тем не менее, пользователь B thirdPartyCarMapRepo.save
делает:
update ThirdPartyCarMap set carId =2 where thirdPartyCarId=45
Таким образом, двойственная природа вызова сохранения вызывает проблемы.Я думаю, что у меня есть следующие возможные решения:
- Подход 1: Реализовать Persistable и переопределить поведение isNew, как описано здесь
- Подход 2: Добавить собственный запросв репозиторий, который выполняет вставку
- Подход 3. Добавьте суррогатный первичный ключ (например, идентификатор с автоматическим приращением) и удалите
thirdPartyCarId
в качестве первичного, но сделайте его уникальным.
Я думаю, что последний вариант, вероятно, самый лучший, но у каждого из них есть проблемы: - Подход 1: isNew будет возвращать true, даже при чтении данных - Подход 2: Похоже на хак - Подход 3: Дополнительные данные добавляются в таблицу только дляпохоже на JPA.
Есть ли другой лучший подход, который не имеет этих проблем?