19

ISession.Get(id) を使用して、特定の顧客の全体を取得したい Customer-Order-Line という 3 レベルのエンティティ階層があります。次の 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 は正しいレコードのセット (1 つの異なる orderid のみ) を返しますが、テストを実行して Orders と Lines の (NH からの) エンティティの正しい数を確認すると、間違った結果が得られます。

(テスト データから) 1xOrder と 4xLineを取得する必要がありますが、4xOrder と 4xLine を取得しています。NH は、結果セット内の Order 情報の「繰り返し」グループを認識しておらず、Order エンティティを正しく「再利用」していないようです。

私はすべての整数 ID (PK) を使用しており、NH がこれらのエンティティの同等性を確認することを期待して、この ID を使用して T の IComparable と T の IEquatable を実装しようとしました。また、ID を使用するために Equals と GetHashCode をオーバーライドしようとしました。これらの「試み」はいずれも成功していません。

「複数レベルのフェッチ」は NH でサポートされている操作ですか? もしそうなら、それをサポートするために必要な XML 設定 (またはその他のメカニズム) はありますか?


NB: 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 がありません (私のモデルでは、保存時に ID が提供されます)。

4

5 に答える 5

21

行との結合により結果が2倍になるため、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

1 対多をバッグとして保持する必要がある場合は、それぞれ 1 レベルの階層のみを持つ 2 つのクエリを発行できます。たとえば、次のようなものです。

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

私も同じ問題を抱えていました。このスレッドを参照してください。解決策は得られませんでしたが、Fabioからヒントを得ました。バッグの代わりにセットを使用してください。そしてそれはうまくいった。

だから私の提案はセットを使用してみることです。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 に答える
0

@Tigraine: クエリはコメント付きの投稿のみを返します。これにより、すべての投稿とすべてのコメント (2 レベル) が表示されます。Ben が尋ねているのは、Customer to Order To LineItem (3 レベル) です。@Ben: 私の知る限り、nHibernate は 3 レベルまでの熱心な読み込みをまだサポートしていません。Hibernate はそれをサポートしています。

于 2008-12-02T21:23:47.060 に答える