NHibernate:多对多关系不起作用

时间:2009-04-03 07:00:58

标签: nhibernate

我有以下数据库架构:

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>

这是我的Store实体类:

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
    }

Product类的映射:

<?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应将inverse设置为false。代码生成工具没有正确处理这个问题。
  2. 下面介绍了添加多对多关系的正确方法。你必须加两次。

1 个答案:

答案 0 :(得分:9)

这可能与storeproducts表中有代理键的事实有关吗?

如果删除此代理键列Id并将主键放在product_id和store_id列的组合上会发生什么?

我相信,如果你想在storeproducts表上有一个代理键,你就必须创建另一个实体。

如果您想使用代理键,则必须使用idbag mapping。

您的Product类和映射如何? 我看到你在Store实体的Products集合的映射中指定了'inverse'属性。

如果您这样做(因此您有双向关联),那么您也应该将Store添加到产品的Stores集合中。 从NH文件开始 - :

  

仅对反向结束所做的更改   该协会没有坚持。   这意味着NHibernate有两个   每个人在记忆中的表征   双向关联,一个链接   从A到B以及从B到B的另一个链接   A.如果你这更容易理解   想想.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
  

非反面用于保存   内存中的表示   数据库。我们会得到一个不必要的   INSERT / UPDATE甚至可能是一个   如果两者都违反外键   触发变化!当然也是如此   对于双向也是如此   一对多协会。

     

您可以映射双向   通过映射a进行一对多关联   一对多的关联   table column(s)作为多对一   协会并宣布   多值的结束逆=“真”。

这意味着,只有一端应该是反向的。 将商品添加到商店应该这样做:

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

(检查集合是否已包含要添加的项目非常重要;否则您将无限循环。