Подтвердить что ты не робот

Ленивые/нетерпеливые стратегии по загрузке в удаленных случаях (JPA)

Я сталкиваюсь с исключениями LazyLoading, такими как большинство людей, которые пытаются удалиться с ORM. В большинстве случаев переключение на желаемую выборку решает проблему (Lazy Loading/Non atomic queries/Thread safety/n + 1 problem...). Но у желающих выборки также есть недостатки, если вы имеете дело с действительно большим графом объектов.

Загрузка всего графа объектов не требуется в большинстве случаев использования. Мне плохо загружать больше необходимых данных (или загружать их из db и извлекать необходимое подмножество).

Итак, какие альтернативные способы решения этой проблемы (во время выполнения)?
Я видел:

  • Ввести зависимость доступа к данным в объект домена и позволить объекту решить либо загрузить ленивый, либо нетерпеливый. Доменный уровень должен быть независимым от любой службы. Доменная инъекция также является дорогостоящей операцией. Домен должен быть недоступен для доступа к данным и должен использоваться с доступом или без доступа к данным.
  • Извлеките все ленивые, за исключением случаев использования, которые требуют больше данных: это лучше для производительности, но это заставляет многих клиентов = > посещать серверы/базы данных. Инициализация ленивых полей также может страдать от боли (с JPA). Этот способ не является общим и относится к тем же ленивым ограничениям, о которых говорилось выше.
  • Инкапсулировать настойчивость в классе Lazy: больше сложностей, нет лучшей практики для взаимодействия с ORM. Уровень вздутия сервисов (так много "написанного вручную" кода чувствует себя плохо).
  • Используйте полные прогнозы для каждого прецедента: мы закончим с SQL и исключим преимущество ORM.
  • Уровень DTO/Virtual Proxy обеспечивает большую сложность и упрощает работу с кодом (Wormhole antipattern → Bloat).

Я много думал о другом. Возможно, общий вариант white./black listning - это решение.

Идея (черный список): определение списка имен классов с границами для операции выборки. Если свойство соответствует и оно ленивое, удалите ленивый (CGLIB) прокси и заполните значение нулем. В противном случае, просто предотвратите выборку (и оставьте значение при нулевом значении). Поэтому мы можем установить четкие границы в наших DAO.

Пример: ProductDao.findByName("Soap",Boundaries.BLACKLIST,"Category, Discount") два последних параметра также могут быть связаны с объектом Boundaries.

Идея (белый список): как черный список, но вы должны объявить свойства, которые должны быть загружены в белый список.

Что вы думаете о таком решении? (Возможные проблемы, ограничения, преимущества...) Как мне написать это в java? Может быть, через AOP, чтобы соответствовать методам DAO (потому что я могу модифицировать поведение прокси-сервера cglib)?

4b9b3361

Ответ 1

  • Вы можете избавиться от всех коллекций и вместо этого использовать NamedQueries. Мы использовали этот подход в одном проекте (EJB + Swing), и он работал очень хорошо - таким образом, вы определяете точные данные, которые нужно извлечь. NamedQueries являются обычными запросами, представьте их как PreparedStatement. Идея заключается не в создании/возврате/обновлении/удалении отдельных объектов с запросами. Идея заключается в том, что вы получаете свои коллекции с запросами. Например, вместо сопоставления списка @ManyToMany, определите NamedQuery, который извлекает этот список. Таким образом, вы можете получать данные коллекции отдельно и только в случае необходимости, а не автоматически.

  • Использовать собственный прокси (используя CGLIB) для перенесенных объектов - всякий раз, когда ссылка на подборку (через ее получатель), попытается восстановить и поймать любой LazyInitializationException и сделать вызов уровня сервера для запрошенных данных.

  • Как и предыдущий, но делать прокси только из коллекций, в том, как Hibernate проксирует их, когда требуется ленивая инициализация.

  • Также обратите внимание на шаблон обработчика значений - может быть полезно.

(Вы также можете использовать hibernate.max_fetch_depth (если используете Hibernate) с комбинацией выше, если она подходит для вашего случая.)

Ответ 2

Хотя это требует немного работы, и для JAX-WS/JAXB требуются последние версии этих библиотек, это очень элегантное решение: создать маршаллер, который может проверить, были ли инициализированы объекты/коллекции.

Как описано здесь: https://forum.hibernate.org/viewtopic.php?f=1&t=998896

Ответ 3

В настоящее время (2013) можно сохранять ленивую загрузку, если вы удаляете свой сервис с помощью GraniteDS.

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

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

Я не эксперт по GraniteDS (пока), но, похоже, он может интегрироваться с уровнями обслуживания JEE6 и Spring и работает со всеми наиболее важными поставщиками JPA.

Естественно, вам нужно будет скрывать удаленный интерфейс GraniteDS за интерфейсом службы для максимизации прозрачного поведения, но это легко сделать, если клиент также использует Spring (поэтому вы вводите службу в соответствии с потребностями среды).