Обновление дочерней сущности, но сохранение родительской сущности приводит к исключению ObjectOptimisticLockingFailureException - PullRequest
0 голосов
/ 14 декабря 2018

У меня есть такая сущность:

class Parent {
    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private List<ChildUpdatedByBatch> childrenUpdatedByBatch;
    @OneToOne(mappedBy = "parent", cascade = CascadeType.ALL)
    private Child child;
....
}

У меня есть пакетное задание, которое в конечном итоге сохраняет / обновляет дочернюю сущность ( ChildUpdatedByBatch ), и обычное действие, которое обновляетДочерняя сущность.

Дело в том, что мы используем parentRepository , чтобы обновить обе сущности, поэтому пакетное задание имеет что-то вроде этого:

// updating parent entity by adding/ or updating a ChildUpdatedByBatch
parentRepository.save(parent);

И обычное действиетакже использует:

// updating parent entity by adding/ or updating the Child entity
parentRepository.save(parent);

Но этот второй (обычное действие) вызывает исключение ObjectOptimisticLockingFailureException, потому что

Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [<package>.ChildEntityUpdatedByBatch#911].

Мне интересно, решит ли создание репозиториев для дочерних объектовпроблема.Я имею в виду, что-то вроде:

child.save(child) // with child having a reference to out-of-date parent

Или, если это не решит проблему ни.

Ответы [ 2 ]

0 голосов
/ 13 февраля 2019

У меня была похожая, как мне кажется, проблема, и, возможно, я смогу немного прояснить суть проблемы.

Драйвер MySQL JDBC (и * 1005База данных * MySQL использует REPEATABLE READ в качестве уровня изоляции транзакции по умолчанию.Он более ограничен, чем, например, Oracle по умолчанию READ COMMITTED level.

Так что, на мой взгляд, происходит.

  1. Транзакция X открыта.
  2. Hibernate загружен Parent с 10 children записями.
  3. Другая транзакция Y запущена и удалены все children записи из того же Parent.

  4. REPEATABLE READ означает, что, если вы снова загрузите Parent в транзакции X, вы увидите те же 10 children записей, как Y ничего не сделал.

  5. Но!Когда вы захотите удалить / обновить children записей в транзакции X Hibernate сделает это, но удаление / обновление вернет количество затронутых записей.

Так что это сложная вещь - количество затронутых записей будет равно нулю, потому что транзакция Y уже удалила все записи (это так называемый фантомпрочитать ).Но Hibernate считает, что он должен удалить 10 записей.После получения 0 удаленных записей Hibernate поднимается ObjectOptimisticLockingFailureException, потому что Hibernate считает эту ситуацию ненормальной.

Я исправил эту проблему, изменив уровень изоляции транзакции на READ COMMITTED в свойствах драйвера JDBC MySQL.

Реализация уровня изоляции REPEATABLE READ в MySQL очень сложна, так что вы можетеобратитесь к этой статье.У этого есть хорошее объяснение темы

Понимание Уровней Изоляции MySQL: Repeatable-Read

0 голосов
/ 14 декабря 2018

Добавление хранилища для Child само по себе, конечно, желательно, но я сомневаюсь, что это решит вашу проблему.

Вам все равно придется обновить родителя с ребенком, чтобы у вас оставалось два parentRepository.save(parent);действия, если я правильно понимаю ваше дело.Таким образом, вы все равно получите OptimisticLockException в какой-то момент.

Я бы просто применил общую процедуру для обработки такого рода исключений:

  • Поймать OptimisticLockException
  • Слияние сущности, на которой произошло исключение,
  • Повторная попытка сохранения / обновления
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...