Избегайте выбора N + One и неверных результатов из eclipselink с пакетным чтением - PullRequest
9 голосов
/ 22 апреля 2011

Я пытаюсь сократить количество n + 1 выборок, понесенных моим приложением, приложение использует EclipseLink в качестве ORM, и в максимально возможном количестве мест я пытался добавить подсказку о пакетном чтении к запросам. В большом количестве мест в приложении я не всегда точно знаю, через какие отношения я буду проходить (в моем представлении отображаются поля, основанные на пользовательских настройках). В этот момент я хотел бы выполнить один запрос, чтобы заполнить все эти отношения для моих объектов.

Моя мечта - вызвать что-то наподобие ReadAllRelationshipsQuery (Collection, RelationshipName) и заполнить все эти элементы, чтобы в дальнейшем вызывать:

Collection.get (0) .getMyStuff уже будет заполнен и не вызовет запрос БД. Как я могу сделать это? Я готов написать любой код, который мне нужен, но я не могу найти способ работы с инфраструктурой eclipselink?

Почему бы мне просто не прочитать все возможные поля и позволить им загружаться лениво? Я обнаружил, что держатели пакетных значений, которые реализуют пакетное чтение, плохо работают с кешем eclipselink. Если держатель значения пакетного чтения не «оценивается» и попадает в кэш ссылок затмения, он может устареть и вернуть неверные данные (это поведение было зарегистрировано как ошибка eclipselink, но отклонено ...) edit : Я нашел ссылку на ошибку здесь: https://bugs.eclipse.org/bugs/show_bug.cgi?id=326197

Как мне избежать выбора N + 1 для объектов, на которые у меня уже есть ссылка?

Ответы [ 4 ]

7 голосов
/ 29 апреля 2011

У вас есть три основных способа загрузки данных в объекты из решения на основе JPA. Это:

  1. Загрузка динамически путем обхода объекта (например, myObject.getMyCollection (). Get ()).
  2. Загрузка графиков объектов путем динамической предварительной выборки с использованием JPA QL (например, FETCH JOINs, как описано в учебник Oracle JPA )
  3. Загрузка путем установки режима выборки ( Есть ли способ изменить тип выборки JPA для метода? )

У каждого из них есть свои плюсы и минусы.

  1. Динамическая загрузка с помощью трансверсального объекта приведет к увеличению количества запросов (с высокой степенью таргетинга). Эти запросы, как правило, небольшие (не большие операторы SQL, но могут загружать много данных) и, как правило, прекрасно работают с кешем второго уровня, но вы можете получить много-много маленьких запросов.
  2. Предварительная выборка с помощью JPA QL даст вам именно то, что вы хотите, но это предполагает, что вы знаете, чего хотите.
  3. Установка режима выборки в EAGER автоматически загрузит много и много данных для вас, но в зависимости от конфигурации и использования это может на самом деле не сильно помочь (или может ухудшить ситуацию), так как вы можете перетащить LOT данных из БД в ваше приложение, которые вы не ожидали.

Несмотря на это, я настоятельно рекомендую использовать p6spy (http://sourceforge.net/projects/p6spy/) в сочетании с любым приложением на основе JPA, чтобы понять эффекты вашей настройки.

К сожалению, JPA упрощает некоторые вещи, а некоторые затрудняет, в основном, побочные эффекты от вашего использования. Например, вы можете решить одну проблему, установив режим извлечения на «нетерпеливый», а затем создать еще одну проблему, когда стремительное извлечение извлекает слишком много данных. EclipseLink предоставляет инструменты, помогающие разобраться в этом ( EclipseLink Performance Tools )

Теоретически, если вы хотите, вы можете написать универсальный обходчик свойств JavaBean, используя что-то вроде Apache BeanUtils . Обычно достаточно просто вызвать метод, подобный size (), для принудительной загрузки (хотя использование размера выборки пакета может немного усложнить).

Одной вещью, на которую следует обратить особое внимание, является объем вашей сессии и использование вами кэшей ( EclipseLink cache ).

Что-то непонятное из вашего поста - это область сеанса. Является ли сеанс одним выстрелом (например, как запрос веб-страницы) или это длительный процесс (например, как классическое приложение клиент / сервер с графическим интерфейсом)?

2 голосов
/ 21 июня 2012

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

Если ваше приложение запрашивает, какие отношения оно хочет, то на каком-то уровне вы должны знать, какие отношения вам требуются, и должны иметь возможность оптимизировать их в своем запросе для объектов.

Обзор методов оптимизации отношений см.

http://java -persistence-performance.blogspot.com / 2010/08 / партии сгрузить-оптимизации объектно-graph.html

Для пакетной выборки существует три типа: JOIN, EXISTS и IN. Описанная вами проблема изменений данных, влияющих на исходный запрос для пакетных отношений кэша, применима только к JOIN и EXISTS, и только в том случае, если у вас есть критерии выбора, основанные на полях updateale (если оптимизируемый вами запрос относится к id или ко всем экземплярам) ты в порядке). Пакетная загрузка IN не имеет этой проблемы, поэтому вы можете использовать пакетную выборку IN для всех отношений и не иметь этой проблемы.

ReadAllRelationshipsQuery (Collection, RelationshipName)

Как насчет,

Query query = em.createQuery("Select o from MyObject o where o.id in :ids");
query.setParameter(ids, ids);
query.setHint("eclipselink.batch", relationship);
0 голосов
/ 29 апреля 2011

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

0 голосов
/ 25 апреля 2011

В запросе используйте FETCH JOIN для предварительной выборки отношений.

Имейте в виду, что результирующие строки будут перекрестным произведением всех выбранных строк, что может легко выполнить больше работы, чем запросы N + 1.

...