使用NHibernate有效地使用嵌套集合检索对象

时间:2010-11-09 17:30:01

标签: 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;

我也尝试了Future查询,但它们无助于减少查询次数。

有什么想法吗?

3 个答案:

答案 0 :(得分:2)

如果我理解正确,您可以使用以下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中获取它们并使用Futures来避免笛卡尔积。

另外,如果我没记错,HQL查询会忽略映射中定义的fetch。如果集合与lazy="false" fetch="join"映射,那么在使用ICriteria时会获取它,但在使用HQL时则不会。

答案 1 :(得分:1)

您可以尝试添加

lazy="false" fetch="join"

到您的行李声明。这样你就可以确定使用一个查询来获取包。

答案 2 :(得分:0)

我正在使用的解决方案是扭转实体之间的关系。有了这个,您可以确定加载Survey的记录时,除非您调用Page方法,否则不会加载LoadAll的记录。 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;}
}