Стремление загрузить дерево с помощью nHibernate повторно запрашивает конечные узлы - PullRequest
2 голосов
/ 08 октября 2011

Нужна некоторая экспертиза, прежде чем я объявлю, что структура NHibernate нарушена или я сумасшедший!

Я пытаюсь загрузить само ссылающиеся на дерево с помощью NHibernate и могу успешно загрузить большинство дочерних списков, однако я не могу заставить его работать с конечными узлами. Мой запрос:

"select p from Proposal p join fetch p.Structures s join fetch s.theChildrenList c " +
"where p._persistenceId = :p1 and s.theProposal = :p1 and c.theProposal = :p1")
.SetParameter("p1", aProposalId)
.SetResultTransformer(new DistinctRootEntityResultTransformer()).
UniqueResult<Proposal>();

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

Я пробовал: 1. используя левое соединение вместо соединения 2. Сделать список детей не-ленивым и fetch = "join" и убрать hql (как подтверждение концепции, снижение производительности в других местах недопустимо) 3. Возиться с не найденным свойством, которое, как я видел, кто-то использовал на форумах спящего режима

Все они дают одинаковые результаты ... один большой запрос со всеми данными (хорошо) и тысячи маленьких запросов, которые не возвращают данных (плохо). Есть идеи?

Я использую NHibernate 2.2, и вот соответствующая часть файла сопоставлений для справки:

  <many-to-one    name="theParentStructure"
                  column="PARENT_STRUCTURE_ID"
                  class="Structure"
                  access="field"
                  update="false"
                  insert="false"
                  not-found="ignore"/>

  <bag            name="theChildrenList"
                  generic="true"
                  table="STRUCTURE"
                  access="field"
                  cascade="all-delete-orphan"
                  inverse="true" fetch="join" lazy="false">  <--Both with and without the last two properties
     <key column="PARENT_STRUCTURE_ID" />
     <one-to-many class="Structure"/>
  </bag>

Любая помощь будет оценена !!

Ответы [ 2 ]

2 голосов
/ 08 октября 2011

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

В прошлый раз, когда я проверял, описанная ситуация была довольно проблемной, поскольку Nhibernate генерировал отдельные запросы для каждого level дочерних объектов. Если вы заранее знаете, что ваша глубина дерева не будет слишком большой, чем, вероятно, вы могли бы жить без eager loading детей ... Но если ваша структура не ограничена в глубине, вы, вероятно, должны искать альтернативы.

Если вы используете SqlServer

Одним из решений, которое я нашел довольно простым для реализации, было использование recursive self-join / CTE. Это решение включает переключение на stored-procedures в качестве источника запроса и повторное создание иерархии вручную в коде. Чтобы получить целое дерево, вам нужен только один запрос в БД (который может включать в себя все виды фильтров и подзапросов + упорядочение), и вы все равно можете повторно использовать сопоставления NHibernate для всех операций CRUD. Один из недостатков этого решения заключается в том, что впоследствии вам придется поддерживать код запроса на стороне базы данных (изменения столбца и отображения и т.

Модифицированный алгоритм обхода дерева предзаказа

Это мое ideal решение для сохранения дерева, поскольку оно не требует stored-procedures и может быть идеально интегрировано с NHibernate и Criteria API (что для меня является огромным преимуществом, поскольку я могу свободно создавать расширенные и многоразовые фильтры в коде). С точки зрения производительности все варианты выбора почти бесплатны - вы перемещаете баланс в сторону операций вставки, обновления и удаления, так как вам нужно пересчитать дерево при каждом изменении - но это можно сделать с помощью hql, например, так ( псевдо-код):

HQLNamedQuery hql = new HQLNamedQuery();
hql.Query = "UPDATE " + typeof(THierarchy).FullName +
    " SET TraversalLeft = (TraversalLeft + :traversalChange) " +
    " WHERE BaseNodeId = :baseNodeId AND TraversalLeft > :minTr AND TraversalLeft <= :maxTr";

Мне удалось реализовать все виды операций с использованием этой техники (например, добавление, добавление диапазона, перемещение, изменение порядка, получение потомков, получение предков, подсчет потомков и т. Д.), И я должен сказать, что как только вы получите Основной принцип, вы можете идеально интегрировать его в разные проекты.

Ссылка на статью, которая подтолкнула меня к этой теме (к сожалению, NHibernate не используется): http://weblogs.asp.net/aghausman/archive/2009/03/16/storing-retrieving-hierarchical-data-in-sql-server-database.aspx

Отображение дерева в NHibernate

Может быть, вам стоит взглянуть на этот пост (точная часть начинается с An alternative approach): http://nhibernate.hibernatingrhinos.com/16/how-to-map-a-tree-in-nhibernate

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

Надеюсь, это поможет.

0 голосов
/ 10 октября 2011

Я думаю, что основная причина в том, что NHibernate не может знать, что запрос возвращает все записи в иерархии.То есть он не знает, что узел является листовым узлом.Как это могло быть?

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

...