[英]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变得复杂的是它有两个职责:它表示计划中包含的要约,它表示客户接受。 这违反了单一责任原则。
您可能必须区分计划下的要约和客户接受的要约。 所以这就是我要做的事情:
我认为这将充分解开您的NHibernate映射和对象保存问题。 :)
在NHibernate中可能(或可能不)有用的提示:您可以将对象映射到Views,就像View是一个表一样。 只需将视图名称指定为表名; 只要视图和映射中包含所有NOT NULL字段,它就可以正常工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.