Как предотвратить перезагрузку Hibernate из поля версии на флеш? [Дубликат]

Как будто вы пытаетесь получить доступ к объекту, который является null. Рассмотрим ниже пример:

TypeA objA;

. В это время вы только что объявили этот объект, но не инициализировали или не инициализировали. И всякий раз, когда вы пытаетесь получить доступ к каким-либо свойствам или методам в нем, он будет генерировать NullPointerException, что имеет смысл.

См. Также этот пример:

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
9
задан Adrian Shum 19 June 2015 в 01:43
поделиться

4 ответа

К сожалению, (по крайней мере, для Hibernate) изменение поля @Version вручную не сделает его еще одной «версией». То есть оптимизированная проверка параллелизма выполняется против значения версии, полученной при чтении объекта, а не в поле версии объекта при его обновлении.

, например.

Это будет работать

Foo foo = fooRepo.findOne(id);  // assume version is 2 here
foo.setSomeField(....);

// Assume at this point of time someone else change the record in DB, 
// and incrementing version in DB to 3

fooRepo.flush();  // forcing an update, then Optimistic Concurrency exception will be thrown

Однако это не сработает

Foo foo = fooRepo.findOne(id);  // assume version is 2 here
foo.setSomeField(....);
foo.setVersion(1);
fooRepo.flush();  // forcing an update, no optimistic concurrency exception
                  // Coz Hibernate is "smart" enough to use the original 2 for comparison

. Есть способ обхода этого. Самый простой способ - это, вероятно, реализовать оптимистичную проверку параллельности самостоятельно. Раньше у меня была утилита для сбора данных «DTO to Model», и я поставил туда эту логику проверки версий. Другой способ - поместить логику в setVersion(), которая вместо реальной установки выполняет проверку версии:

class User {
    private int version = 0;
    //.....

    public void setVersion(int version) {
        if (this.version != version) {
            throw new YourOwnOptimisticConcurrencyException();
        }
    }

    //.....
}
13
ответ дан Adrian Shum 19 August 2018 в 05:05
поделиться

В дополнение к ответу @Adrian Shum, я хочу показать, как я решил эту проблему. Если вы хотите вручную изменить версию Entity и выполнить обновление, чтобы вызвать OptimisticConcurrencyException, вы можете просто скопировать Entity со всем своим полем, что заставило объект покинуть свой контекст (так же, как EntityManager.detach()). Таким образом, он ведет себя надлежащим образом.

Entity entityCopy = new Entity();
entityCopy.setId(id);
... //copy fields
entityCopy.setVersion(0L); //set invalid version
repository.saveAndFlush(entityCopy); //boom! OptimisticConcurrencyException

EDIT: собранная версия работает, только если кэш-память спящего режима не содержит объект с тем же идентификатором. Это не сработает:

Entity entityCopy = new Entity();
entityCopy.setId(repository.findOne(id).getId()); //instance loaded and cached 
... //copy fields
entityCopy.setVersion(0L); //will be ignored due to cache
repository.saveAndFlush(entityCopy); //no exception thrown  
0
ответ дан Dmitry 19 August 2018 в 05:05
поделиться
  • 1
    Как вы просматриваете ранее существовавший объект, но не имеете его в кеше, чтобы сделать работу с первым примером кода? – pgreen2 12 December 2017 в 20:02
  • 2
    @ pgreen2 В этом сценарии вы просматриваете существующий объект, но для сохранения в БД вы его собираете, используя тот, который отправляется пользователем. Тогда поле версии отдельного объекта может быть изменено. И наоборот, поле версии получаемого объекта не может быть изменено. – Dmitry 14 December 2017 в 11:45

Правильно ответ на часть ответа @AdrianShum.

. Поведение сравнения версий по умолчанию в основном выполняет следующие шаги:

  1. Получить версию с версией с ее номером версии, позволяет вызывать V1 .
  2. Предположим, вы изменили свойство какой-либо сущности, затем Hibernate увеличивает номер версии до V2 «в памяти». Он не касается базы данных.
  3. Вы совершаете изменения или автоматически выполняете среду, а затем Hibernate будет пытаться обновить объект, включая его номер версии, значением V2. Запрос на обновление, сгенерированный Hibernate, будет изменять реестр объекта только в том случае, если он соответствует идентификатору и номеру предыдущей версии (V1).
  4. После того, как реестр сущностей успешно изменен, объект принимает V2 как свою фактическую версию Значение.

Теперь предположим, что между шагами 1 и 3 объект был изменен другой транзакцией, поэтому его номер версии на этапе 3 не является V1. Затем, когда номер версии отличается, запрос на обновление не будет изменять какой-либо реестр, hibernate поймет это и выбросит исключение.

Вы можете просто протестировать это поведение и убедиться, что исключение выбрано напрямую, изменяя номер версии в вашей базе данных между шагами 1 и 3.

Изменить. Не знаете, какой провайдер постоянства JPA вы используете с Spring Data JPA, но для получения более подробной информации об оптимизации блокировки с JPA + Hibernate я предлагаю вам прочитать главу 10, раздел Управление одновременным доступом , книги Сохранение Java с гибернацией (спящий режим в действии)

0
ответ дан Guillermo 19 August 2018 в 05:05
поделиться
  • 1
    Вы пропустили то, что мы обсуждаем. Нам всем ясно, как работает оптимистический параллелизм. Мы фокусируемся на том, что для запроса на обновление, созданного Hibernate, он НЕ использует поле @Version в Entity. Вместо этого Hibernate хранит версию сущности где-то еще и использует ее для оптимизации проверки параллелизма. – Adrian Shum 18 June 2015 в 10:35
0
ответ дан Michal 30 October 2018 в 16:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: