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>();
Этот последний запрос может быть или не быть более эффективным, если использовать ленивую загрузку всей дочерней коллекции при первом доступе, а затем индексировать ее на последующих "страницах". Это зависит от ваших данных и использования.
Если бы я априори не знал, что дочерние коллекции будут гигантскими, я бы сначала пошел ленивым путем загрузки. Если время и профилирование показывают серьезную медлительность, я бы переключился на метод Критерии.
У меня был такой же случай, как у тебя. У меня был класс конфигурации, который может быть либо глобальной конфигурацией, либо конкретной конфигурацией проекта, либо конкретной конфигурацией пользователя. То, что я сделал, было это:
- Я сопоставляю Родителя, как обычно в любом случае, со всеми возможными родителями (кроме глобальных, это означает, что применяется ко всем, поэтому нет родителей)
< 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>
- Я отображаю конфигурацию как коллекцию во всех возможных родителях
< 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 несколько более эффективен для меня, у других может быть другое мнение по этому поводу.