处理旧版数据库时,在NHibernate中为多对一关系建模的最佳方法?

时间:2020-03-05 18:39:36  来源:igfitidea点击:

警告我对NHibernate非常陌生。我知道这个问题似乎很简单,而且我敢肯定有一个简单的答案,但是我已经花了一段时间研究了这个问题。我正在处理的是传统的db,它实际上不能在结构上进行更改。我有一个详细信息表,其中列出了客户已接受的付款计划。每个付款计划都有一个ID,该ID链接回到参考表以获取计划的条款,条件等。在我的对象模型中,我有一个AcceptedPlan类和一个Plan类。最初,我使用了从详细信息表到参考表的多对一关系在NHibernate中对该关系进行建模。我还创建了一对多的关系,其关系从Plan类到AcceptedPlan类朝相反的方向发展。当我只是在读取数据时,这很好。我可以转到我的AcceptedPlan类的一个Plan对象,以读取该计划的详细信息。当我不得不开始向明细表中插入新行时,出现了我的问题。根据我的阅读,似乎创建新子对象的唯一方法是将其添加到父对象,然后保存会话。但是,我不想每次创建新的明细记录时都必须创建一个新的父Plan对象。这似乎是不必要的开销。有谁知道我是否以错误的方式进行此操作?

解决方案

回答

我要对此建模的方法如下:

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

将使用一个袋子来映射到客户的PaymentPlan,该袋子使用详细信息表确定将哪个客户ID映射到哪个PaymentPlan。使用级联all-delete-orphan,如果删除了客户,则详细信息的条目和客户拥有的PaymentPlans都将被删除。

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

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

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

最后,我们将获得一个Customer对象,一个PaymentPlan对象和一个PlanTerms对象,其中Customer(客户表)拥有所有都属于特定PlanTerm(计划表)的PaymentPlans(详细信息表)实例。

如果需要的话,我有一些更具体的映射语法示例,但是最好将其与我们自己的模型一起使用,并且数据库表上没有足够的信息来提供任何特定的示例。

回答

我不知道是否可能是因为我的NHibernate经验有限,但是我们能否创建一个BaseDetail类,该类仅具有Details的属性,因为它们直接映射到Detail表。

然后创建第二个继承自BaseDetail类的类,该类具有添加的Parent Plan对象,这样,当我们只想创建一个Detail行并为其分配PlanId时就可以创建BaseDetail类,但是如果我们需要填充完整的Detail使用父计划对象进行记录,我们可以使用继承的Detail类。

我不知道这是否很有道理,但请告诉我,我将作进一步澄清。

回答

我认为这里的问题是AcceptedOffer对象包含一个Plan对象,然后Plan对象似乎包含一个包含AcceptedOffer对象的AcceptedOffers集合。与客户相同。我认为,这些对象是彼此的子对象,这是导致我们出现问题的原因。

同样,使AcceptedOffer变得复杂的原因是它有两个职责:它指示计划中包括的要约,指示客户的接受。这违反了单一责任原则。

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

  • 创建一个没有状态的单独的Offer对象,例如,它没有客户并且没有状态-它仅具有OfferId和它所属的计划作为其属性。
  • 修改Plan对象以具有Offers集合(它不必在其上下文中接受offer)。
  • 最后,修改AcceptedOffer对象,使其包含要约,客户和状态。客户保持不变。

我认为这将充分解决NHibernate映射和对象保存问题。 :)

回答

我会避免让子对象包含其逻辑父对象,这样做时,它很快就会变得非常混乱且非常递归。在进行此类操作之前,我将先看看我们打算如何使用域模型。我们仍然可以轻松地在表中保留ID引用,而无需对其进行映射。

这是两个示例映射,可能会向正确的方向倾斜,我不得不调整表名等,但这可能会有所帮助。我可能还会建议将StatusId映射到枚举。

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

<?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>

回答

在NHibernate中可能有用(或者可能没有有用)的技巧:我们可以将对象映射到View,就像View是一个表一样。只需将视图名称指定为表名称即可;只要视图和映射中包括所有NOT NULL字段,它就可以正常工作。

回答

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

<?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>

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