【问题标题】:Optimize SQL join query for NHibernate优化 NHibernate 的 SQL 连接查询
【发布时间】:2009-09-02 23:07:25
【问题描述】:

采用一个相当简单的域模型 Orders、items 和 shipping ,其中 Order 是根实体,而 shipping 是根实体。我想查找给定订单的所有货件。查询非常简单,但我看到 NHibernate 出现不良行为。

模型

public class Order
{
    public Order(){ Items = new List<LineItem>(); }
    public virtual int Id { get; private set; }
    public virtual DateTime Created { get; set; }
    public virtual IList<LineItem> Items { get; private set; }
}

public class LineItem
{
    public virtual int Id { get; private set; }
    public virtual int Quantity { get; set; }
    public virtual Order Order { get; set; }
}

public class Shipment
{
    public virtual int Id { get; private set; }
    public virtual DateTime Date { get; set; }
    public virtual LineItem LineItem { get; set; }
}

LINQ

在这个查询中使用 NHibernate.Linq:

var shipments = from shipment in session.Linq<Shipment>()
                where shipment.LineItem.Order == order
                select shipment;

以下 SQL 查询的结果:

SELECT this_.Id            as Id5_2_,
       this_.Date          as Date5_2_,
       this_.LineItem_id   as LineItem3_5_2_,
       lineitem1_.Id       as Id4_0_,
       lineitem1_.Quantity as Quantity4_0_,
       lineitem1_.Order_id as Order3_4_0_,
       order2_.Id          as Id3_1_,
       order2_.Created     as Created3_1_,
       order2_.IsClosed    as IsClosed3_1_
FROM   [Shipment] this_
       left outer join [LineItem] lineitem1_
         on this_.LineItem_id = lineitem1_.Id
       left outer join [Order] order2_
         on lineitem1_.Order_id = order2_.Id
WHERE  lineitem1_.Order_id = 1 /* @p0 */

生成的 Shipment 对象是正确的,但查询加载了太多数据,因为我只对发货日期感兴趣。订单和订单项数据会立即被丢弃并且永远不会使用。我已经尝试使用延迟加载以及我可以在网上找到的所有获取策略,但我无法让它简单地返回基本数据。

如何减少 SQL 查询中的噪音,使其仅加载货件数据和订单项的主键以支持延迟加载?更像是这样的:

SELECT this_.Id            as Id5_2_,
       this_.Date          as Date5_2_,
       this_.LineItem_id   as LineItem3_5_2_,
       lineitem1_.Id       as Id4_0_,
FROM   [Shipment] this_
       inner outer join [LineItem] lineitem1_
         on this_.LineItem_id = lineitem1_.Id
WHERE  lineitem1_.Order_id = 1 /* @p0 */

自定义 SQL 查询(更新)

使用如下所示的自定义 SQL 查询可以获得所需的性能和正确的行为。然而,它有点违背了 ORM 的目的。为什么 NHibernate 不能生成这么简单的查询?

Session
    .CreateSQLQuery(
            @"SELECT SH.*, LI.Id FROM Shipment SH
              INNER JOIN LineItem LI ON LI.Id = SH.LineItem_id
              WHERE LI.Order_id = ?" )
    .SetInt32( 0, order.Id )
    .List<Shipment>();

【问题讨论】:

  • 您可以发布您的 HBM 文件吗?

标签: nhibernate join


【解决方案1】:

如果您只对日期感兴趣,而不是用“select shipping;”结束您的 LINQ 语句,您可以用“select shipping.Date;”结束它这样您就不会返回完整的对象层次结构。如果你想要一些额外的细节,你可以创建一个匿名类型?

var shipping = from shipping in session.Linq() where shipping.LineItem.Order == order 选择新的 {Id = shipping.Id, Date = shipping.Date, LineItemId = shipping.LineItem.Id, OrderId = shipping.LineItem.Order.Id};

【讨论】:

  • 哦,这几乎可以工作。使用您的想法确实会产生更清晰的查询(尽管它仍然有比所需更多的连接)。但是 - 我确实需要真正的 Shipment 对象,因为实际模型有点复杂。
【解决方案2】:

您可以向域模型 Order 类添加多对多关系。如果您使用的是 nhibernate 映射 xml 文件,则可以通过将以下内容添加到 Order 映射来实现。

<bag name="Shipments" table="LineItem" lazy="true">
    <key column="id"/>
    <many-to-many class="Shipment" column="lineitem_id" />
</bag>

您必须将 Shipments 属性添加到您的 Orders 类(应该是 IList 类型。否则您可能需要非通用 IList 接口)。

然后您可以在 linq 中查询 Shipments 属性,这应该会产生更清晰的连接。

var shipments = from shipment in order.Shipments select shipment.Date;

【讨论】:

  • 这是一个显而易见的解决方案 - 并且由于域原因不起作用。订单不负责发货。发货是在与订单完全不同的流程和服务中发生的事件。
【解决方案3】:

您应该能够在 C# 中使用 lamda 表达式和新的对象初始化器

我已经有一段时间没有这样做了,因为我回到了旧学校 2.0。

我知道在 linq 中你可以做到这一点,我不确定 Nhibernate linq 是否支持这一点。

var shipments = from shipment in session.Linq<Shipment>()
                                where shipment.LineItem.Order == order
                                select(x => new Shipment { Date = x.Date } );

看看这个link here,它是在c#中,但在一个java站点上。这应该希望只执行一个语句,该语句消失并选择订单等于指定订单的日期。但它会返回一个 Shipment 对象,其中仅填充日期。

【讨论】:

  • 对于其他所有 ORM 中如此简单的事情来说,这似乎需要做很多工作。为什么我不能等到 NHibernate 生成更好、更简单的查询?
  • 我相信这是可能的,但我不完全确定如何做到这一点。以下三种方法是我开始尝试实现这一目标的地方。 ISession.CreateCriteria ISession.CreateQuery ISession.CreateSqlQuery 这是我能做的最好的了,希望对你有帮助。
【解决方案4】:

基于进一步的研究和此处的帖子。答案是你不能 :(

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-24
    • 2018-12-27
    • 1970-01-01
    • 2012-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多