2009-11-08 5 views
8

私はNHibernateのセットアップで数日間戦ってきましたが、私のマッピングを設定する方法は、私はそれが期待されるように動作するように。削除を許可するが、重複レコードを避けるためにNHibernateの多対多リレーションシップを定義する正しい方法は何ですか

私が問題になる前に、少しずつコードを書いています。余分な読書のために事前にお詫び申し上げます。

セットアップは、ちょうどこれらのテーブルで、現時点では非常に単純です:

カテゴリー
区分
名前

項目
アイテムID
名前

ItemCat
アイテムID
区分

egoryアイテムは多くのカテゴリにすることができ、各カテゴリは(簡単な多対多の関係)多くのアイテムを持つことができます。

私は私のマッピングのように定めている:

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

    <class name="Category" lazy="true"> 

    <id name="CategoryId" unsaved-value="0"> 
     <generator class="native" /> 
    </id> 
    <property name="Name" /> 

    <bag name="Items" table="ItemCategory" cascade="save-update" inverse="true" generic="true"> 
     <key column="CategoryId"></key> 
     <many-to-many class="Item" column="ItemId"></many-to-many> 
    </bag> 

    </class> 

</hibernate-mapping> 

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

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

    <id name="ItemId" unsaved-value="0"> 
     <generator class="native" /> 
    </id> 
    <property name="Name" /> 

    <bag name="Categories" table="ItemCategory" cascade="save-update" generic="true"> 
     <key column="ItemId"></key> 
     <many-to-many class="Category" column="CategoryId"></many-to-many> 
    </bag> 

    </class> 

</hibernate-mapping> 

項目に分類し、分類リスト内の項目のリストに項目を追加するための私の方法は、関係の両側を設定します。 カテゴリー

public virtual IList<Category> Categories { get; protected set; } 
    public virtual void AddToCategory(Category category) 
    { 
     if (Categories == null) 
      Categories = new List<Category>(); 

     if (!Categories.Contains(category)) 
     { 
      Categories.Add(category); 
      category.AddItem(this); 
     } 
    } 

項目

public virtual IList<Item> Items { get; protected set; } 
    public virtual void AddItem(Item item) 
    { 
     if (Items == null) 
      Items = new List<Item>(); 

     if (!Items.Contains(item)) 
     { 
      Items.Add(item); 
      item.AddToCategory(this); 
     } 
    } 

今では邪魔だ、私がいる問題は、次のとおりです。

  1. Categorから 'inverse = "true"を削除するとy.Itemsマッピングでは、検索ItemCategoryテーブルに重複したエントリがあります。

  2. 'inverse = "true"'を使用すると、NHibernateが一致するレコードをルックアップテーブルから削除しないため、カテゴリを削除しようとするとエラーが発生するため、外部キー制約のため失敗します。

  3. バッグにcascade = "all"を設定した場合、エラーなしで削除できますが、カテゴリを削除するとそのカテゴリのすべてのアイテムも削除されます。

私はあなたが期待するように動作する多対多マッピングを可能にするために、私のマッピングを設定している途中でいくつかの基本的な問題はありますか?

「あなたが期待する」とは、削除されているアイテムとそれに対応するルックアップの値よりも何も削除しないということです(リレーションシップの反対側のアイテムは影響を受けません)。コレクションは、正確で重複しない値でルックアップテーブルを更新します。

ご意見をいただければ幸いです。

答えて

10

Category.ItemsコレクションからItem.Categoriesコレクションに、inverse="true"を移動することが、マップの動作を期待するために行うために必要な作業です。これを行うことで、NHibernateは、どちらがアソシエーションの所有側であり、それが「カテゴリ」側であるかを理解するようになります。

これを行うと、カテゴリオブジェクトを削除することによって、関連オブジェクトが所有側であるため、一致するレコードをルックアップテーブルから削除することができます。

削除するカテゴリオブジェクトに割り当てられているアイテムを削除しないようにするには、カスケード属性を残す必要があります。cascade="save-update"

cascade="all"は、削除されたカテゴリオブジェクトに関連付けられているアイテムを削除します。

inverse = truが存在する側のエンティティを削除すると、関連テーブルのエントリがクリアされないため、外部キー違反例外が発生します。

あなたのマッピングが(あなたの質問に記載された説明によって)働きたいと思ったとおりに正確に働くソリューションは、関連テーブルを明示的にマッピングすることです。 あなたのマッピングは、次のようになります。

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

    <class name="Category" lazy="true"> 

    <id name="CategoryId" unsaved-value="0"> 
     <generator class="native" /> 
    </id> 
    <property name="Name" /> 

    <bag name="ItemCategories" generic="true" inverse="true" lazy="true" cascade="none"> 
     <key column="CategoryId"/> 
     <one-to-many class="ItemCategory"/> 
    </bag> 

    <bag name="Items" table="ItemCategory" cascade="save-update" generic="true"> 
     <key column="CategoryId"></key> 
     <many-to-many class="Item" column="ItemId"></many-to-many> 
    </bag> 

    </class> 

</hibernate-mapping> 

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

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

    <id name="ItemId" unsaved-value="0"> 
     <generator class="native" /> 
    </id> 
    <property name="Name" /> 

    <bag name="ItemCategories" generic="true" inverse="true" lazy="true" cascade="all-delete-orphan"> 
     <key column="ItemId"/> 
    <one-to-many class="ItemCategory"/> 
    </bag> 

    <bag name="Categories" table="ItemCategory" inverse="true" cascade="save-update" generic="true"> 
     <key column="ItemId"></key> 
     <many-to-many class="Category" column="CategoryId"></many-to-many> 
    </bag> 

    </class> 

</hibernate-mapping> 

それはあなたが次のことを可能にする上でそのまま:

  1. カテゴリを削除し、アイテムのみのいずれかを削除せずにアソシエーションテーブルにエントリを削除
  2. カテゴリを削除せずに関連テーブル内のエントリのみを削除する
  3. Category.Itemsコレクションを作成してカテゴリを保存することで、カテゴリ側からカスケードで保存しますy。
  4. Item.Categoriesではinverse = trueが必要なので、この側からカスケード保存する方法はありません。 Item.Categoriesコレクションに値を設定してアイテムオブジェクトを保存すると、アイテムテーブルに挿入され、カテゴリテーブルに挿入されますが、関連テーブルには挿入されません。私はこれがNHibernateがどのように動作するのかと思いますが、私はまだそれを回避する方法を見つけていません。

上記のすべてをユニットテストでテストします。 上記のためにItemCategoryクラスマッピングファイルとクラスを作成する必要があります。

+0

"inverse = 'true'"を逆にすると、カテゴリを期待どおりに削除できますが、これまでの問題とは逆の結果になります。アイテムを削除しようとするとエラーが発生しますルックアップ値を削除します。 –

+0

私は主題についてさらに調査を行い、自分の調査結果を含めるために私の答えを編集しました。 – tolism7

+0

更新していただきありがとうございました。これは大きな助けとなりました。遅い返事をお送りいただき、ありがとうございます。回答を更新したことをお知らせしてメールを受け取っていませんでした。 –

1

コレクションを同期させていますか? Hibernateは正しいオブジェクトグラフを持っていると思います。 Item.Categoriesからエントリを削除すると、Category.Itemsから同じエントリを削除して、2つのコレクションが同期するようにする必要があると思います。

+0

上記のサンプルのaddメソッドに似た削除メソッドがあります。これらは正常に機能します。実際のオブジェクトの削除に問題があります。 オブジェクトを削除するときに、関連するアイテムのコレクションをループすることができますが、NHibernateがこれを選択する必要があると考えました。 –

+0

通常、この種の関連付けを、削除可能な実際のItemCategoryオブジェクトにマップします。私はあなたのような直接の協会には、それを修正する方法は不明です。申し訳ありませんが私はより多くの助けができませんでした。 =/ – RMorrisey

+0

したがって、ItemCategoryオブジェクトとマッピングを作成した場合、ItemとCategoryの両方がItemCategoryと一対多の関連を持ち、ItemCategoryオブジェクトのコレクションを持つことになります。アイテムのカテゴリを取得するには、Item.ItemCategories.Categoriesのような操作を行いますか?または、ItemCategoryマッピングオブジェクトを使用できますが、Item.CategoriesとCategory.Itemsだけでドメインモデルを「クリーン」に保つことは可能ですか? –

関連する問題