首页 文章

NHibernate:多对多关系无法首先保存子对象(“无法插入null”或“瞬态对象”)

提问于
浏览
0

我有一个聚合FreightDateTime类的寄售类 . 同时,FreightDateTime类也由GoodsItem类聚合 . 以同样的方式,FreightDateTime与我现在遗漏的许多其他类相关联 .

为了避免带有ConsignmentId外键,GoodsItemId外键等的数据库表FreightDateTime,我决定该关联应该是多对多的 . 这样,NHibernate会为每个关系生成一个关联表(ConsigmentFreightDateTimes,GoodsItemFreightDateTimes),这更有意义 .

因此,在映射文件中,关联看起来例如像这样:

<bag name="DateTimes" table="FreightDateTimes" lazy="false" cascade="all">
  <key column="ConsignmentId"/>
  <many-to-many class="Logistics.FreightDateTime, Logistics" column="DateTimeId" />
</bag>

将级联设置为“全部”产量:

System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'DateTimeId', table 'LogiGate.dbo.FreightDateTimes'; column does not allow nulls. INSERT fails.

将级联设置为“无”会产生:

NHibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Logistics.FreightDateTime

在这两种情况下,这意味着NHibernate正在尝试保存Consignment实例,尽管尚未保存子FreightDateTime实例 . 在第一种情况下,外键仍为“null”,因此无法插入到结果表中,在第二种情况下,NHibernate知道实例尚未保存,因此抛出异常 .

所以问题是如何让NHibernate首先保存所有子实例而不明确告诉它这样做 . 我预感到允许列DateTimeId上的空值可以解决这个问题,但我认为这既不可取也不可行 .

1 回答

  • 1

    尝试在另一侧映射关联,但在该侧使用inverse =“true”属性 . 因此,在FreightDateTime映射文件中创建一个包,用于将关联映射为与寄售的多对多关系 .

    此外,我在这里回答了类似的问题:What is the correct way to define many-to-many relationships in NHibernate to allow deletes but avoiding duplicate records

    阅读问题和我的答案可能会帮助您了解您的多对多关联发生了什么,并可能会给您提供解决方案的提示 . 最终的建议是最后的选择 .

    上述问题的答案只是想知道另一个人的不同问题是什么 .

    解决方案是显式映射关联表 . 如果你的表是:Person,Note和关联表(X表)是PersonNote你的映射应该是这样的:

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                       assembly="..."
                       namespace="...">
    
      <class name="Person" table="Person" lazy="true">
    
        <id name="PersonId">
          <generator class="native" />
        </id>
        <property name="FirstName" />
        .....
        <bag name="PersonNotes" generic="true" inverse="true" lazy="true" cascade="none">
            <key column="PersonId"/>
            <one-to-many class="PersonNote"/>
        </bag>
    
        <bag name="Notes" table="PersonNote" cascade="save-update">
          <key column="PersonId"></key>
          <many-to-many class="Note" column="NoteId"></many-to-many>
        </bag>
    
      </class>
    
    </hibernate-mapping>
    
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                       assembly="..."
                       namespace="...">
    
      <class name="Note" table="Note" lazy="true">
    
        <id name="NoteId" unsaved-value="0">
          <generator class="native" />
        </id>
        <property name="Title" />
        ....
        <bag name="PersonNotes" inverse="true" lazy="true" cascade="all-delete-orphan">
            <key column="NoteId"/>
            <one-to-many class="PersonNote"/>
        </bag>
    
        <bag name="People" table="PersonNote" inverse="true" cascade="save-update" generic="true">
          <key column="NoteId"></key>
          <many-to-many class="Person" column="PersonId"></many-to-many>
        </bag>
    
      </class>
    
    </hibernate-mapping>
    

    如上所述它允许您以下内容:

    • 删除Person并仅删除关联表中的条目而不删除任何Notes

    • 删除注释,仅删除关联表中的条目而不删除任何Person实体

    • 通过填充Person.Notes集合并保存Person,仅从Person侧保存Cascades .

    • 因为在Note.People中有必要使用inverse = true,所以还没有找到解决方法 .

    • 只能通过在Note实体的PersonNotes集合中添加新项目,显式地在关联表中级联保存条目 .

    以上所有都是通过单元测试进行测试的 . 您需要创建PersonNote类映射文件和类以使其工作 .

    如果你需要另一个类来做笔记,那就说组织然后你只将另一个关联表添加到名为OrgnanisationNote的模式中,你就像上面那样对组织映射文件和Note映射文件做同样的事情 .

    我必须再次说,这应该是任何想要完全控制他/她的多对多关联的人的最终选择 .

相关问题