2010-11-09 4 views
2

Je crée une application de sondage dans laquelle j'ai un sondage qui contient une collection de pages. Chaque page aura une collection de questions et chaque question aura une collection d'options de réponse. Ma structure de classe ressemble à:Récupérer efficacement un objet avec des collections imbriquées à l'aide de NHibernate

public class Survey : Entity { 
    public IList<Page> Pages { get; set; } 
} 

public class Page : Entity { 
    public IList<Question> Questions { get;set; } 
} 

public class Question : Entity { 
    public IList<Option> Options { get; set; } 
} 

public class Option : Entity {} 

La mise en correspondance pour chaque classe est:

<!-- mapping for ID and other properties excluded --> 
<class name="Survey"> 
    <bag name="Pages" generic="true" inverse="true"> 
     <key column="SurveyId" /> 
     <one-to-many class="Page" /> 
    </bag> 
    <bag name="Questions" access="none"> 
     <key column="SurveyId" /> 
     <one-to-many class="Question" /> 
    </bag> 
</class> 

<class name="Page"> 
    <many-to-one name="Survey" column="SurveyId" /> 
    <bag name="Questions" generic="true" inverse="true"> 
     <key column="PageId" /> 
     <one-to-many class="Question" /> 
    </bag> 
</class> 

<class name="Question"> 
    <many-to-one name="Page" column="PageId" /> 
    <many-to-one name="Survey" column="SurveyId" /> 
    <bag name="Options" generic="true" inverse="true"> 
     <key column="QuestionId" /> 
     <one-to-many class="Option" /> 
    </bag> 
</class> 

<class name="AnswerOption"> 
    <many-to-one name="Question" column="QuestionId" /> 
</class> 

Je dois montrer toutes les questions sur une page si je commence avec l'objet de l'enquête et la boucle à travers les pages, articles et options. Cela provoque NHibernate à exécuter de nombreuses requêtes et je voudrais optimiser cela. Comment puis-je obtenir l'objet de sondage avec les collections imbriquées de la meilleure façon possible sans exécuter trop de requêtes?

C'est le code que j'ai en ce moment, mais il exécute encore beaucoup de requêtes:

var result = Session.CreateMultiQuery() 
    .Add(Session.CreateQuery("from Survey s inner join fetch s.Pages where s.Id = :id")) 
    .Add(Session.CreateQuery("from Survey s inner join fetch s.Question where s.Id = :id")) 
    .SetInt32("id", id) 
    .List(); 

IList list = (IList)result[0]; 
return list[0] as Survey; 

J'ai aussi essayé requêtes futures mais ils ne contribuent pas à réduire le nombre de requêtes.

Des idées?

Répondre

1

Vous pouvez essayer d'ajouter

lazy="false" fetch="join" 

à vos déclarations de sac. De cette façon, vous pouvez être sûr que le sac sera récupéré en utilisant une requête.

+0

J'avais ajouté lazy = "false" à mon mapping sans chercher et cela n'a pas aidé. L'ajout de fetch a réduit les requêtes. Cependant, je ne veux pas modifier le mapping car je chercherais juste une question ou simplement l'objet de l'enquête et je n'aurais pas besoin des collections. Cela peut-il être fait en utilisant l'API HQL ou Criteria? –

0

Une solution que j'utilise est l'inversion de la relation entre les entités. Avec cela, vous pouvez être sûr quand un enregistrement de Survey est chargé, aucun enregistrement de Page est chargé sauf si vous appelez la méthode LoadAll. LoadAll est une méthode dans chaque classe qui effectue une recherche dans tous les enregistrements liés. Considérez le code suivant:

public class Survey : Entity { 
Survey[] LoadAll { 
string hql = "from Page page where page.SurveyID =" + this.ID; 
//.... 
} 
} 

public class Page : Entity { 
    public Survey { get;set; } 
} 

public class Question : Entity { 
    public Page Page { get; set; } 
} 

public class Option : Entity { 
    public Question Question {set; get;} 
} 
+0

Correct. J'ai une méthode LoadAll mais pas dans la classe Survey. Ce que je veux savoir, c'est s'il y a un moyen d'écrire HQL (ou Critères) pour charger l'enquête, toutes les pages, toutes les questions dans chaque page et toutes les options de réponse pour chaque question de la manière la plus efficace. –

+0

Si j'utilisais SQL, j'utiliserais 'select distinct' sur' Options'. –

1

Si je comprends bien, vous pouvez y parvenir avec la HQL suivante (bien sûr la même idée peut être utilisé avec ICriteria)

from Survey s 
    inner join fetch s.Pages p 
    inner join fetch p.Questions q 
    inner join fetch q.Options 
where s.Id = :id 

Si vous voulez aussi chercher survey.Questions alors la meilleure option est d'aller chercher celles-ci dans un qyery séparé et d'utiliser Futures pour éviter les produits cartésiens.

De plus, si je me souviens bien, les requêtes HQL ignorent les fetchs définis dans les mappages. Si la collection est mappée avec lazy="false" fetch="join", elle est récupérée lorsqu'elle est utilisée, mais pas lors de l'utilisation de HQL.

+0

Je vais essayer d'utiliser les requêtes futures et publier une mise à jour. –

Questions connexes