繁体   English   中英

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

[英]Best way to model Many-To-One Relationships in NHibernate When Dealing With a Legacy DB?

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

我会避免让子对象包含他们的逻辑父对象,当你这样做时,它会变得非常混乱并且非常快速递归。 在你做这类事之前,我会先看看你打算如何使用领域模型。 您可以轻松地在表格中使用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>

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

<?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映射到哪个PaymentPlans。 使用cascade all-delete-orphan,如果客户被删除,详细信息条目和客户拥有的PaymentPlans都将被删除。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM