2009-11-08 8 views
8

Walczyłem z konfiguracją NHibernate od kilku dni i po prostu nie mogę znaleźć prawidłowego sposób, aby ustawić moje mapowanie, aby działało tak, jak bym tego oczekiwał.Jaki jest poprawny sposób definiowania relacji wielu do wielu w NHibernate, aby umożliwić usuwanie, ale unikanie duplikatów rekordów

Zanim przejdę do problemów, muszę przejść trochę kodu, więc przepraszam z góry za dodatkowy odczyt.

Konfiguracja jest bardzo prosta w tej chwili, z tylko tych tabelach:

Kategoria
CategoryId
Nazwa

Przedmiot
ItemId
Nazwa

ItemCat egory
ItemId
CategoryId

Element może być w wielu kategoriach, a każda kategoria może mieć wiele elementów (proste wiele do wielu relacji).

Mam mapowanie określone jako:

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

moje metody dodawania elementów do listy pozycji w kategorii i listy kategorii w pozycję podaną obu stron relacji.

W Przedmiot:

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

W Kategoria:

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

teraz to na uboczu, kwestie Mam to:

  1. Jeśli usunę "inverse =" true "z kategorii mapowanie y.Items, otrzymuję zduplikowane wpisy w tabeli LookKategorii kategorii.

  2. Podczas używania "inverse =" true "", pojawia się błąd, gdy próbuję usunąć kategorię, ponieważ NHibernate nie usuwa pasującego rekordu z tabeli odnośników, więc nie działa z powodu ograniczenia klucza obcego.

  3. Jeśli ustawię kaskadę = "wszystkie" na torebkach, mogę usunąć bezbłędnie, ale usunięcie kategorii spowoduje również usunięcie wszystkich pozycji w tej kategorii.

Czy jest jakiś podstawowy problem z tym, w jaki sposób skonfigurowałem swoje mapowanie, aby mapowanie wielu do wielu działało zgodnie z oczekiwaniami?

"Tak jak można się spodziewać", oznacza to, że usunięcie nie usunie niczego poza usunięciem elementu i odpowiednimi wartościami wyszukiwania (pozostawiając element na drugim końcu relacji bez zmian) i aktualizacjami do kolekcja zaktualizuje tabelę odnośników z poprawnymi i nie powielonymi wartościami.

Wszelkie sugestie będą bardzo cenne.

Odpowiedz

10

To, co musisz zrobić, aby twoje odwzorowania działały zgodnie z oczekiwaniami, to przenieść inverse="true" z kolekcji Category.Items do kolekcji Item.Categories. Robiąc to, NHibernate będzie wiedział, która strona jest właścicielem, i która będzie stroną "Kategorii".

Jeśli to zrobisz, usuwając obiekt kategorii, usunie on pasujący rekord z tabeli odnośników, tak jak tego chcesz, ponieważ jest to strona powiązania z właścicielem.

Aby NIE usunąć pozycji, które są przypisane do obiektu Kategorii, który ma zostać usunięty, należy pozostawić atrybut kaskadowy jako: cascade="save-update".

cascade="all" spowoduje usunięcie pozycji powiązanych z usuniętym obiektem kategorii.

Efektem ubocznym jest to, że usunięcie obiektu po stronie, w której istnieje odwrotność = tru, spowoduje wykrycie wyjątku naruszenia klucza obcego, ponieważ wpis w tabeli powiązań nie zostanie wyczyszczony.

Rozwiązaniem, które sprawi, że twoje odwzorowania będą działać dokładnie tak, jak chcesz, aby działały (zgodnie z opisem podanym w pytaniu) byłoby jawne odwzorowanie tabeli powiązań. mapowania powinien wyglądać tak:

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

Jak to jest powyżej pozwala następujące:

  1. usunąć kategorię i tylko usunąć wpis w tabeli stowarzyszenia bez usuwania dowolnych elementów
  2. Usuń element i usuń tylko wpis w tabeli powiązań bez usuwania żadnej z kategorii
  3. Zapisuj kaskadami tylko od strony kategorii, wypełniając kolekcję Category.Items i zapisując kategorię y.
  4. Ponieważ odwrotność = prawda jest konieczna w Item.Categories, nie ma sposobu, aby zapisać kaskadowanie z tej strony. Zapełniając kolekcję Item.Categories, a następnie zapisując obiekt Item, otrzymasz wstawienie do tabeli Item i wstawienie do tabeli kategorii bez wstawiania do tabeli powiązań. Zgaduję, że tak działa NHibernate i jeszcze nie znalazłem sposobu na obejście tego.

Wszystkie powyższe są testowane z testami jednostkowymi. Należy utworzyć plik mapowania klasy klasy ItemCategory i klasę, aby powyższe działało.

+0

Jeśli odwrócę "inverse = 'true'" zgodnie z sugestią, mogę usunąć kategorie zgodnie z oczekiwaniami, ale wtedy mam odwrotność problemu, który miałem wcześniej: pojawia się błąd, gdy próbuję usunąć element, ponieważ nie usuń wartość wyszukiwania. –

+0

Zrobiłem więcej badań na ten temat i zredagowałem moją odpowiedź, aby uwzględnić moje ustalenia. – tolism7

+0

Dzięki za aktualizację - to była ogromna pomoc. Przepraszamy za spóźnioną odpowiedź. Nie dostałem wiadomości e-mail z informacją, że zaktualizowałeś swoją odpowiedź. –

1

Czy przechowujesz kolekcje w synchronizacji? Hibernate spodziewa się, że, jak sądzę, ma poprawny wykres obiektów; jeśli usuniesz wpis z Item.Categories, myślę, że musisz usunąć ten sam wpis z Category.Items, aby te dwie kolekcje były zsynchronizowane.

+0

Mam metodę usuwania podobną do metod dodawania w powyższych przykładach, a te działają dobrze, jest to po prostu usunięcie rzeczywistego obiektu, z którym mam problem. Przypuszczam, że mógłbym przechodzić przez kolekcję powiązanych obiektów w obiekcie podczas usuwania, ale myślałem, że NHibernate powinien to odbierać. –

+0

Zazwyczaj mapowałbym ten rodzaj powiązania z rzeczywistym obiektem ItemCategory, który można usunąć. Nie jestem tak pewny w bezpośrednim związku, jak ty, jak to naprawić.Przepraszam, że nie mogę pomóc. =/ – RMorrisey

+0

Tak więc, jeśli utworzyłem obiekt ItemCategory i mapowanie, oznaczałoby to, że zarówno element, jak i kategoria miałyby powiązanie jeden do wielu z kategorią ItemCategory oraz kolekcję obiektów ItemCategory? Czy następnie zrobiłbym coś takiego jak Item.ItemCategories.Categories, aby uzyskać, powiedzmy, Kategorie przedmiotu? Czy możliwe jest użycie obiektu odwzorowującego ItemCategory, ale model domeny jest "czysty" tylko w Item.Categories i Category.Items? –

Powiązane problemy