23

子オブジェクトの と 1 対多の関係を持つ親オブジェクトがありISetます。子オブジェクトには一意の制約があります (PageNumおよびContentID- 親への外部キー)。

<set name="Pages" inverse="true" cascade="all-delete-orphan" access="field.camelcase-underscore">
    <key column="ContentId" />
    <one-to-many class="DeveloperFusion.Domain.Entities.ContentPage, DeveloperFusion.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</set>

私が遭遇した問題はContentPage、親コレクションから要素を削除し、同じトランザクション内で同じ一意のキーを持つ新しい要素を追加した場合です... NHibernateがに挿入を実行しようとするため、一意の制約違反が発生します消去。

NHibernate に最初に削除を強制する方法はありますか?

4

3 に答える 3

31

次のようにハードコーディングされているため、トランザクションで操作の順序を指定するオプションはありません (ドキュメントから)。

SQL ステートメントは、次の順序で発行されます。

  • すべてのエンティティの挿入。対応するオブジェクトが ISession.Save() を使用して保存されたのと同じ順序で
  • すべてのエンティティの更新
  • すべてのコレクションの削除
  • すべてのコレクション要素の削除、更新、および挿入
  • すべてのコレクションの挿入
  • すべてのエンティティの削除。対応するオブジェクトが ISession.Delete() を使用して削除されたのと同じ順序で

(例外は、ネイティブ ID 生成を使用するオブジェクトが保存時に挿入されることです。)

そのため、既存の識別子を持つ新しいエンティティを追加する理由についてお答えいただけますか? 識別子は、特定の「エンティティ」に対して一意であると想定されています。そのエンティティがなくなった場合、その識別子もなくなるはずです。

別のオプションは、削除/挿入の代わりにそのレコードを更新することです。これにより、ID が同じに保たれるため、(少なくともキーに対して) 一意の制約違反が発生せず、他のすべてのデータを変更して「新しい」レコードにすることができます。

編集:これは非主キー列の一意の制約に関する問題であるため、回答したときに質問に完全に注意を払っていなかったようです。

次の 2 つのソリューションから選択できると思います。

  1. Session.Flush()削除後に呼び出すと、その時点までのセッションに対するすべての変更が実行されます。その後、残りを続行できます (新しいオブジェクトを挿入します)。これはトランザクション内でも機能するため、原子性について心配する必要はありません。
  2. ReplacePage既存のエンティティを新しいデータで更新する関数を作成しますが、主キーと一意の列は同じに保ちます。
于 2009-04-01T21:17:15.693 に答える
4

同じ問題が発生しています...一意の制約を含むテーブルにマップされたコレクションを持つエンティティがあります。

私が理解していないのは、Stuarts の回答によると、コレクションの挿入の前にコレクションの削除が発生する必要があるということですか? NHibernate のソースコードを調べてみると、メソッドに次CollectionUpdateActionのコードを含むクラスが見つかりました。Execute

persister.DeleteRows(collection, id, session);
persister.UpdateRows(collection, id, session);
persister.InsertRows(collection, id, session);

次に、挿入の前に削除が実行されると思いますが、明らかにそうではありません。このシナリオでは CollectionUpdateAction は使用されませんか? CollectionUpdateAction はいつ使用されますか?

答え:

私はこれを次のように回避しました:

  • 私のマッピングでは、cascade オプションを「all-delete-orphan」ではなく「delete-orphan」に設定しました
  • データベースへのアクセスはすべてリポジトリ経由で行われます。リポジトリの save メソッドには、エンティティ用に次のコードがあります。

    public void Save( Order orderObj )
    {
        // Only starts a transaction when there is no transaction
        // associated yet with the session
       With.Transaction(session, delegate()
       {
           session.SaveOrUpdate (orderObj);
           session.Flush();
    
           foreach( OrderLine line in orderObj.Lines )
           {
                session.SaveOrUpdate (line);
           }
        };
     }
    

そこで、orderObj を保存します。カスケードが delete-orphan に設定されているため、削除されるオブジェクトはデータベースから削除されます。

を呼び出した後SaveOrUpdate、データベースへの変更を確実にフラッシュする必要があります。

delete-orphan カスケード設定により、OrderLine が挿入または更新されないようにするため、コレクションをループして、OrderLine ごとに「SaveOrUpdate」を呼び出す必要があります。これにより、新しい OrderLines が確実に挿入され、変更されたものが更新されます。変更されていない OrderLine に対しては何も実行されません。

これは理想的な解決策ではありませんが (見苦しいハックです)、ある程度は機能し、リポジトリの背後で抽象化されているため、これが今のところこの問題に対処する方法です...

于 2009-05-15T09:48:55.170 に答える
0

子の主キー(ContentPage.Id)を削除し、複合キーを主キー(つまり、PageNumとContentID)にすると、機能する可能性があると思います。これは、休止状態で発生する同じ問題に対して提供されるソリューションです。

于 2010-11-03T13:35:15.880 に答える