NHibernate родитель, детская коллекция с фьючерсами - PullRequest
0 голосов
/ 09 апреля 2011

У меня есть такая настройка: Родитель, с коллекцией Child.

class Parent {
    IList<Child> Childs { get; set; }
}

HQL:

(«От родителя»). Будущее ();

(«От ребенка»). Будущее ();

foreach(Parent p in result) {
    foreach(Child c in p.Childs) {
    }
}

Это дает классическую проблему N + 1. Два оператора SQL отправляются на сервер за одну поездку туда и обратно, поэтому все данные находятся в кэше первого уровня, поэтому почему NH все еще существует SQL для каждого дочернего элемента.

Версия 3.1.0.400

1 Ответ

5 голосов
/ 09 апреля 2011

Когда вы выполняете будущий запрос, вы помещаете все родительские и дочерние объекты в кэш 1-го уровня.Родительские объекты содержат ленивую коллекцию, которую необходимо заполнить.Чтобы заполнить коллекцию, NHibernate должен запросить базу данных.(Мы выясним, почему всего за секунду.) Запрос возвращает дочерние объекты, и эти дочерние объекты уже находятся в кэше L1.Таким образом, эти объекты используются для заполнения коллекции.

Теперь, почему NHibernate вынужден запрашивать базу данных для заполнения коллекции Childs?В коллекции может быть предложение where, отфильтровывающее дочерние объекты с IsDeleted == true.Вы можете иметь код в EventListener, который отфильтровывает определенные дочерние объекты.По сути, многое может произойти, и NHibernate не может делать какие-либо предположения об отношениях между родительским и дочерним объектами.

Вы можете предоставить ему достаточно информации, указав стратегию выборки в HQL или в вашем отображении.В HQL вы могли бы написать:

var parents = session.CreateQuery("from Parent p join fetch p.Childs").Future<Parent>();

Запрос объекта Child, использующий будущее, будет совершенно необязательным, так как вы выбираете детей с родителями.Из-за выборки соединения вы получите дубликаты родительских объектов, хотя они будут одним и тем же объектом.(Вы выполняете внутреннее объединение в базе данных и возвращаете одну копию родительской строки для каждой дочерней строки.) Вы можете избавиться от них, перебирая parent.Distinct ().

Если вы всегда хотитечтобы получить дочерние объекты с соответствующим родительским объектом, вы также можете использовать fetch = "join" в вашем родительском отображении.

<bag name="Children" cascade="all-delete-orphan" fetch="join">
  <key column="ParentId"/>
  <one-to-many class="Child"/>
</bag>

Если ни один из этих параметров не подходит для вашего сценария, вы можете указать размер пакета всоставление карт.Вы по-прежнему будете выполнять запрос к базе данных, когда нажмете parent.Childs, но NHibernate с радостью инициализирует любые другие прокси-серверы коллекции.

<bag name="Children" cascade="all-delete-orphan" batch-size="10">
  <key column="ParentId"/>
  <one-to-many class="Child"/>
</bag>
...