2010-06-28 3 views
5

ich NHibernate bin mit, und ich habe die beiden folgenden Klassen, die meine Datenbank-Schema zuzuordnen:Wie NHibernate verwenden, um Elemente mit Kriterien auf einer Liste abrufen

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; } 
} 

würde Ich mag alle Einträge bekommen von Tabelle A, die alle Elemente ihrer MyList-Eigenschaft mit einem Datum weniger als einem bestimmten Wert enthalten.

Wie kann ich das mit einer eleganten NHibernate-Syntax tun?

Antwort

1

schulde ich Ihnen den "elegant" Teil ... :-)

Dies ist eine mögliche HQL. Beachten Sie, dass Ihre Bedingung invertiert: Anstatt nach "A, die alle Elemente ihrer MyList-Eigenschaft mit einem Datum weniger als einen gegebenen Wert haben" suche ich nach "A, die keine Elemente ihrer MyList haben Eigenschaft mit einem Datum größer als oder gleich ein gegebener Wert ".

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

Verbrauch:

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

Beachten Sie, dass, wenn Sie eine bidirektionale Beziehung zwischen A und B (durch Zugabe einer A Eigenschaft) erklären, die Abfrage ist viel einfacher:

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

Update: Hier ist, wie es geht mit Kriterien:

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

guter Punkt für die bidirektionale Beziehung: Ich werde eine Eigenschaft des Typs A in meiner Klasse B hinzufügen. HQL ist großartig, aber ich frage mich, ob wir nicht mit DetachedCriteria() und Projections.Max() Methoden sth – PierrOz

+0

OK, Ich habe die Option Kriterien hinzugefügt (es wäre nicht schwer, das zu akzeptieren und sie so zu ändern, dass sie 'Subqueries.PropertyIn' verwendet und die Unterabfrage invertiert, um eine Projektion zu verwenden). Criteria ist jedoch für dynamisch erstellte Abfragen (Suche) nützlicher. Schau dir das ganze Rauschen an, verglichen mit dem HQL. –

+1

wunderbar vielen dank !! – PierrOz

0

verwenden

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

Diese Abfrage gibt NICHT zurück, was PierrOz erwartet. –

0

wenn Ihre Klasse B sieht ungefähr so ​​aus (wo die MyList Eigenschaft von A für dieses FK sieht)

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

dann denke ich, die Sie suchen (HQL)

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

Die derzeit akzeptierte Antwort basiert auf einer korrelierten Sub-Abfrage, die als Faustregel nur "schlechtes SQL" ist.

Es ist viel besser, dies einfach unter Verwendung von mengenbasierter Semantik auszudrücken, anstatt einen funktionelleren Ansatz zu wählen.

Im Wesentlichen möchten Sie Ihren SQL wie folgt aussehen:

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

Dieser lautet wie „Ich habe eine Menge von Spalten von A wie im Zusammenhang mit einer Reihe von B, wobei B Datum weniger wollen als ein Wert“. Dies kann mit dem ICriteria API erreicht werden:

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>(); 

Ein Teil der Trick wird mit NHibernate eingebauten in DistinctRootEntityResultTransformer: Da der linke äußere Verknüpfung mehrere Instanzen von A pro B zurückkehren konnten, wollen wir unsere ICriteria nur kehrt die verschiedene Instanzen (vorausgesetzt, wir kümmern uns nicht um die Bestellung oder was auch immer).

+0

Ich schlage vor, dass Sie Ihre "Faustregeln" mit einigen harten Fakten unterstützen. Außerdem würde Ihr vorgeschlagenes SQL nicht funktionieren: Verwendung von B in der WHERE-Anweisung macht es zu einem inneren Join (das gleiche passiert mit Ihren Kriterien, die die zusätzliche Last des clientseitigen distinkten Transformers haben) –

+0

Es würde KEINEN inneren Join verursachen wie ich es als Outer-Join angegeben habe. Sie können dies über einen Profiler überprüfen. Im Wesentlichen gibt es zwei Ansätze für das Problem: eine einzelne Abfrage (was die Lösung ist, die ich vorgeschlagen habe) oder zwei Abfragen (entweder über eine korrelierte Unterabfrage oder zwei völlig getrennte Abfragen). Beide haben Vor-/Nachteile. Sie haben den Hauptnachteil einer einzelnen Abfrage erwähnt. Im Falle der korrelierten Sub-Abfrage, überprüfen Sie dies: http://StackOverflow.com/Questions/141278/Subqueries-VS-Joins –

+0

Ein Filter außerhalb der JOIN-Bedingung verwandelt es in eine innere Verbindung (* versuch es *) . Sie können es mit HQL (mit der WITH-Klausel) in die Join-Bedingung aufnehmen, aber nicht mit Criteria. Außerdem: die Unterabfrage ist in diesem Fall NICHT korreliert (ich verwende keine Elemente der äußeren Abfrage innerhalb der inneren Abfrage), also ist es äquivalent zu einem Join, zumindest mit anständigen DB-Engines. –

Verwandte Themen