Отвечая на мой собственный вопрос: да, можно полагаться на @Transactional
, чтобы предотвратить потерянные обновления, а также на @Version
.
Я провел тест по следующему сценарию:
1 Поток A выбирает объект E
2. Поток B выбирает тот же объект
3. Поток A обновляет объект следующим образом: поле приращения E.a++
и сохраняет объект.
4. Поток B делает то же самое.
Эта ситуация, очевидно, приводит к проблеме, известной как «потерянное обновление», поскольку в конце поле увеличивается только один раз, а не в два раза (как и ожидалось).
Я ожидал, что установка @Transactional
для метода, который выполняет действия «выбрать, обновить и сохранить», предотвратит эту ситуацию. На самом деле это сработало, но только когда я установил уровень изоляции для @Transactional
на REPEATABLE_READ
. После того, как я это сделал, возникла исключительная ситуация при сохранении сущности из второго потока.
Мое предположение было неверным только в той части, в которой я предположил, что "потерянные обновления" невозможны при уровне изоляции по умолчанию (я тестировал с * 1033). *).
Это не так!
Хорошая статья, которую я нашел, когда копался в проблеме, была от Влада Михалча: https://vladmihalcea.com/a-beginners-guide-to-database-locking-and-the-lost-update-phenomena/
Он точно объясняет проблема "потерянного обновления" и все возможные решения, которые включают:
1. isolation level = REPEATABLE_READ
, что возможно c с @Transactional
2. MV CC, что возможно именно с @Version
Итак, еще раз, чтобы подвести итог, оба подхода возможны для решения этой конкретной проблемы.