Я хочу восстановить после неудачной транзакции.
Теперь, конечно же, после любого отката все сущности отключаются, а диспетчер сущностей закрывается. Однако пользовательский интерфейс по-прежнему содержит отдельные объекты. Очевидно, что мы не можем просто выбросить изменения пользователя, поэтому мы хотели бы позволить им повторить попытку (исправить выделенную ошибку проверки, затем снова нажать кнопку).
Следуя Java Persistence WikiBook,
Один из методов обработки ошибок - вызвать слияние для каждого управляемого объекта после сбоя фиксации в новом EntityManager, а затем попытаться зафиксировать новый EntityManager. Одна из проблем может заключаться в том, что любые назначенные идентификаторы или оптимистичные версии блокировки, которые были назначены или увеличены, могут нуждаться в сбросе. Кроме того, если исходный EntityManager был РАСШИРЕН, любые используемые объекты все равно будут отсоединены, и их необходимо будет сбросить.
Поначалу этот вариант кажется простым, пока мы неизбежно не столкнемся именно с теми ожидаемыми проблемами. Некоторые службы могут запускать сброс по разным причинам, который увеличивает @Version
поля как в БД (которая откатывается), так и в объектах Java (чего нет). Следующее "сохранение" вызывает merge
, что вызывает неожиданный OptimisticLockException
.
Есть ли надежный способ "откатить" поля версии в объектных компонентах Java?
Хорошо, это сложно. У нас есть каскадные сущности со своими собственными @Versions, поэтому делать это вручную кажется хрупким. (В любом случае, как мы можем достоверно узнать исходные (сохраненные) версии? Не можем запросить, потому что какой-то другой пользователь может успешно обновить объект за это время; запрос текущей версии может нарушить автоматическую блокировку!)
Другой более сложный метод обработки ошибок - всегда работать с нетранзакционным EntityManager. Когда пришло время фиксировать, создается новый EntityManager, нетранзакционные объекты объединяются в него, и новый EntityManager фиксируется. Если фиксация завершается неудачно, только состояние нового EntityManager может быть несогласованным, исходный EntityManager не будет затронут. Это может позволить исправить проблему и повторно объединить EntityManager с другим новым EntityManager. Если фиксация успешна, любые изменения фиксации могут быть объединены обратно в исходный EntityManager, который затем можно продолжать использовать в обычном режиме. Это решение требует значительных накладных расходов, поэтому его следует использовать только в том случае, если действительно требуется обработка ошибок, а поставщик JPA не предоставляет альтернатив.
Это кажется логичным. Есть ли у кого-нибудь опыт реализации такого рода восстановления с двумя EntityManager (особенно с Spring)? Есть ли подводные камни, о которых я должен знать, прежде чем пытаться это сделать? Похоже, что теперь каждая служба и DAO должны знать о двух менеджерах сущностей (в Spring сегодня они почти не зависят от уровня персистентности). Операции поиска DAO используют один EM; 'update' использует другое. Или используйте отдельные DAO для чтения и записи. Ой.
Другие варианты, которые я рассмотрел, включают:
- Используйте DTO в пользовательском интерфейсе, поэтому автоинкремент ни на что не влияет. Уродливый.
- Переместите вызов
merge
в конец любой составной операции. Сущность присоединяется только после успешной проверки и обновления состояния. Кажется странным, что служба "сохранения" больше неmerge
(только проверяет). Фактически, пользовательский интерфейс возьмет на себя ответственность за вызов DAO! Это так необычно, как кажется?
Совет? Спасибо :-)
Обновить Моя архитектура включает в себя:
- Отдельные сущности обновляются пользовательским интерфейсом (JSF)
- Идентификаторы объектов НЕ генерируются автоматически (предварительно назначенные UUID и / или бизнес-ключи)
- Сущности имеют автоматически увеличивающиеся
@Version
поля для блокировки - Служба "Сохранить" проверяет, вызывает
em.merge
(JPA через Hibernate) - Сервисы "Process" проверяют, применяют бизнес-логику, обновляют состояние объекта
- Services can be composed. One UI button might do
- (Spring
@Transactional
advice around UI controller: begin) - Сохранять
- Процесс 1
- Процесс 2
- (
@Transactional
: совершить)
- (Spring
- Любая служба может вызвать исключение проверки (JSR 303). , который откатывается, как ожидалось (сообщения отображаются в пользовательском интерфейсе)
EntityManager.merge()
опасно. См. JPA 2.1, раздел 3.3.3 Откат транзакции. В нем говорится: [..] состояние атрибутов версии и сгенерированное состояние (например, сгенерированные первичные ключи) могут быть несовместимыми. Как указывается в этом разделе, передача такой обособленной сущности в операцию слияния может завершиться ошибкой. В разделе 3.2.7.1 «Слияние состояния отдельного объекта» есть объяснение: любые столбцы версии, используемые объектом, должны проверяться реализацией постоянства времени выполнения во время операции слияния [..]. - person Martin Andersson   schedule 03.11.2014