警告 - 我对 NHibernate 很陌生。我知道这个问题看起来很简单——我敢肯定有一个简单的答案,但我已经在这个问题上旋转了一段时间。我正在处理一个在结构上确实无法改变的遗留数据库。我有一个详细信息表,其中列出了客户已接受的付款计划。每个付款计划都有一个 ID,它链接回参考表以获取计划的条款、条件等。在我的对象模型中,我有一个 AcceptedPlan 类和一个 Plan 类。最初,我在 NHibernate 中使用从 detail 表到 ref 表的多对一关系来建模这种关系。我还创建了一个从 Plan 类到 AcceptedPlan 类的相反方向的一对多关系。当我只是读取数据时,这很好。我可以转到我的计划对象,这是我的 AcceptedPlan 类的一个属性,以阅读计划的详细信息。当我不得不开始向详细信息表中插入新行时,我的问题就出现了。根据我的阅读,似乎创建新子对象的唯一方法是将其添加到父对象,然后保存会话。但是我不想每次创建新的详细记录时都必须创建一个新的父 Plan 对象。这似乎是不必要的开销。有谁知道我是否以错误的方式处理这个问题?不想每次我想创建一个新的详细记录时都必须创建一个新的父计划对象。这似乎是不必要的开销。有谁知道我是否以错误的方式处理这个问题?不想每次我想创建一个新的详细记录时都必须创建一个新的父计划对象。这似乎是不必要的开销。有谁知道我是否以错误的方式处理这个问题?
6 回答
我会避免让子对象包含其逻辑父对象,当您这样做时,它会很快变得非常混乱和非常递归。在你做那种事情之前,我会先看看你打算如何使用域模型。您仍然可以轻松地在表中保留 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>
我在写的时候没有看到你的数据库图。
<?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,它表示客户已接受的计划。
将使用包映射到客户的付款计划,该包使用详细信息表来确定哪个客户 ID 映射到哪些付款计划。使用 cascade all-delete-orphan,如果客户被删除,则详细信息中的条目和客户拥有的 PaymentPlans 都将被删除。
PaymentPlan 对象包含一个表示付款计划条款的 PlanTerms 对象。
PlanTerms 将使用多对一映射级联保存更新映射到 PaymentPlan,它只会将相关 PlanTerms 对象的引用插入到 PaymentPlan 中。
使用此模型,您可以独立创建 PlanTerms,然后当您向客户添加新的 PaymentPlan 时,您将创建一个传入相关 PlanTerms 对象的新 PaymentPlan 对象,然后将其添加到相关 Customer 的集合中。最后,您将保存 Customer 并让 nhibernate 级联保存操作。
您最终会得到一个 Customer 对象、一个 PaymentPlan 对象和一个 PlanTerms 对象,其中 Customer(客户表)拥有 PaymentPlans 实例(详细信息表),所有这些实例都遵循特定的 PlanTerms(计划表)。
如果需要,我有一些更具体的映射语法示例,但最好使用您自己的模型完成它,而且我没有足够的数据库表信息来提供任何具体示例。
我不知道这是否可能是因为我的 NHibernate 经验有限,但您能否创建一个 BaseDetail 类,该类仅具有 Details 的属性,因为它们直接映射到 Detail 表。
然后创建第二个类,该类继承自具有附加父计划对象的 BaseDetail 类,因此当您只想创建一个详细信息行并将 PlanId 分配给它时,您可以创建一个 BaseDetail 类,但如果您需要填充完整的详细信息记录与父计划对象可以使用继承的细节类。
我不知道这是否有很大的意义,但请告诉我,我会进一步澄清。
我认为您在这里遇到的问题是您的 AcceptedOffer 对象包含一个 Plan 对象,然后您的 Plan 对象似乎包含一个包含 AcceptedOffer 对象的 AcceptedOffers 集合。与客户相同。我认为,这些对象是彼此的孩子的事实是导致您的问题的原因。
同样,使您的 AcceptedOffer 变得复杂的原因在于它有两个职责:它表示计划中包含的报价,它表示客户接受。这违反了单一职责原则。
您可能必须区分计划下的报价和客户接受的报价。所以这就是我要做的:
- 创建一个没有状态的单独的 Offer 对象,例如,它没有客户,也没有状态——它只有一个 OfferId 和它所属的计划作为它的属性。
- 修改您的 Plan 对象以拥有一个 Offers 集合(它不必在其上下文中接受 offer)。
- 最后,修改 AcceptedOffer 对象,使其包含要约、客户和状态。客户保持不变。
我认为这将充分解决您的 NHibernate 映射和对象保存问题。:)
在 NHibernate 中可能(或可能没有)有用的提示:您可以将对象映射到视图,就好像视图是一个表一样。只需将视图名指定为表名即可;只要所有 NOT NULL 字段都包含在视图和映射中,它就可以正常工作。