19

我有一个 3 级实体层次结构:Customer-Order-Line,我想使用 ISession.Get(id) 为给定客户完整检索它。我有以下 XML 片段:

客户.hbm.xml:

<bag name="Orders" cascade="all-delete-orphan" inverse="false" fetch="join">
  <key column="CustomerID" />
  <one-to-many class="Order" />
</bag>

order.hbm.xml:

<bag name="Lines" cascade="all-delete-orphan" inverse="false" fetch="join">
  <key column="OrderID" />
  <one-to-many class="Line" />
</bag>

我使用了 fetch="join" 属性来表示我想为每个父级获取子实体,这已经构建了正确的 SQL:

SELECT 
    customer0_.ID AS ID8_2_, 
    customer0_.Name AS Name8_2_, 
    orders1_.CustomerID AS CustomerID__4_, 
    orders1_.ID AS ID4_, 
    orders1_.ID AS ID9_0_, 
    orders1_.PostalAddress AS PostalAd2_9_0_, 
    orders1_.OrderDate AS OrderDate9_0_, 
    lines2_.OrderID AS OrderID__5_, 
    lines2_.ID AS ID5_, 
    lines2_.ID AS ID10_1_, 
    lines2_.[LineNo] AS column2_10_1_, 
    lines2_.Quantity AS Quantity10_1_, 
    lines2_.ProductID AS ProductID10_1_ 

FROM Customer customer0_ 

LEFT JOIN [Order] orders1_ 
       ON customer0_.ID=orders1_.CustomerID 

LEFT JOIN Line lines2_ 
       ON orders1_.ID=lines2_.OrderID 

WHERE customer0_.ID=1

到目前为止,这看起来不错 - SQL 返回了正确的记录集(只有一个不同的 orderid),但是当我运行测试以确认 Orders 和 Lines 的正确实体数量(来自 NH)时,我得到了错误的结果

应该(从我的测试数据中)得到 1xOrder 和 4xLine,但是,我得到了 4xOrder 和 4xLine。NH 似乎没有识别结果集中的“重复”订单信息组,也没有正确“重用”订单实体。

我正在使用所有整数 ID (PK),并且我尝试使用此 ID 实现 T 的 IComparable 和 T 的 IEquatable,希望 NH 会看到这些实体的相等性。我还尝试覆盖 Equals 和 GetHashCode 以使用 ID。这些“尝试”都没有成功。

NH 是否支持“多级提取”操作,如果支持,是否需要 XML 设置(或其他机制)来支持它?


注意:我使用了 Sirocco 的解决方案,对我自己的代码进行了一些更改,最终解决了这个问题。对于所有集合,xml 需要从包更改为集合,并且实体本身已更改为实现 IComparable<>,这是建立唯一性的集合的要求。

public class BaseEntity : IComparable<BaseEntity>
{
    ...

    private Guid _internalID { get; set; }
    public virtual Guid ID { get; set; }

    public BaseEntity()
    {
        _internalID = Guid.NewGuid();
    }

    #region IComparable<BaseEntity> Members

    public int CompareTo( BaseEntity other )
    {
        if ( ID == Guid.Empty || other.ID == Guid.Empty )
            return _internalID.CompareTo( other._internalID );

        return ID.CompareTo( other.ID );
    }

    #endregion

    ...

 }

请注意 InternalID 字段的使用。这是新的(瞬态)实体所必需的,否则它们最初不会有 ID(我的模型在保存时提供了它们)。

4

5 回答 5

21

您将获得 4XOrder 和 4XLines ,因为使用线连接会使结果加倍。您可以在 ICriteria 上设置一个 Transformer,例如:

.SetResultTransformer(new DistinctRootEntityResultTransformer())
于 2008-12-15T12:45:18.833 回答
5

我刚刚阅读了Ayende 的博客文章,他使用了以下示例:

session.CreateCriteria(typeof(Post))
    .SetFetchMode("Comments", FetchMode.Eager)
    .List();

在条件查询中避免对特定查询进行延迟加载

也许这可以帮助你。

于 2008-12-02T08:48:35.120 回答
1

如果您需要将一对多保存为袋子,则可以发出 2 个查询,每个查询只有 1 级层次结构。例如这样的:

var temp = session.CreateCriteria( typeof( Order ) )
    .SetFetchMode( "Lines", NHibernate.FetchMode.Eager )
    .Add( Expression.Eq( "Customer.ID", id ) )
    .List();

var customer = session.CreateCriteria( typeof( Customer ) )
    .SetFetchMode( "Orders", NHibernate.FetchMode.Eager )
    .Add( Expression.Eq( "ID", id ) )
    .UniqueResult();

行在第一个查询中被加载到 NH 缓存中,因此在以后访问例如 customer.Orders[0].Lines[0] 时不需要延迟加载。

于 2011-11-24T23:54:21.247 回答
0

@Tigraine:您的查询仅返回带有评论的帖子。这会带来带有所有评论的所有帖子(2 个级别)。Ben 要求的是 Customer to Order To LineItem(3 级)。@Ben:据我所知,nHibernate 还不支持预先加载到 3 级。Hibernate 确实支持它。

于 2008-12-02T21:23:47.060 回答
0

我遇到了同样的问题。看到这个线程。我没有得到解决方案,而是来自 Fabio 的提示。使用 Set 而不是 bag。它奏效了。

所以我的建议是尝试使用 set。您不必使用 Iesi 集合使用 IDictonary 并且 NH 很高兴

public override IEnumerable<Baseline> GetAll()
{
     var baselines = Session.CreateQuery(@" from Baseline b
                                            left join fetch b.BaselineMilestones bm
                                            left join fetch bm.BaselineMilestonePrevious ")
                                            .SetResultTransformer(Transformers.DistinctRootEntity)
                                            .List<Baseline>();
     return baselines;
}
于 2009-08-14T21:08:16.317 回答