NHibernate针对2个列表的双向多对多映射

时间:2011-10-05 16:36:55

标签: nhibernate nhibernate-mapping

我正在尝试为2个具有彼此集合的类生成正确的映射。

我目前有一个Zone和一个Vehicle类。 Zone类包含包含区域的车辆列表。 Vehicle类包含包含Vehicle的区域列表。如您所见,这两个列表彼此直接相关。但是,在尝试保存我的一个对象时,我的映射一直给我一个外键约束错误。

有人可以解释我做错了吗?

以下是Vehicle类的映射:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class xmlns="urn:nhibernate-mapping-2.2" name="EMTRAC.Devices.Device, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Device`">
    <id name="PK" type="System.Int64, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="PK" />
      <generator class="identity" />
    </id>
    <version name="LastModifiedOn" column="LastModifiedOn" type="timestamp" access="field.pascalcase-underscore" />
    <joined-subclass name="EMTRAC.Vehicles.Vehicle, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
      <key>
        <column name="Device_id" />
      </key>      
      <component name="Zones" access="property">
        <bag name="_list" cascade="save-update" access="field" table="VehicleZones">
          <key>
            <column name="Veh_id"/>
          </key>          
          <many-to-many class="EMTRAC.Zones.Zone, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
        </bag>
      </component>
      <property name="ID" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <column name="ID" />
      </property>      
    </joined-subclass>
  </class>
</hibernate-mapping>

这是我对Zone类的映射:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class xmlns="urn:nhibernate-mapping-2.2" name="EMTRAC.Zones.Zone, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Zone`">
    <id name="ID" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="PK"/>
      <generator class="identity" />
    </id>
    <version name="LastModifiedOn" column="LastModifiedOn" type="timestamp" access="field.pascalcase-underscore" />
    <property name="ID" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="ID" />
    </property>    
    <component name="Vehicles" access="property">
      <bag name="_list" cascade="save-update" access="field" table="VehicleZones" inverse="true">
        <key>
          <column name="Zone_id" not-null="false"/>
        </key>
        <many-to-many class="EMTRAC.Vehicles.Vehicle, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      </bag>
    </component>
  </class>
</hibernate-mapping>

我通过以下方式保存区域和车辆:

    using (var session = _sessionFactory.OpenSession())
{
    foreach (Zone zone in Program.data.Zones.list)
    {
        session.SaveOrUpdate(zone);
    }
    foreach (Vehicle veh in Program.data.Vehicles.list)
    {
        session.SaveOrUpdate(veh);
    }
}

之后我将区域添加到车辆列表和车辆到区域列表,然后我尝试通过以下方式保存列表:

using (var session = _sessionFactory.OpenSession())
{              
    foreach (Zone zone in Program.data.Zones.list)
    {
        foreach (Vehicle veh in Program.data.Vehicles.list)
        {
            veh.Zones.Add(zone);
            zone.Vehicles.Add(veh);
        }
    }

    using (var tx = session.BeginTransaction())
    {
        foreach (Vehicle veh in Program.data.Vehicles.list)
        {
            session.Update(veh.Zones);
        }
        tx.Commit();
    }

}

此时Commit调用将抛出外键约束异常。我做错了什么?

1 个答案:

答案 0 :(得分:0)

好的,我终于弄清楚我做错了什么。我最终确定的映射版本是:

车辆:

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class xmlns="urn:nhibernate-mapping-2.2" name="EMTRAC.Devices.Device, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Device`">
    <id name="PK" type="System.Int64, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="PK" />
      <generator class="identity" />
    </id>
    <version name="LastModifiedOn" column="LastModifiedOn" type="timestamp" access="field.pascalcase-underscore" />    
    <joined-subclass name="EMTRAC.Vehicles.Vehicle, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
      <key>
        <column name="Device_id" />
      </key>      
      <component name="Zones" access="property">
        <bag name="_list" cascade="save-update" access="field" table="VehicleZones" inverse="true">
          <key>
            <column name="veh_id" not-null="true"/>
          </key>
          <many-to-many class="EMTRAC.Zones.Zone, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
        </bag>
      </component>
      <property name="ID" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <column name="ID" />
      </property>      
    </joined-subclass>
  </class>
</hibernate-mapping>

区域:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class xmlns="urn:nhibernate-mapping-2.2" name="EMTRAC.Zones.Zone, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Zone`">
    <id name="ID" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="PK"/>
      <generator class="identity" />
    </id>
    <version name="LastModifiedOn" column="LastModifiedOn" type="timestamp" access="field.pascalcase-underscore" />
    <property name="ID" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="ID" />
    </property>
    <component name="Vehicles" access="property">
      <bag name="_list" cascade="save-update" access="field" table="VehicleZones">
        <key>
          <column name="veh_id" not-null="true"/>
        </key>
        <many-to-many class="EMTRAC.Vehicles.Vehicle, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      </bag>
    </component>
  </class>
</hibernate-mapping>

他们的主要原因是我所有的问题都是在区域映射方面设置的inverse =“true”。

如果您注意到我从区域侧移除了此标志并将其放在车辆端。该标志通知Hibernate哪个类负责维护关系。我正在保存车辆,而不是导致处理外键错误的区域。

因为我实际上希望车辆负责保存这种关系,所以我需要在Vehicle侧将逆集设置为true。我还需要将级联标记为“保存更新”,以便车辆将更新级联到区域并将关系链接到区域。因此,保存单个车辆链接表格中的区域以及保存区域对象,有效地修复了我在保存过程中遇到的错误。

现在保存工作正常。虽然我会建议采用以下方式保存这些项目的方法:

foreach (Vehicle veh in Program.data.Vehicles.list)
{
    using (ITransaction tx = session.BeginTransaction())
    {
        session.Save(veh);

        //  Commit transactions
        tx.Commit();
    }
}

如果两个列表中有大量类。

原因是您的事务将在您调用提交期间锁定数据库中的项目。一旦调用了commit,就会实际执行所有更新和排队的所有内容。因此,保存仅仅是在调用提交后将实际事务发送到要发布的数据库。提交本身是所有实际工作发生的地方,一旦被调用,实际项目就存储在数据库中。显然你不希望交易花费很长一段时间。

在我的情况下,我试图节省5000辆车和9600个区域。这最终将导致负责存储包含4800万行的两个列表的表,因此每个车辆创建单独的事务和提交调用的原因。使用这种方法可以防止这些物品在这4800万个插入物的整个持续时间内被锁定,并且效率更高。而是在保存每辆车之后调用提交,因此除了第一个保存的车辆之外(因为第一个保存必须保存所有区域,如果它们不存在),每个车辆只发出9600个命令交易。这明显好于4800万。

但是我想指出,这不是一个样本量小得多的主要问题。这只是每个人都要考虑的事情。

希望这有助于任何偶然发现这一点的人。