【问题标题】:NHibernate QueryOver: How to join unrelated entities?NHibernate QueryOver:如何加入不相关的实体?
【发布时间】:2011-06-16 20:32:03
【问题描述】:

我有以下查询可以得到我想要的结果:

int associatedId = 123;

MyObject alias = null;

var subQuery = QueryOver.Of<DatabaseView>()
    .Where(view => view.AssociatedId == associatedId)
    .And(view => view.ObjectId == alias.ObjectId)
    .Select(view => view.ObjectId);

var results = session.QueryOver<MyObject>(() => alias)
    .WithSubquery.WhereExists(subQuery)
    .List();

DatabaseView 已被映射为实际的 NHibernate 实体(因此我可以将其与 QueryOver 一起使用),但它与 HBM 映射中的 MyObject 无关。

此查询使用SELECT ... FROM MyObject WHERE EXISTS (subquery for DatabaseView here) 返回IList&lt;MyObject&gt;。如何重写它以返回相同的数据但使用 JOIN 而不是子查询?

【问题讨论】:

    标签: join nhibernate queryover


    【解决方案1】:

    在 NHibernate 5.1+ 中,QueryOver/Criteria 可以通过Entity Join

    int associatedId = 123;
    
    MyObject alias = null;
    DatabaseView viewAlias = null;
    
    var results = session.QueryOver<MyObject>(() => alias)
        .JoinEntityAlias(() => viewAlias, () => viewAlias.ObjectId == alias.ObjectId && viewAlias.AssociatedId == associatedId)
        .List();
    

    标准示例:

    int associatedId = 123;
    var results = session.CreateCriteria<MyObject>("alias")
        .CreateEntityAlias(
                "viewAlias",
                Restrictions.EqProperty("viewAlias.ObjectId", "alias.ObjectId")
                && Restrictions.Eq("viewAlias.AssociationId", associatedId),
                JoinType.InnerJoin,
                typeof(DatabaseView).FullName)
        .List();
    

    【讨论】:

    • 我喜欢这个,因为我不需要添加 Remotion.Linq。谢谢
    【解决方案2】:

    您可以在 NHibernate 3+ 中使用 Linq 加入不相关的实体

    有趣的是,您使用了join 查询表达式元素:

    from type1 in Repository.Query<MyType1>() 
    join type2 in Repository.Query<MyType2>() 
    on type1.Id equals type2.Id
    

    注意:Repository.Query 只是从会话中返回一个 IQueryable 查询

    我希望 QueryOver 有一个解决方案,因为我并不总是想在我的域中建模双向关系,但它们对于查询仍然有用。

    此外,您可以使用 Criteria API 映射 Access="noop" 2 路关系,而无需放入您的 POCO 类:

    http://ayende.com/blog/4054/nhibernate-query-only-properties

    【讨论】:

    • 这实际上在加入两个IQueryable 实例时有效。我很惊讶这适用于 LINQ (Query&lt;&gt;) 实现,但不适用于 QueryOver&lt;&gt;
    【解决方案3】:

    我意识到这个问题已经有 5 年历史了,“正确”的答案绝对是你不能用 QueryOver 做到这一点,正如其他答案所表明的那样。但是,如果您真的需要此功能(就像我一样),我找到了一个不错的解决方法。

    解决方案是在映射 XML 中使用带有本机 SQL 的“加载器查询”来生成相关集合(请参阅http://nhibernate.info/doc/nhibernate-reference/querysql.html#querysql-load)。在 OP 的具体示例中,您将继续按照建议将您的 DatabaseView 映射为实体,然后在映射中编写以下内容:

    <class name="MyObject"...>
        ...
        <set name="MyViews" inverse="true">
            <key column="ObjectId" foreign-key="none"/>
            <one-to-many class="MyObject"/>
            <loader query-ref="myObjectViewsLoadQuery"/>
        </set>
    </class>
    

    然后我们只需要在原始 SQL 中定义我们命名的 myObjectViewsLoadQuery 来向 NH 解释如何将两者连接起来:

    <sql-query name="myObjectViewsLoadQuery">
        <load-collection alias="view" role="MyObject.MyViews"/>
        SELECT view.*
        FROM DatabaseView view
        WHERE view.ObjectId = :id
    </sql-query>
    

    我们现在可以假装在我们的查询中有一个名为MyViews 的“真实”集合,将MyObjectDatabaseView 关联起来:

    MyObject alias = null;
    DatabaseView view = null;
    var results = session.QueryOver<MyObject>(() => alias)
         .JoinAlias( () => alias.MyViews, () => view )
         //.Where( () => view.Property == "myValue" ) // optionally, restrict the view etc.
         .List();
    

    当然,如果您只关心“优雅”的查询,这会很麻烦。但是,如果您使用 QueryOver 的原因是您希望能够接受任意输入表达式来过滤您的 DatabaseView,或者各种类似的活动,那么这非常有效。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多