【问题标题】:NHibernate Bidirectional many-to-many Mapping for 2 Lists2个列表的NHibernate双向多对多映射
【发布时间】:2011-10-08 01:05:36
【问题描述】:

我正在尝试为 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 调用会抛出外键约束异常。我做错了什么?

【问题讨论】:

    标签: nhibernate nhibernate-mapping


    【解决方案1】:

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

    车辆:

        <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 负责保存此关系,因此我需要在 Vehicle 端将 inverse 设置为 true。我还需要将级联标记为“保存更新”,以便车辆将更新级联到区域并将关系链接到区域。因此保存单个车辆链接表中的区域以及保存区域对象,有效地修复了我在之前的保存过程中遇到的错误。

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

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

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

    这样做的原因是,在您调用提交之前,您的事务将锁定数据库中的项目。一旦调用了提交,所有的更新和排队的所有事情都会实际执行。因此,保存只是将实际事务发送到数据库,以便在调用提交后发出。提交本身是所有实际工作发生的地方,一旦被调用,实际项目就会存储在数据库中。显然,您不希望交易花费非常长的时间。

    就我而言,我试图拯救 5000 辆汽车和 9600 个区域。这最终会得到负责存储包含 4800 万行的两个列表的表,因此是为每辆车创建单独的事务和提交调用的原因。使用这种方法可以防止这些项目在这 4800 万次插入的整个持续时间内被锁定,并且效率更高。而是在保存每辆车后调用提交,因此除了保存第一辆车(因为第一次保存还必须保存所有区域,如果它们不存在),每次只会发出 9600 个命令交易。这显然比 4800 万要好得多。

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

    希望这对任何偶然发现此问题的人有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-01
      • 1970-01-01
      • 2010-09-23
      • 1970-01-01
      • 2014-11-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多