NHibernate: отношения многие ко многим не работают - PullRequest
2 голосов
/ 03 апреля 2009

У меня есть следующая схема базы данных:

http://lh4.ggpht.com/_SDci0Pf3tzU/SdM3XnAmmxI/AAAAAAAAEps/Ie3xW3ZVNfQ/s400/styleerror.png

А это мой файл сопоставления:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="CodeSmithSampel.Generated.BusinessObjects" assembly="CodeSmithSampel">
    <class name="CodeSmithSampel.Generated.BusinessObjects.Store, CodeSmithSampel" table="store" lazy="true">
        <id name="Id" column="Id">
            <generator class="native" />
        </id>
        <property name="Name" column="Name" />
        <bag name="Employees" lazy="true" cascade="all-delete-orphan" inverse="true" >
            <key column="Store_id"></key>
            <one-to-many class="Employee"></one-to-many>
        </bag>
        <bag name="Products" table="storeproduct" lazy="true" cascade="all" inverse="true" >
            <key column="Store_id"></key>
            <many-to-many column="Product_id" class="Product" />
        </bag>
    </class>
</hibernate-mapping>

И это мой класс сущностей Магазина:

public partial class Store : BusinessBase<int>
{
    #region Declarations

        private string _name = String.Empty;


        private IList<Employee> _employees = new List<Employee>();
        private IList<Product> _products = new List<Product>();

        #endregion

    #region Constructors

    public Store() { }

    #endregion

    #region Methods

    public override int GetHashCode()
    {
        System.Text.StringBuilder sb = new System.Text.StringBuilder();

        sb.Append(this.GetType().FullName);
            sb.Append(_name);

        return sb.ToString().GetHashCode();
    }

    #endregion

    #region Properties

        public virtual string Name
    {
        get { return _name; }
            set
            {
                OnNameChanging();
                _name = value;
                OnNameChanged();
            }
    }
        partial void OnNameChanging();
        partial void OnNameChanged();

        public virtual IList<Employee> Employees
    {
        get { return _employees; }
        set
            {
                OnEmployeesChanging();
                _employees = value;
                OnEmployeesChanged();
            }
    }
        partial void OnEmployeesChanging();
        partial void OnEmployeesChanged();

        public virtual IList<Product> Products
    {
        get { return _products; }
        set
            {
                OnProductsChanging();
                _products = value;
                OnProductsChanged();
            }
    }
        partial void OnProductsChanging();
        partial void OnProductsChanged();

    #endregion
}

Класс продукции:

   public partial class Product : BusinessBase<int>
    {
        #region Declarations

        private float _price = default(Single);
        private string _name = null;


        private IList<Store> _stores = new List<Store>();

        #endregion

        #region Constructors

        public Product() { }

        #endregion

        #region Methods

        public override int GetHashCode()
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();

            sb.Append(this.GetType().FullName);
            sb.Append(_price);
            sb.Append(_name);

            return sb.ToString().GetHashCode();
        }

        #endregion

        #region Properties

        public virtual float Price
        {
            get { return _price; }
            set
            {
                OnPriceChanging();
                _price = value;
                OnPriceChanged();
            }
        }
        partial void OnPriceChanging();
        partial void OnPriceChanged();

        public virtual string Name
        {
            get { return _name; }
            set
            {
                OnNameChanging();
                _name = value;
                OnNameChanged();
            }
        }
        partial void OnNameChanging();
        partial void OnNameChanged();

        public virtual IList<Store> Stores
        {
            get { return _stores; }
            set
            {
                OnStoresChanging();
                _stores = value;
                OnStoresChanged();
            }
        }
        partial void OnStoresChanging();
        partial void OnStoresChanged();

        #endregion
    }

Отображение для класса продукта:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="CodeSmithSampel.Generated.BusinessObjects" assembly="CodeSmithSampel">
    <class name="CodeSmithSampel.Generated.BusinessObjects.Product, CodeSmithSampel" table="product" lazy="true">
        <id name="Id" column="Id">
            <generator class="native" />
        </id>
        <property name="Price" column="Price" />
        <property name="Name" column="Name" />
        <bag name="Stores" table="storeproduct" lazy="true" cascade="all" inverse="true" >
            <key column="Product_id"></key>
            <many-to-many column="Store_id" class="Store" />
        </bag>
    </class>
</hibernate-mapping>

Что особенно странно, так это то, что когда я добавляю объект Store в один из продуктов, запись в базе данных не обновляется; кажется, что добавление не происходит, хотя новый объект хранилища существует в базе данных:

        IManagerFactory managerFactory = new ManagerFactory();
        var productManager = managerFactory.GetProductManager();

        var myProduct= productManager.GetById(2);

        var myStore = new Store();
        myStore.Name = "new Store";  //a "new store" entry is created in the Store table
        myProduct.Stores.Add(myStore);  // but this "new store" is not linked to the myproduct, as it should.

       productManager.Session.CommitChanges();

Есть что-то, по чему я скучаю?

Примечание. Я генерирую приведенный выше код с помощью CodeSmith.

Редактировать: принятый ответ работает. Причина, по которой я попал в эту проблему, заключается в том, что

  1. Только один класс сущностей должен иметь inverse = true, а не два. Таким образом, либо Product, либо Store должны установить обратное значение false. Инструмент генерации кода не справился с этим должным образом.
  2. Правильный способ добавления отношений «Много ко многим» описан ниже. Вы должны добавить два раза.

1 Ответ

9 голосов
/ 03 апреля 2009

Может ли это быть как-то связано с тем, что у вас есть суррогатный ключ в таблице storeproducts?

Что произойдет, если вы удалите этот идентификатор столбца суррогатного ключа и поместите первичный ключ в комбинацию столбцов product_id и store_id?

Я считаю, что если вы хотите иметь суррогатный ключ в таблице storeproducts, вам придется создать еще одну сущность.

Если вы хотите использовать суррогатный ключ, вам придется использовать отображение idbag.

Как выглядит класс продукта и его отображение? Я вижу, что вы указываете атрибут 'inverse' в своем отображении коллекции Products в сущности Store.

Если вы сделаете это (и, следовательно, у вас будет двунаправленная ассоциация), то вы должны добавить Store в коллекцию Stores продукта. Поскольку - из документации NH -:

Изменения сделаны только в обратном конце Ассоциации не сохранились. Это означает, что NHibernate имеет два представления в памяти для каждого двунаправленная ассоциация, одна ссылка от А до Б и еще одна ссылка от Б до О. Это легче понять, если вы думать об объектной модели .NET и как мы создаем многие ко многим отношения в C #:

category.Items.Add(item);          // The category now "knows" about the relationship
item.Categories.Add(category);     // The item now "knows" about the relationship

session.Update(item);                     // No effect, nothing will be saved!
session.Update(category);                 // The relationship will be saved

Не обратная сторона используется для сохранения представление в памяти в база данных. Мы бы получили ненужный ВСТАВИТЬ / ОБНОВИТЬ и, возможно, даже нарушение внешнего ключа, если оба вызвать изменения! То же самое конечно также верно для двунаправленного ассоциации один ко многим.

Вы можете отобразить двунаправленный связь один ко многим путем картирования связь один ко многим с одним и тем же столбцы таблицы как многие-к-одному ассоциация и декларирование многозначный конец inverse = "true".

Это означает, что только один из концов должен быть обратным. Добавление товара в магазин должно быть сделано так:

public class Store
{
   public void AddProduct( Product p )
   {
       if( _products.Contains (p) == false )
       {
             _products.Add (p);
             p.AddStore(this);
       }
   }
}
public class Product
{
    public void AddStore( Store s )
    {
       if( _stores.Contains (s) == false )
       {
          _stores.Add (s);
          s.AddProduct(this);
       }
    }
}

(Очень важно проверить, содержит ли коллекция уже добавляемый элемент; в противном случае вы попадете в бесконечный цикл.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...