2010-11-09 73 views
2

我正在创建一个调查应用程序,其中有一个调查,其中包含一组页面。每个页面都有一组问题,每个问题都会有一组答案选项。我的阶级结构是这样的:使用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 {} 

每个类的映射是:

<!-- 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> 

我需要显示通过页面的页面,所以我开始与调查对象,并循环上的所有问题,项目和选项。这导致NHibernate执行许多查询,我想优化这个。如何在不执行太多查询的情况下以最好的方式获得嵌套集合的调查对象?

这是我此刻的代码,但它仍然执行许多查询:

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; 

我也曾尝试以后的查询,但他们不帮助减少查询的数量。

任何想法?

回答

1

你可以尝试添加

lazy="false" fetch="join" 

到你的包声明。这样你就可以确定袋子会被一个查询取回。

+0

我已经将lazy =“false”添加到我的映射中,但没有获取并且没有帮助。添加访存减少了查询。但是,我不想更改映射,因为我只会提取一个问题或调查对象,而不需要集合。这可以使用HQL或Criteria API来完成吗? – 2010-11-10 02:40:32

0

我使用的解决方案是反转实体之间的关系。有了这个,你可以确定何时加载了Survey的记录,除非你调用LoadAll方法,否则不会加载Page的记录。 LoadAll是每个类中搜索所有相关记录的方法。请考虑下面的代码:

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

正确。我有一个LoadAll方法,但不在Survey类中。我想知道的是,是否有办法以最有效的方式编写HQL(或标准)来加载调查,所有页面,每个页面中的所有问题以及每个问题的所有答案选项。 – 2010-11-10 08:24:50

+0

如果我使用SQL,我会在'Options'上使用'select distinct'。 – 2010-11-10 08:47:55

1

如果我理解正确的话,你可以用下面的HQL(当然同样的想法可以用的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 

做到这一点。如果你也想获取survey.Questions那么最好的选择就是在单独的qyery中使用并使用期货来避免笛卡尔积。另外,如果我没有记错的话,HQL查询会忽略在映射中定义的提取。如果集合映射为lazy="false" fetch="join",那么它在使用ICriteria时获取,但在使用HQL时不会。

+0

我会尝试使用未来的查询并发布更新。 – 2010-11-12 07:53:59