0

FreightDateTime クラスを集約する委託クラスがあります。同時に、FreightDateTime クラスも GoodsItem クラスによって集約されます。同様に、FreightDateTime は、ここでは省略した他の多くのクラスに関連付けられています。

ConsignmentId 外部キー、GoodsItemId 外部キーなどを使用したデータベース テーブル FreightDateTime を回避するために、関連付けは多対多にする必要があると判断しました。このように、NHibernate は代わりに各関係 (ConsigmentFreightDateTimes、GoodsItemFreightDateTimes) の関連付けテーブルを生成します。これはより理にかなっています。

したがって、マッピング ファイルでは、関連付けは次のようになります。

<bag name="DateTimes" table="FreightDateTimes" lazy="false" cascade="all">
  <key column="ConsignmentId"/>
  <many-to-many class="Logistics.FreightDateTime, Logistics" column="DateTimeId" />
</bag>

カスケードを「すべて」に設定すると、次のようになります。

System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'DateTimeId', table 'LogiGate.dbo.FreightDateTimes'; column does not allow nulls. INSERT fails. 

カスケードを「なし」に設定すると、次の結果が得られます。

NHibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Logistics.FreightDateTime

どちらの場合も、子の FreightDateTime インスタンスは保存されていませんが、NHibernate が Consignment インスタンスを保存しようとしていることを意味します。最初のケースでは、外部キーはまだ 'null' であるため、結果のテーブルに挿入できません。2 番目のケースでは、NHibernate はインスタンスがまだ保存されていないことを認識しているため、例外をスローします。

問題は、明示的にそうするように指示せずに、最初にすべての子インスタンスを NHibernate に保存させる方法です。列 DateTimeId で null を許可するとうまくいくと思いますが、それは望ましくも可能でもないと思います。

4

1 に答える 1

1

反対側でも関連付けをマップしてみてください。ただし、その側では inverse="true" 属性を使用してください。そのため、FreightDateTime マッピング ファイルにバッグを作成して、積荷との関連付けを多対多にマッピングします。

また、ここで同様の質問に答えました: What is the correct way to define many-to-man Relationships in NHibernate to allow deletes but avoiding duplicate records

質問と私の回答を読むと、多対多の関連付けで何が起こっているのかを理解するのに役立ち、解決策のヒントが得られるかもしれません。最終的な推奨事項は、一種の最後の手段です。

上記の質問の答えは、他の人の別の問題を経験していることについての考えを与えることです.

解決策は、関連付けテーブルを明示的にマップすることです。テーブルが Person、Note で、関連付けテーブル (X テーブル) が PersonNote の場合、マッピングは次のようになります。

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="..."
                   namespace="...">

  <class name="Person" table="Person" lazy="true">

    <id name="PersonId">
      <generator class="native" />
    </id>
    <property name="FirstName" />
    .....
    <bag name="PersonNotes" generic="true" inverse="true" lazy="true" cascade="none">
        <key column="PersonId"/>
        <one-to-many class="PersonNote"/>
    </bag>

    <bag name="Notes" table="PersonNote" cascade="save-update">
      <key column="PersonId"></key>
      <many-to-many class="Note" column="NoteId"></many-to-many>
    </bag>

  </class>

</hibernate-mapping>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="..."
                   namespace="...">

  <class name="Note" table="Note" lazy="true">

    <id name="NoteId" unsaved-value="0">
      <generator class="native" />
    </id>
    <property name="Title" />
    ....
    <bag name="PersonNotes" inverse="true" lazy="true" cascade="all-delete-orphan">
        <key column="NoteId"/>
        <one-to-many class="PersonNote"/>
    </bag>

    <bag name="People" table="PersonNote" inverse="true" cascade="save-update" generic="true">
      <key column="NoteId"></key>
      <many-to-many class="Person" column="PersonId"></many-to-many>
    </bag>

  </class>

</hibernate-mapping>

上記のように、次のことができます。

  1. 個人を削除し、メモを削除せずに関連テーブルのエントリのみを削除します
  2. Note を削除し、Person エンティティを削除せずに関連付けテーブルのエントリのみを削除します
  3. Person.Notes コレクションにデータを入力して Person を保存することにより、Person 側のみから Cascades を使用して保存します。
  4. Note.People では inverse=true が必要なため、こちら側からカスケード保存を行う方法はありません。Note.People コレクションにデータを入力してから Note オブジェクトを保存すると、Note テーブルへの挿入と Person テーブルへの挿入が行われますが、関連テーブルへの挿入は行われません。これがNHibernateの仕組みだと思いますが、それを回避する方法はまだ見つかりません。
  5. Note エンティティの PersonNotes コレクションに新しい項目を追加することによってのみ、関連付けテーブルの保存エントリを明示的にカスケードできます。

上記はすべて単体テストでテストされています。上記を機能させるには、PersonNote クラス マッピング ファイルとクラスを作成する必要があります。

別のクラスに Organization などのメモを含める必要がある場合は、OrgnanisationNote というスキーマに別の関連付けテーブルを追加するだけで、Orgnanisation マッピング ファイルと Note マッピング ファイルに対して上記と同じことを行います。

多対多の関連付けを完全に制御したい人にとっては、これが最終的な選択肢であるともう一度言わなければなりません。

于 2009-11-30T15:05:13.147 に答える