处理遗留数据库时在 NHibernate 中建模多对一关系的最佳方法?

StackOverflow https://stackoverflow.com/questions/10915

  •  08-06-2019
  •  | 
  •  

警告 - 我对 NHibernate 非常陌生。我知道这个问题看起来很简单 - 而且我确信有一个简单的答案,但我已经在这个问题上旋转了一段时间了。我正在处理一个遗留数据库,它的结构确实无法改变。我有一个详细信息表,其中列出了客户已接受的付款计划。每个付款计划都有一个 ID,可链接回参考表以获取计划的条款、条件等。在我的对象模型中,我有一个 AcceptedPlan 类和一个 Plan 类。最初,我使用从详细表到引用表的多对一关系在 NHibernate 中对这种关系进行建模。我还创建了一个从 Plan 类到 AcceptedPlan 类的相反方向的一对多关系。当我只是读取数据时,这很好。我可以转到我的 Plan 对象(它是 AcceptedPlan 类的属性)来读取计划的详细信息。当我必须开始向详细信息表插入新行时,我的问题出现了。根据我的阅读,创建新子对象的唯一方法似乎是将其添加到父对象,然后保存会话。但我不想每次想要创建新的详细记录时都必须创建新的父计划对象。这似乎是不必要的开销。有谁知道我是否以错误的方式处理这个问题?

有帮助吗?

解决方案

我会避免让子对象包含其逻辑父对象,当您这样做时,它会变得非常混乱并且非常递归。在做此类事情之前,我会先看看您打算如何使用域模型。您可以轻松地在表中保留 ID 引用,只需将它们保留为未映射即可。

这里有两个示例映射,可能会引导您走向正确的方向,我不得不使用 adlib 表名称等,但它可能会有所帮助。我可能还建议将 StatusId 映射到枚举。

请注意 bag 将详细信息表有效映射到集合中的方式。

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2">
    <class lazy="false" name="Namespace.Customer, Namespace" table="Customer">
        <id name="Id" type="Int32" unsaved-value="0">
            <column name="CustomerAccountId" length="4" sql-type="int" not-null="true" unique="true" index="CustomerPK"/>
            <generator class="native" />
        </id>

        <bag name="AcceptedOffers" inverse="false" lazy="false" cascade="all-delete-orphan" table="details">
          <key column="CustomerAccountId" foreign-key="AcceptedOfferFK"/>
          <many-to-many
            class="Namespace.AcceptedOffer, Namespace"
            column="AcceptedOfferFK"
            foreign-key="AcceptedOfferID"
            lazy="false"
           />
        </bag>

  </class>
</hibernate-mapping>


<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2">
    <class lazy="false" name="Namespace.AcceptedOffer, Namespace" table="AcceptedOffer">
        <id name="Id" type="Int32" unsaved-value="0">
            <column name="AcceptedOfferId" length="4" sql-type="int" not-null="true" unique="true" index="AcceptedOfferPK"/>
            <generator class="native" />
        </id>

        <many-to-one 
          name="Plan"
          class="Namespace.Plan, Namespace"
          lazy="false"
          cascade="save-update"
        >
        <column name="PlanFK" length="4" sql-type="int" not-null="false"/>
        </many-to-one>

        <property name="StatusId" type="Int32">
            <column name="StatusId" length="4" sql-type="int" not-null="true"/>
        </property>

  </class>
</hibernate-mapping>

其他提示

我在写作时没有看到你的数据库图。

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2">
    <class lazy="false" name="Namespace.Customer, Namespace" table="Customer">
        <id name="Id" type="Int32" unsaved-value="0">
            <column name="customer_id" length="4" sql-type="int" not-null="true" unique="true" index="CustomerPK"/>
            <generator class="native" />
        </id>

        <bag name="AcceptedOffers" inverse="false" lazy="false" cascade="all-delete-orphan">
            <key column="accepted_offer_id"/>
            <one-to-many class="Namespace.AcceptedOffer, Namespace"/>
        </bag>

  </class>
</hibernate-mapping>


<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping default-cascade="save-update" xmlns="urn:nhibernate-mapping-2.2">
    <class lazy="false" name="Namespace.AcceptedOffer, Namespace" table="Accepted_Offer">
        <id name="Id" type="Int32" unsaved-value="0">
            <column name="accepted_offer_id" length="4" sql-type="int" not-null="true" unique="true" />
            <generator class="native" />
        </id>

        <many-to-one name="Plan" class="Namespace.Plan, Namespace" lazy="false" cascade="save-update">
            <column name="plan_id" length="4" sql-type="int" not-null="false"/>
        </many-to-one>

  </class>
</hibernate-mapping>

应该可以解决这个问题(我只完成了集合的示例映射,您必须添加其他属性)。

我对此进行建模的方法如下:

Customer 对象包含 ICollection <PaymentPlan> PaymentPlans,它代表客户已接受的计划。

将使用包映射到客户的 PaymentPlan,该包使用详细信息表来确定哪个客户 ID 映射到哪个 PaymentPlan。使用级联所有删除孤儿,如果客户被删除,则详细信息中的条目和客户拥有的 PaymentPlan 都将被删除。

PaymentPlan 对象包含一个 PlanTerms 对象,它表示付款计划的条款。

PlanTerms 将使用多对一映射级联保存更新映射到 PaymentPlan,这只会将对相关 PlanTerms 对象的引用插入到 PaymentPlan 中。

使用此模型,您可以独立创建 PlanTerms,然后当您向客户添加新的 PaymentPlan 时,您将创建一个新的 PaymentPlan 对象,传入相关的 PlanTerms 对象,然后将其添加到相关客户的集合中。最后,您将保存 Customer 并让 nhibernate 级联保存操作。

您最终会得到一个 Customer 对象、一个 PaymentPlan 对象和一个 PlanTerms 对象,其中 Customer(客户表)拥有 PaymentPlans(详细信息表)的实例,这些实例都遵循特定的 PlanTerms(计划表)。

如果需要,我有一些更具体的映射语法示例,但最好使用您自己的模型来完成它,并且我没有足够的数据库表信息来提供任何具体示例。

我不知道这是否可能是因为我的 NHibernate 经验有限,但是您可以创建一个 BaseDetail 类,它只包含详细信息的属性,因为它们直接映射到详细信息表。

然后创建第二个类,该类继承自具有附加 Parent Plan 对象的 BaseDetail 类,以便当您只想创建详细信息行并为其分配 PlanId 时可以创建 BaseDetail 类,但如果您需要填充完整的详细信息记录与Parent计划对象可以使用继承的Detail类。

我不知道这是否有意义,但请告诉我,我会进一步澄清。

我认为您遇到的问题是您的 AcceptedOffer 对象包含一个 Plan 对象,然后您的 Plan 对象似乎包含一个包含 AcceptedOffer 对象的 AcceptedOffers 集合。客户也是如此。我认为,这些对象是彼此的子对象这一事实是导致您出现问题的原因。

同样,AcceptedOffer 的复杂性在于它有两个职责:它表示计划中包含的优惠,表示客户接受。这违反了单一责任原则。

您可能必须区分计划下的要约和客户接受的要约。所以这就是我要做的:

  1. 创建一个没有状态的单独 Offer 对象,例如,它没有客户,也没有状态——它只有一个 OfferId 和它所属的 Plan 作为其属性。
  2. 修改您的 Plan 对象以拥有 Offers 集合(它不必在其上下文中包含已接受的 Offer)。
  3. 最后,修改 AcceptedOffer 对象,使其包含 Offer、Customer 和 Status。客户保持不变。

我认为这将足以解决您的 NHibernate 映射和对象保存问题。:)

对 NHibernate 可能(或可能没有)有用的提示:您可以将对象映射到视图,就像视图是表格一样。只需将视图名称指定为表名称即可;只要所有 NOT NULL 字段都包含在视图和映射中,它就能正常工作。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top