在处理传统数据库时,在NHibernate中建立多对一关系的最佳方法?

时间:2008-08-14 11:56:30

标签: c# nhibernate

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

6 个答案:

答案 0 :(得分:3)

我不会让子对象包含他们的逻辑父对象,当你这样做时,它会很快变得非常混乱和非常快速递归。在你做这类事之前,我会先看看你打算如何使用领域模型。您可以轻松地在表格中使用ID引用,并将它们保留为未映射。

以下两个示例映射可能会在正确的方向上推动您,我必须使用adlib表名等,但它可能会有所帮助。我可能还建议将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>

答案 1 :(得分:1)

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

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

应该可以做到这一点(我只为集合做了示例映射,你必须添加其他属性)。

答案 2 :(得分:0)

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

客户对象包含ICollection&lt; PaymentPlan&gt; PaymentPlans代表客户已接受的计划。

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

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

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

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

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

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

答案 3 :(得分:0)

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

然后创建第二个类,该类继承自具有附加父计划对象的BaseDetail类,因此当您只想创建一个Detail行并将PlanId分配给它时,可以创建一个BaseDetail类,但是如果需要填充带有父计划对象的完整详细记录,您可以使用继承的详细信息类。

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

答案 4 :(得分:0)

我认为你在这里遇到的问题是你的AcceptedOffer对象包含一个Plan对象,然后你的Plan对象似乎包含一个包含AcceptedOffer对象的AcceptedOffers集合。与客户一样。我认为,物体是彼此的孩子的事实是导致你的问题的原因。

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

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

  1. 创建一个单独的Offer对象,该对象没有状态,例如,它没有客户且没有状态 - 它只有一个OfferId和它所属的Plan作为其属性。
  2. 修改您的计划对象以拥有优惠集合(在其上下文中不必具有已接受的优惠)。
  3. 最后,修改您的AcceptedOffer对象,使其包含商品,客户和状态。客户保持不变。
  4. 我认为这将充分解开您的NHibernate映射和对象保存问题。 :)

答案 5 :(得分:0)

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