优化NHibernate的SQL连接查询

时间:2009-09-02 23:07:26

标签: nhibernate join

采用相当简单的域模型订单,物料和货件,其中订单是根实体,货件是根实体。我想查找给定订单的所有货件。查询非常简单,但我看到了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>();

4 个答案:

答案 0 :(得分:1)

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

var shipment =来自session.Linq()中的货件,其中shipment.LineItem.Order == order 选择新{Id = shipment.Id,Date = shipment.Date,LineItemId = shipment.LineItem.Id,OrderId = shipment.LineItem.Order.Id};

答案 1 :(得分:1)

您可以为域模型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;

答案 2 :(得分:1)

您应该能够在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对象,只填充日期。

答案 3 :(得分:0)

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