NHibernate сущность слабой связи

Допустим, у меня есть объект под названием MyItem. Он может быть включен во многих "родителей", таких как SomeCollection и SomeOtherCollection. Поскольку это может быть включено во многих родителей, и поскольку я не хочу, чтобы MyItem знал о родителях, я бы не хотел, чтобы в MyItem были какие-либо свойства, ссылающиеся на родителя.

И так как родитель, как SomeCollection, может содержать много многих MyItems, я чувствую, что мне нужно что-то вроде подкачки, чтобы получить детей от родителя. Это не позволило бы мне иметь свойство в SomeCollection, ссылающееся на MyItems. Ленивый загружен или нет, это всегда "все или ничего" (верно?).

Я определенно нуждаюсь в некоторой ссылке между сущностями MyItem и их родителями, хотя бы в форме таблицы сопоставления в базе данных.

Вопросы:

  • Как мне создать сопоставления для этого? Могу ли я иметь сопоставления, или вместо этого следует сохранить отношение в бизнес-логике?
  • Как бы я запросить, какие сущности MyItem существуют в SomeCollection? Могу ли я сделать это только с одной поездкой в ​​базу данных с использованием ICriteria?

2 ответа

Многие-к-одному
Родитель содержит свойство Child, ребенок может быть связан с несколькими родителями.

class Parent
{
    public virtual MyItem Child { get; set; }
}

<class name="Parent">
    <many-to-one name="Child" column="MyItemId" />
</class>

Многие ко многим с соединительным столом
Родитель содержит коллекцию детей, дети могут быть связаны от нескольких родителей.

class Parent
{
    public virtual IList<MyItem> Children { get; set; }
}

<class name="Parent">
    <bag name="Children" table="parent_myitem">
        <key column="parentid" />
        <many-to-many class="MyItem" column="MyItemId" />
    <bag>
</class>

Критерии запроса

// find Parent with child named "foo".
DetachedCriteria.For<Parent>()
    .CreateAlias("Child", "c")
    .Add(Restrictions.Eq("c.Name", "foo"));

// find Parent with particular child
DetachedCriteria.For<Parent>()
    .Add(Restrictions.Eq("Child", child ));


// find Parent with one of children named "foo".
DetachedCriteria.For<Parent>()
    .CreateAlias("Children", "c")
    .Add(Restrictions.Eq("c.Name", "foo"));

// find a "page" of children for a parent
DetachedCriteria.For<Parent>()
    .Add(Restrictions.Eq("Id", parent.Id ))
    .CreateAlias("Children", "c")
    .SetFirstResult( 1041 )
    .SetMaxResults( 20 )
    .GetExecutableCriteria( session )
    .List<MyItem>();

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

Если бы я априори не знал, что дочерние коллекции будут гигантскими, я бы сначала пошел ленивым путем загрузки. Если время и профилирование показывают серьезную медлительность, я бы переключился на метод Критерии.

У меня был такой же случай, как у тебя. У меня был класс конфигурации, который может быть либо глобальной конфигурацией, либо конкретной конфигурацией проекта, либо конкретной конфигурацией пользователя. То, что я сделал, было это:

  1. Я сопоставляю Родителя, как обычно в любом случае, со всеми возможными родителями (кроме глобальных, это означает, что применяется ко всем, поэтому нет родителей)
< class name="ConfigurationDomain" table="configuration">  
    < property name="ProjectId" column="project_id" type="int" insert="false" update="false" />  
    < property name="UserId" column="user_id" type="int" insert="false" update="false" />  
    < many-to-one name="Project" column="project_id" lazy="false" />  
    < many-to-one name="User" column="estimator_id" lazy="false" />
< /class>  
  1. Я отображаю конфигурацию как коллекцию во всех возможных родителях
< class name="UserDomain" table="user">  
    < set name="ConfigurationList" lazy="true" cascade="all-delete-orphan">  
      < key column="user_id" />  
      < one-to-many class="ConfigurationDomain" />  
    < /set>  
< /class>  

< class name="ProjectDomain" table="user">  
    < set name="ConfigurationList" lazy="true" cascade="all-delete-orphan">  
      < key column="project_id" />  
      < one-to-many class="ConfigurationDomain" />  
    < /set>  
< /class>

Просто так, и это сработало для меня. Как мне получить доступ к конфигурации, скажем, пользователя с идентификатором 55 это:

Я не использую someUser.ConfigurationList, так как он медленный. Я только сопоставляю родителя, чтобы я мог сделать это на HQL (это намного быстрее):

select c from ConfigurationDomain c where c.UserId=55

И чтобы получить глобальную конфигурацию, я бы сделал это:

select c from ConfigurationDomain c where (c.UserId IS NULL) and (c.ProjectId IS NULL)

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

ПРИМЕЧАНИЕ. Я использовал Criteria в первые годы своей работы с NHibernate, но потом обнаружил, что HQL несколько более эффективен для меня, у других может быть другое мнение по этому поводу.

Другие вопросы по тегам