Как контролировать постоянство JPA в формах Wicket? - PullRequest
4 голосов
/ 18 марта 2011

Я создаю приложение с использованием JPA 2.0 (реализация Hibernate), Spring и Wicket. Все работает, но я обеспокоен тем, что мое поведение в форме основано на побочных эффектах.

В качестве первого шага я использую OpenEntityManagerInViewFilter. Мои доменные объекты выбираются с помощью LoadableDetachableModel, который выполняет entityManager.find() в своем методе load. В моих формах я обертываю CompoundPropertyModel вокруг этой модели, чтобы связать поля данных.

Меня беспокоит форма отправки действий. В настоящее время моя форма отправки передает результат form.getModelObject() в сервисный метод, помеченный @Transactional. Поскольку сущность внутри модели по-прежнему привязана к менеджеру сущностей, аннотации @Transactional достаточно для принятия изменений.

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

  • Фрагментируйте мою сущность на подкомпоненты, соответствующие формам редактирования, и создайте главную сущность, связав их вместе в @OneToOne взаимосвязь. Вызывает уродливый дизайн таблицы и затрудняет последующее изменение форм.
  • Отсоединение объекта сразу же после его загрузки с помощью LoadableDetachableModel и ручное объединение правильных полей на сервисном уровне. Трудно управлять отложенной загрузкой, могут потребоваться специализированные версии модели для каждой формы, чтобы обеспечить загрузку правильных дочерних объектов.
  • При создании модели для формы клонируйте объект в локальную копию, а затем вручную объедините правильные поля в слое обслуживания. Требуется реализация большого количества конструкторов копирования / методов клонирования.
  • Используйте опцию Hibernate dynamicUpdate, чтобы обновлять только измененные поля сущности. Вызывает нестандартное поведение JPA во всем приложении. Не виден в уязвимом коде и вызывает сильную связь с реализацией Hibernate.

Ответы [ 2 ]

3 голосов
/ 22 марта 2011

РЕДАКТИРОВАТЬ

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

Помимо этого, при условии, что вы довольны «победами при последней записи» в подгруппах свойств,тогда «dynamicUpdate» в Hibernate кажется наиболее разумным решением, если только вы не подумаете о переключении ORM в ближайшее время.Я нахожу странным, что JPA, по-видимому, не предлагает ничего, что позволяет вам только обновлять грязные поля, и нахожу вероятным, что это произойдет в будущем.

Дополнительно (мой оригинальный ответ)

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

Очевидный ответ, как говорит Адриан в своем комментарии, заключается в использовании традиционной транзакции:фильтр по запросу.Это гарантирует, что все операции в запросе происходят в одной транзакции.Однако определенно будет использовать подключение к БД при каждом запросе.

Существует более элегантное решение с кодом здесь .Техника заключается в том, чтобы лениво создавать экземпляр управления данными и начинать транзакцию только тогда, когда это необходимо (т.е. когда происходит первый вызов EntityModel.getObject ()).Если есть транзакция, открытая в конце цикла запроса, она фиксируется.Преимущество этого состоит в том, что никогда не бывает потраченных впустую соединений с БД.

Данная реализация использует объект wicket RequestCycle (обратите внимание, что это немного отличается в v1.5 и более поздних версиях), но вся реализация на самом деле довольно общаяТаким образом, и вы можете использовать его (например) без калитки через фильтр сервлета.

0 голосов
/ 21 марта 2011

После некоторых экспериментов я придумал ответ. Спасибо @artbristol, который указал мне правильное направление.

  1. Я установил правило в своей архитектуре: методы сохранения DAO должны вызываться только для сохранения отдельных объектов. Если объект подключен, DAO выдает IllegalStateException. Это помогло отследить любой код, который изменял сущности вне транзакции.
  2. Затем я изменил свой LoadableDetachableModel, чтобы иметь два варианта. Классический вариант для использования в представлениях данных только для чтения возвращает сущность из JPA, которая будет поддерживать отложенную загрузку. Второй вариант, для использования в связывании формы, использует Dozer для создания локальной копии.
  3. Я расширил базовый DAO для двух вариантов сохранения. Один сохраняет весь объект, используя merge, а другой использует Apache Beanutils для копирования списка свойств.

Это, по крайней мере, позволяет избежать повторяющегося кода. Недостатки - это необходимость настроить Dozer таким образом, чтобы он не загружал всю базу данных, следуя лениво загруженным ссылкам, и имея еще больше кода, который ссылается на свойства по имени, отбрасывая безопасность типов.

...