2009-08-07 6 views
5

ho lottato con questo per un po ', e non riesco a capirlo ...Parzialmente popolano Collezione Bambino con NHibernate

Ho una classe di BlogPost, che ha una collezione di Comments e ognuno dei commenti ha un campo DatePosted.

Che cosa devo fare è di query per un BlogPost e tornare con una collezione carico parziale Comments, dire tutti i commenti postati riguardanti l'1 agosto 2009.

Ho questa query:

BlogPost post = session.CreateCriteria<BlogPost>() 
    .Add(Restrictions.Eq("Id", 1)) 
    .CreateAlias("Comments", "c") 
    .Add(Restrictions.Eq("c.DatePosted", new DateTime(2009, 8, 1))) 
    .UniqueResult<BlogPost>(); 

Quando eseguo questa query e controllo lo sql generato, esegue prima una query sulla tabella BlogPost, unendosi alla tabella Comment con la limitazione della data corretta in, quindi esegue una seconda query solo sulla tabella Comment che restituisce tutto.

Il risultato è la raccolta Comments della classe BlogPost completamente riempita!

Cosa sto sbagliando?

Ho campioni di codice se qualcuno ha bisogno di maggiori informazioni ...!

risposta

0

In realtà non stai facendo nulla di sbagliato: l'ibernazione non funziona in questo modo.

Se si passa da BlogPost ai commenti, Ibernazione popolerà i commenti in base alla mappatura dell'associazione che è stata specificata, non alla query utilizzata per recuperare il BlogPost. Presumibilmente la tua mappatura sta semplicemente facendo un join su una colonna chiave. Puoi utilizzare filter to get the effect che stai cercando. Ma penso che recupererà ancora tutti i commenti e poi farà un post-filtro.

Più semplicemente, basta eseguire una query per ciò che si vuole:

List<Comments> comments = session.CreateCriteria<BlogPost>() 
      .Add(Restrictions.Eq("Id", 1)) 
      .CreateAlias("Comments", "c") 
      .Add(Restrictions.Eq("c.DatePosted", new DateTime(2009, 8, 1))) 
      .list(); 

questo sarà di fatto ritorno solo i commenti dalla data specificata. se ti fa sentire meglio, è quindi possibile impostare loro come questo:

post.setComments(comments); //having already retreived the post elsewhere 

Sono rimasto sorpreso anche da questo comportamento quando ho incontrato esso. Sembra un bug, ma mi è stato detto in base al design.

+0

NH non fa mai post-filtraggio. –

0

grazie per la risposta, credo di aver capito perché è stato progettato, ma avrei pensato che ci sarebbe stato un metodo integrato per abilitare questo, la soluzione funziona, ma sembra un po 'un trucco!

il mio problema è che la raccolta figlio è ENORME se non filtrata (l'esempio che ho dato di post e commenti è stato quello di proteggere i nomi degli innocenti!) E ora c'è modo in cui posso tirare indietro tutti i dati ogni volta .

ho eseguito Sql Profiler su questo e sta ancora recuperando tutti i dati. quando eseguo il seguente codice, la prima query fa quello che ci si aspetta, solo un post viene restituito, ma non appena viene eseguita la seconda query, due query vanno al database, il primo a recuperare i commenti filtrati (bingo!) , e poi un secondo per popolare la proprietà post.Comments con tutti i commenti, proprio quello che sto cercando di evitare!

 var post = session.CreateCriteria<BlogPost>() 
      .Add(Restrictions.Eq("Id", 1)) 
      .UniqueResult<BlogPost>(); 

     var comments = session.CreateCriteria<Comment>() 
      .Add(Restrictions.Eq("BlogPostId", 1)) 
      .Add(Restrictions.Eq("DatePosted", new DateTime(2009, 8, 1))) 
      .List<Comment>(); 

     post.Comments = comments; 

questo è molto strano, non è come se fossi enumerare oltre l'elenco post.Comments, quindi perché è popolato ?! ecco le mie classi e mappe:

public class BlogPostMap : ClassMap<BlogPost> 
{ 
    public BlogPostMap() 
    { 
     Id(b => b.Id); 
     Map(b => b.Title); 
     Map(b => b.Body); 
     HasMany(b => b.Comments).KeyColumnNames.Add("BlogPostId"); 
    } 
} 
public class CommentMap : ClassMap<Comment> 
{ 
    public CommentMap() 
    { 
     Id(c => c.Id); 
     Map(c => c.BlogPostId); 
     Map(c => c.Text); 
     Map(c => c.DatePosted); 
    } 
} 

public class BlogPost 
{ 
    public virtual int Id { get; set; } 
    public virtual string Title { get; set; } 
    public virtual string Body { get; set; } 
    public virtual IList<Comment> Comments { get; set; } 
} 
public class Comment 
{ 
    public virtual int Id { get; set; } 
    public virtual int BlogPostId { get; set; } 
    public virtual string Text { get; set; } 
    public virtual DateTime DatePosted { get; set; } 
} 

qualche idea?

+0

Utilizzare SetResultTransformer. Vedi la mia risposta. –

0

Sono d'accordo che sembra un trucco per popolare manualmente la raccolta.

È possibile utilizzare invece un caricatore personalizzato. Qualcosa del genere:

<query name="loadComments"> 
<return alias="comments" class="Comment"/> 
<load-collection alias="comments" role="Post.comments"/> 
from Comments c where c.Id = ? and c.DatePosted = SYSDATE 
</query> 

Inoltre, è possibile utilizzare sql-query se si desidera un maggiore controllo. Di tanto in tanto mi sono chinato a scrivere caricatori personalizzati quando non riuscivo a mettermi in letargo per generare la query che volevo. Ad ogni modo, non so perché non ci ho pensato in primo luogo.

2

C'è un trasformatore per questa, vedere the documentation.

Citazione:

Nota che le collezioni gattini tenuti dalle istanze di Cat restituiti dalle due precedenti interrogazioni non sono pre-filtrato secondo i criteri! Se si desidera che desideri recuperare solo i gattini che corrispondono ai criteri, , è necessario utilizzare SetResultTransformer(CriteriaUtil.AliasToEntityMap).

IList cats = 
sess.CreateCriteria(typeof(Cat)) 
    .CreateCriteria("Kittens", "kt") 
     .Add(Expression.Eq("Name", "F%")) 
    .SetResultTransformer(CriteriaUtil.AliasToEntityMap) 
    .List(); 

Si potrebbe anche usare filtri che vengono attivati ​​tramite session.EnableFilter(name).

C'è un similar question here.

0

Rendi pigro l'insieme di commenti, in modo che Hibernate non lo prelevi quando ricevi il BlogPost. Quindi utilizzare un filtro sulla raccolta dei commenti.

 
comments = session.CreateFilter(blogPost.Comments, ...).List();
Problemi correlati