如何使用 NHibernate 检索具有列表条件的元素

Posted

技术标签:

【中文标题】如何使用 NHibernate 检索具有列表条件的元素【英文标题】:How to use NHibernate to retrieve elements with a criteria on a List 【发布时间】:2011-03-09 06:16:59 【问题描述】:

我正在使用 NHibernate,并且我有以下两个类来映射我的数据库架构:

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; 

我想获取表 A 的所有条目,这些条目的 MyList 属性的所有元素的 Date 都小于给定值。

如何使用优雅的 NHibernate 语法做到这一点?

【问题讨论】:

【参考方案1】:

当前接受的答案依赖于相关的子查询,根据经验,这只是“糟糕的 SQL”。

使用基于集合的语义而不是更实用的方法来简单地表达这一点要好得多。

基本上你希望你的 SQL 看起来像这样:

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

这读作“我希望 A 中的一组列与一组 B 相关,其中 B 的日期小于某个值”。这可以使用 ICriteria API 来实现:

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

部分技巧是使用 NHibernate 的内置 DistinctRootEntityResultTransformer:因为左外连接可以为每个 B 返回多个 A 实例,我们希望我们的 ICriteria 只返回不同的实例(假设我们不关心排序或其他否则)。

【讨论】:

我建议你用一些确凿的事实来支持你的“经验法则”。另外,您建议的 SQL 不起作用:在 WHERE 语句中使用 B 使其成为内部连接(您的 Criteria 也会发生同样的情况,它具有客户端不同转换器的额外负担) 它不会导致内部联接,因为我已将其指定为外部联接。您可以通过探查器验证这一点。基本上有两种方法可以解决这个问题:一个查询(这是我建议的解决方案)或两个查询(通过相关的子查询或两个完全独立的查询)。两者都有优点/缺点。您已经提到了单个查询的主要缺点。在相关子查询的情况下,请查看:***.com/questions/141278/subqueries-vs-joins JOIN 条件外的过滤器将其变为内部连接(尝试)。您可以使用 HQL(使用 WITH 子句)使其成为连接条件的一部分,但不能使用 Criteria。另外:在这种情况下,子查询是不相关的(我没有在内部查询中使用外部查询的元素),所以它相当于一个连接,至少在体面的数据库引擎中是这样。 好的。您对 JOIN 的看法是正确的。直到现在我才真正看过实际的查询计划(我应该看过)。虽然有趣的是,两种查询(连接或子查询)的查询计划最终都具有完全相同的查询计划:既有少量数据,也有大量数据。【参考方案2】:

我欠你“优雅”的部分... :-)

这是一个可能的 HQL。请注意,这颠倒了您的条件:我不是在寻找 “其 MyList 属性的所有元素的 Date 小于给定值的 A”,而是寻找“A that 不MyList 属性的任何 元素的日期大于或等于给定值”。

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

用法:

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

请注意,如果您声明AB 之间的双向关系(通过添加A 属性),则查询要简单得多:

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

更新:以下是使用 Criteria 的方法:

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

【讨论】:

双向关系的优点:我将在我的 B 类中添加一个 A 类型的属性。HQL 很棒,但我想知道我们是否无法通过 DetachedCriteria() 和 Projections 获得某事。 Max() 方法 好的,我添加了 Criteria 选项(将其更改为使用 Subqueries.PropertyIn 并反转子查询以使用投影并不难)。但是,Criteria 对于动态构造的查询(搜索)更有用。看看所有的噪音,与 HQL 相比。【参考方案3】:

如果您的 B 类看起来像这样(A 的 MyList 属性在其中查找此 FK)

public class B

    public virtual int Id  get; set; 
    public virtual DateTime Date  get; set; 
    public virtual A FK_ToA  get; set; 

那么我认为您正在寻找(HQL)

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

【讨论】:

【参考方案4】:

使用这个

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

【讨论】:

该查询不会返回 PierrOz 的预期结果。

以上是关于如何使用 NHibernate 检索具有列表条件的元素的主要内容,如果未能解决你的问题,请参考以下文章

NHibernate:检索非空一对一关联类的条件表达式

nhibernate - sproutcore:如何只检索参考 ID 而不加载参考/关系?

使用 NHibernate 创建数据库视图

如何使用 DISTINCT 在 NHibernate SQL 查询中进行分页

如何最好地在 NHibernate 中检索和更新这些对象?

如何让 NHibernate ISession 缓存主键未检索到的实体