Повторная попытка отката транзакции JPA и восстановление: объединение сущности с автоинкрементным @Version - PullRequest
7 голосов
/ 26 июня 2011

Я хочу восстановить после неудачной транзакции.

Теперь, конечно, после любого отката все сущности отсоединяются, а менеджер сущностей закрывается.Тем не менее, пользовательский интерфейс по-прежнему содержит отдельные объекты.Очевидно, что мы не можем просто отбросить изменения пользователя, поэтому мы хотели бы позволить им повторить (исправьте выделенную ошибку проверки, затем снова нажмите кнопку).

После Java Persistence WikiBook ,

Одним из методов обработки ошибок является вызов объединения для каждого управляемого объекта после сбоя фиксации в новом EntityManager, а затем попытка зафиксировать новый EntityManager.Одной из проблем может быть то, что любые идентификаторы, которые были назначены, или версии оптимистической блокировки, которые были назначены или увеличены, возможно, потребуется сбросить.Кроме того, если исходный EntityManager был EXTENDED, все используемые объекты все равно будут отсоединены, и их необходимо будет сбросить.

Сначала эта опция кажется простой, пока мы неизбежно не столкнемся именно с этими ожидаемыми проблемами.,Некоторые службы могут вызывать сброс по различным причинам, что приводит к увеличению полей @Version как в БД (откат), так и в объектах Java (которых нет).Следующее «сохранение» вызывает merge, что приводит к неожиданному OptimisticLockException.

Есть ли надежный способ «откатить» поля версии в бинах сущностей Java?

ОК, швы очень тяжелые.У нас есть каскадные сущности с их собственными @Versions, поэтому выполнение этого вручную кажется хрупким.(Как мы можем достоверно знать исходные (сохраненные) версии в любом случае? Невозможно выполнить запрос, потому что какой-то другой пользователь может успешно обновить сущность в это время; запрос текущей версии может прервать оплокирование!)

Еще один более сложный метод обработки ошибок - всегда работать с нетранзакционным EntityManager.Когда приходит время для фиксации, создается новый EntityManager, нетранзакционные объекты объединяются в нем, и новый EntityManager фиксируется.Если фиксация завершится неудачно, только состояние нового EntityManager может быть непоследовательным, исходный EntityManager останется неизменным.Это может позволить исправить проблему, и EntityManager повторно объединится в другой новый EntityManager.Если фиксация прошла успешно, любые изменения фиксации можно объединить обратно в исходный EntityManager, который затем можно будет продолжать использовать как обычно.Это решение требует значительных накладных расходов, поэтому его следует использовать только в том случае, если действительно требуется обработка ошибок, а поставщик JPA не предоставляет альтернатив.

Это кажется логичным. Есть ли у кого-нибудь опыт реализации такого восстановления с помощью двух EntityManager (особенно в Spring)?Есть ли какие-либо подводные камни, о которых я должен знать, прежде чем пытаться это сделать? Кажется, что каждый сервис и DAO теперь должны были бы узнать о двух менеджерах сущностей (с Spring, сегодня они почти не зависят от уровня постоянства).Операции DAO 'find' используют один EM;«обновление» использует другой.Или используйте отдельные DAO для чтения и записи.Ой.

Другие варианты, которые я рассмотрел, включают:

  • Использование DTO в пользовательском интерфейсе, поэтому автоинкремент не влияет ни на что.Ужасно.
  • Переместите вызов на merge на конец любой составной операции.Объект присоединяется только после успешного завершения всех проверок и обновлений состояния.Кажется странным, что служба «save» больше не будет merge (только проверка).По сути, пользовательский интерфейс будет нести ответственность за вызов DAO!Это так необычно, как кажется?

Советы? Спасибо: -)

Обновление Моя архитектура включает в себя:

  • Отдельные сущности обновляются пользовательским интерфейсом (JSF)
  • Идентификаторы сущностей НЕ генерируются автоматически (предварительно назначенные идентификаторы UUID и / или бизнес-ключи)
  • Для сущностей автоматически увеличиваются поля @Versionдля оплокирования
  • Проверка службы «Сохранить», вызовы em.merge (JPA через Hibernate)
  • Проверка службы «Обработка», применение бизнес-логики, обновление состояния объекта
  • Услуги могут быть составлены. Одна кнопка пользовательского интерфейса может сделать
    1. (Spring @Transactional совет по контроллеру пользовательского интерфейса: begin )
    2. Сохранить
    3. Процесс 1
    4. Процесс 2
    5. (@Transactional: commit )
  • Любая служба может выдать исключительную ситуацию проверки (JSR 303), которая откатывается должным образом (сообщения отображаются в пользовательском интерфейсе)

Ответы [ 4 ]

1 голос
/ 13 апреля 2014

Вы задаете два разных вопроса, а затем задаете общий вопрос. Я иду с советом (поместил бы это в комментарии, но ТАК не позволяет мне комментировать пока ...)

Использование DTO (или любой структуры данных только для пользовательского интерфейса) в пользовательском интерфейсе не страшно - это архитектурно правильно. Я согласен с Дж. Б. Низетом, что нетранзакционный EM-подход по сути превращает ваши организации в DTO. Если вы должны использовать сущности в своем пользовательском интерфейсе (и я понимаю, что Spring поощряет это), это путь.

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

1 голос
/ 26 июня 2011

Может поддерживать только комментарий JB Nizet: при слиянии копируется только состояние отключенных сущностей.

0 голосов
/ 06 июля 2016

Поскольку EntityManager.merge() создает копию переданной сущности, решение кажется довольно простым: поддерживать ссылку на переданную сущность в пользовательском интерфейсе (компонент JSF) до успешного завершения транзакции и только после этого обновлятьссылка с скопированной сущностью:

@ManagedBean
@SomeScoped
public class EntityBean
{
    @EJB
    private PersistenceService service;

    private Object entity;

    public void update()
    {
        try
        {
            // service.update() is a CMT method
            Object updatedEntity = service.update(entity);

            // if we are here transaction has completed successfully, so update the reference 
            entity = updatedEntity;
        }
        catch(Exception e)
        {
            // if catched, entity has not been modified (just like transaction never happened)
            // now handle exception (log, add a FacesMessage, rethrow, ...)
        }
    }

    ...
}

Вы можете повторно визуализировать пользовательский интерфейс, и UIComponent s будет использовать ту же сущность, которую пользователь заполнил перед отправкой предыдущего запроса.

Таким образом, вам не нужно беспокоиться о сбросе @Id s, @Version s, ни о каких-либо других измененных полях или свойствах, включая каскадные ассоциации, ни об использовании нескольких EntityManager s, ни об использовании расширенного контекста персистентности.

Тем не менее, этот простой пример имеет некоторые ограничения:

  1. никогда не использует EntityManager.persist на «переданном» объекте, так как persist не выполняет копирование.Вы должны всегда использовать EntityManager.merge, но есть случаи, когда это просто невозможно.

  2. переданные объекты должны быть в состоянии «ПЕРЕХОДНОЕ» или «ОТКЛЮЧЕНО» для EntityManager.merge создать копию (в противном случае переданная сущность возвращается, копия не выполняется).Это подразумевает, что использование расширенного контекста персистентности или OpenSessionInViewFilter и его производных строго запрещено.

  3. Особое внимание следует уделить реализации компоновки сервиса с суб-транзакциями: вы можете покончить с завинченнымиСубтранзакция успешно завершена, но родительский откат.

Обратите внимание, что я не знаю, как работает Spring, поэтому все это просто может быть неприменимо.

0 голосов
/ 12 апреля 2014

Очевидно, мы не можем просто выбросить изменения пользователя

если пользователь пытается обновить, а кто-то другой уже изменил его, ему лучше обновить копию и снова внести изменения, нет?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...