2010-06-28 3 views
5

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

public class A 
{ 
    public virtual int Id { get; set;} 
    public virtual List<B> MyList { get; set; } 
} 

public class B 
{ 
    public virtual int Id { get; set; } 
    public virtual DateTime Date { get; set; } 
    public virtual A FKtoA { get; set; } 
} 

Я хотел бы получить все записи из таблица A, которая имеет все элементы своего свойства MyList с датой меньше заданного значения.

Как я могу это сделать с элегантным синтаксисом NHibernate?

ответ

1

Я обязан Вам "элегантный" часть ... :-)

Это возможный HQL. Обратите внимание, что инвертированное ваше условие: вместо поиска «A, которые имеют все элементы своего свойства MyList с датой менее заданного значения», я ищу «A, что не имеет элементов их MyList свойство с датой больше или равно заданное значение ".

from A a 
where a not in 
     (select a1 
     from A a1, B b 
     where b.Date >= :date 
     and b in elements(a1.MyList)) 

Использование:

var results = session.CreateQuery("hql from above") 
        .SetParameter("date", DateTime.Today) 
        .List(); 

Обратите внимание, что, если вы объявляете двунаправленную связь между A и B (путем добавления A свойства), запрос намного проще:

from A a 
where a not in 
     (select b.A 
     from B b 
     where b.Date >= :date) 

Обновление: вот как это делается с критериями:

session.CreateCriteria<A>().Add(
    Subqueries.PropertyNotIn("id", 
          DetachedCriteria.For<A>() 
           .CreateCriteria("MyList") 
           .SetProjection(Projections.Property("id")) 
           .Add(Restrictions.Ge("Date", DateTime.Today)))) 
+0

Хорошая точка для двунаправленных отношений: я добавлю свойство типа A в своем классе B. HQL замечательный, но мне было интересно, не можем ли мы получить методы DetachedCriteria() и Projections.Max() – PierrOz

+0

OK, Я добавил параметр Criteria (это было бы нелегко принять и изменить его, чтобы использовать 'Subqueries.PropertyIn' и инвертировать подзапрос, чтобы использовать проекцию). Однако критерии более полезны для динамически построенных запросов (поиска). Посмотрите на весь шум, по сравнению с HQL. –

+1

замечательно спасибо много !! – PierrOz

0

Используйте этот

ICriteria criteria = session.CreateCriteria<ClassOfTableOne>(); 
criteria.CreateAlias("FieldNameOfTypeTable2","aliasName"); 
criteria.SetFetchMode("aliasName", FetchMode.Join); 
criteria.Add(Restrictions.Lt("aliasName.Date", yourdate)); 
+0

Этот запрос НЕ возвращает то, что ожидает PierrOz. –

0

если ваш класс B выглядит примерно так (где MyList свойство выглядит для этого FK)

public class B 
{ 
    public virtual int Id { get; set; } 
    public virtual DateTime Date { get; set; } 
    public virtual A FK_ToA { get; set; } 
} 

тогда я думаю, что вы ищете (HQL)

nhSes.CreateQuery("select b.FK_ToA from B b where b.Date < :passedDate") 
    .SetTimestamp("passedDate", DateTime.Now).List<A>() 
-1

В настоящее время принятый ответ основан на коррелированном подзапросе, который, как правило, является просто «плохим SQL».

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

По сути вы хотите, чтобы ваш SQL выглядеть следующим образом:

SELECT 
A.Id 
FROM A 
LEFT OUTER JOIN B ON A.Id = B.FKtoA 
WHERE B.Date < @MyDate 

Это читается как «Я хочу, чтобы набор столбцов от А по сравнению с набором B, где B дата меньше некоторого значения». Это может быть достигнуто с помощью ICriteria API:

ICriteria criteria = session.CreateCriteria<A>(); 
criteria.CreateAlias("MyList", "b", JoinType.LeftOuterJoin) 
criteria.Add(Restrictions.Lt("b.Date", myDate)); 
criteria.SetResultTransformer(new DistinctRootEntityResultTransformer()); 
criteria.List<A>(); 

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

+0

Я предлагаю вам вернуться к своим «правилам» с некоторыми твердыми фактами. Кроме того, ваш предложенный SQL не будет работать: использование B в операторе WHERE делает его внутренним соединением (то же самое происходит с вашими критериями, которые имеют дополнительную нагрузку на отдельный трансформатор на стороне клиента) –

+0

Это не приведет к внутреннему соединению поскольку я указал его как внешнее соединение. Вы можете проверить это через профилировщик. По сути, есть два подхода к проблеме: один запрос (который является предложенным решением) или два запроса (либо через коррелированный подзапрос, либо два полностью отдельных запроса). У обоих есть плюсы и минусы. Вы отметили основной недостаток одного запроса. В случае коррелированного подзапроса проверьте это: http://stackoverflow.com/questions/141278/subqueries-vs-joins –

+0

Фильтр вне условия JOIN превращает его в внутреннее соединение (* попробуйте *) , Вы можете сделать это частью условия соединения с помощью HQL (используя предложение WITH), но не с критериями. Также: подзапрос в этом случае НЕ коррелирован (я не использую элементы внешнего запроса внутри внутреннего), поэтому он эквивалентен соединению, по крайней мере, с приличными двигателями БД. –

Смежные вопросы