NHibernate 多个级别的急切获取

发布于 2024-07-09 08:25:24 字数 2450 浏览 15 评论 0原文

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

customer.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),但是当我运行测试来确认订单和行的实体数量(来自 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(我的模型在保存时提供了它们)。

I have a 3-leveled hierarchy of entities: Customer-Order-Line, which I would like to retrieve in entirety for a given customer, using ISession.Get(id). I have the following XML fragments:

customer.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>

I have used the fetch="join" attribute to indicate that I want to fetch the child entities for each parent, and this has constructed the correct 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

So far, this looks good - SQL returns the correct set of records (with only one distinct orderid), but when I run a test to confirm the correct number of entities (from NH) for Orders and Lines, I get the wrong results

I should be getting (from my test data), 1xOrder and 4xLine, however, I am getting 4xOrder and 4xLine. It appears that NH is not recognising the 'repeating' group of Order information in the result set, nor correctly 'reusing' the Order entity.

I am using all integer IDs (PKs), and I've tried implementing IComparable of T and IEquatable of T using this ID, in the hope that NH will see the equality of these entities. I've also tried overridding Equals and GetHashCode to use the ID. Neither of these 'attempts' have succeeded.

Is "multiple leveled fetch" a supported operation for NH, and if so, is there an XML setting required (or some other mechanism) to support it?


NB: I used sirocco's solution with a few changes to my own code to finally solve this one. the xml needs to be changed from bag to set, for all collections, and the entitities themselves were changed to implement IComparable<>, which is a requirement of a set for uniqueness to be established.

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

    ...

 }

Note the use of an InternalID field. This is required for new (transient) entities, other wise they won't have an ID initially (my model has them supplied when saved).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

掩饰不了的爱 2024-07-16 08:25:24

您将得到 4XOrder 和 4XLines,因为与 line 的连接使结果加倍。 您可以在 ICriteria 上设置 Transformer,如下所示:

.SetResultTransformer(new DistinctRootEntityResultTransformer())

You're getting 4XOrder and 4XLines because the join with lines doubles the results . You can set a Transformer on the ICriteria like :

.SetResultTransformer(new DistinctRootEntityResultTransformer())
离旧人 2024-07-16 08:25:24

我刚刚阅读了 Ayende 的博客文章< /a> 他使用了以下示例:

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

在标准查询中避免在一个特定查询上延迟加载

也许这可以帮助您。

I just read Ayende's Blogpost where he used the following Example:

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

In a Criteria Query to avoid Lazy Loading on one particular Query

Maybe that can help you.

年少掌心 2024-07-16 08:25:24

如果您需要将一对多作为包保存,那么您可以发出 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] 时,它们不需要延迟加载。

If you need to keep your one-to-manys as bags, then you can issue 2 queries, each with only 1 level of hierarchy. eg something like this:

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

Lines get loaded into the NH cache in the first query, so they won't need lazy loading when later accessing eg customer.Orders[0].Lines[0].

帅哥哥的热头脑 2024-07-16 08:25:24

@Tigraine:您的查询仅返回带评论的帖子。 这将带来包含所有评论的所有帖子(2 级)。 Ben 要求的是 Customer to Order To LineItem(3 级)。
@Ben:据我所知,nHibernate 还不支持急切加载最多 3 级。 Hibernate 确实支持它。

@Tigraine: your query only returns Post with Comments. This brings All posts with all Comments (2 levels). What Ben asking is Customer to Order To LineItem (3 level).
@Ben: to my knowledge nHibernate doesn't support eager loading upto 3 level yet. Hibernate does support it thou.

↘人皮目录ツ 2024-07-16 08:25:24

我也遇到了同样的问题。 请参阅此帖子。 我没有得到解决方案,但得到了法比奥的提示。 使用 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;
}

I was having the same problem. See this thread. I didn't get a solution but a hint from Fabio. Use Set instead of bag. And it worked.

So my suggestion is try to use set. You don't have to use Iesi collection use IDictonary and NH is happy

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文