#java #spring #jpa
#Ява #весна #jpa
Вопрос:
У меня есть объект с оптимистичной блокировкой:
@Version private Integer version;
Я делаю запрос get по идентификатору, который выполняет findById
в бэкэнде. Я использую идентификатор для выполнения обновления с помощью метода PUT, который выполняет a save
в серверной части. После этого я снова выполняю get, чтобы вернуть измененный объект: После этого я снова пытаюсь обновить его и получаю:
{ "timestamp": { "nano": 305933000, "epochSecond": 1638524115 }, "status": 500, "error": [], "type": "ObjectOptimisticLockingFailureException", "path": "uri=/rooms/", "message": "Internal Server Error" }
Почему я получаю эту ошибку? Первая транзакция обновления должна быть выполнена, не так ли? Как я могу разблокировать объект?
Я не пользуюсь EntityManger
. Я использую JpaRepsoitory
save
этот метод.
Редактировать
Если я предоставлю версию в качестве атрибута в вызове get и введу версию в сущность, которая будет работать при ее сохранении.
Вы обычно так это делаете? Что клиент должен предоставить последнюю версию сущности, которая должна быть обновлена?
Комментарии:
1. Если вы получаете ошибку оптимистичной блокировки, это означает, что объект, который вы пытались обновить, не был обновлен, когда вы его редактировали. Другими словами: после обновления вам нужно повторно загрузить текущее состояние, отредактировать его, затем вы можете обновить его снова. Вы не можете просто слепо обновлять дважды, потому что нет никакой гарантии, что никто другой не редактировал в промежутке. Именно этого оптимистичная блокировка и пытается избежать. Оптимистическая блокировка-это причудливый способ сказать: «обновляйте версию при каждом сохранении, создавайте исключение, если кто-то пытается выполнить обновление с объектом, у которого нет последней версии».
2. Да, я понял это. Но как я могу повторно загрузить текущее состояние?
3. загрузка по идентификатору? Я не знаю. Как вы загрузили его в первую очередь?
4. И это именно то, что я делаю. Я делаю
findById
это, чтобы получить идентификатор объекта. После этого я обновляю его и выполняюsave
. После этого я делаю это сноваfindById
. Но если я сделаюsave
это после этого, я получу исключение5. Или — каково значение поля версия, когда вы вызываете «После этого я снова выполняю get, чтобы вернуть измененный объект»? Это должно быть значение из базы данных, последнее значение после вашего последнего вызова сохранения. Если это не так, то у вас проблема где-то в другом месте. Затем это значение необходимо использовать и использовать в объекте при следующем вызове обновления
Ответ №1:
Вы не получите исключение до тех пор, пока сохранение не будет выполнено в базе данных, и ваше сохранение произойдет не сразу, помните, что оно использует транзакционную обратную запись.
Если у вас автоматический режим очистки, который используется по умолчанию для JPA, то сохранение не будет перенесено в базу данных до вашего второго идентификатора findById, который считается запуском запроса, поэтому любые обновления должны быть сначала сброшены. Как только обновление выполняется, происходит сравнение версий, JPA понимает, что строка не была обновлена, и создает исключение оптимистической блокировки.
Кстати, когда вы говорите, что не используете EntityManager: вы не используете его напрямую, но он все еще задействован, репозиторий JPA использует его. Все эти действия по хранению обновлений и принятию решения о том, когда их отправлять, выполняются менеджером сущности. Он также кэширует результаты выполняемых запросов.
Когда вы говорите «текущее состояние», существует несколько состояний. Есть состояние менеджера сущностей, затем состояние выполняемой транзакции, затем состояние базы данных с зафиксированными изменениями. EntityManager использует режим сброса, чтобы решить, когда синхронизировать свое собственное состояние кэша 1-го уровня с выполняемой транзакцией.
И да, вы должны указать обновляемому объекту номер последней версии, вот что сравнивается.
Комментарии:
1. Спасибо вам за ваше объяснение!