警告 - NHibernateの新機能です。私はこの質問が簡単だと分かっています - そして、私は単純な答えがあると確信していますが、私はこの車で私の車輪をしばらく回転させています。私は本当に構造的に変更することはできませんレガシーDBを扱っています。私は、顧客が受け入れた支払計画をリストアップした詳細テーブルを持っています。各支払いプランには、参照テーブルにリンクして、プランの条件、条件などを取得するIDがあります。オブジェクトモデルでは、AcceptedPlanクラスとPlanクラスがあります。もともと、NHibernateでこの関係をモデル化するために、ディテール・テーブルからrefテーブルへの多対1の関係を使用しました。私はまた、PlanクラスからAcceptedPlanクラスまで反対方向に1対多の関係を作成しました。私が単にデータを読んでいる間、これは問題ありませんでした。プランの詳細を読むためのAcceptedPlanクラスのプロパティだったPlanオブジェクトに行くことができました。私の問題は、詳細テーブルに新しい行を挿入しなければならないときに発生しました。私の読んだところでは、新しい子オブジェクトを作成する唯一の方法は、それを親オブジェクトに追加してからセッションを保存することです。しかし、新しい詳細レコードを作成するたびに、新しい親Planオブジェクトを作成する必要はありません。これは不必要なオーバーヘッドのようです。私が間違ったやり方でこのことをやっているのか誰にも分かりますか?レガシーDBを扱うときにNHibernateで多対1の関係をモデル化する最も良い方法は?
答えて
私は、その論理的な親を含む子オブジェクトを持っているから離れてしまいます。非常に面倒で非常に再帰的です。私はあなたがそのようなことをする前にドメインモデルをどのように使用しようとしているかを見ていきたいと思います。あなたはテーブルにID参照を簡単に置いて、マッピングされていないままにしておくことができます。
ここに2つの例があります。正しい方向に向けるかもしれません。テーブル名などを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>
次のように私はこれをモデル化するために取ると思いアプローチがある:
Customerオブジェクトは、顧客が承認した計画を表しているICollection <PaymentPlan> PaymentPlansが含まれています。
お客様へのPaymentPlanは、詳細テーブルを使用してどのPaymentPlansにマップされているかを示す顧客IDを作成するバッグを使用してマップされます。 cascade all-delete-orphanを使用すると、顧客が削除された場合、詳細のエントリと顧客が所有するPaymentPlansの両方が削除されます。
PaymentPlanオブジェクトには、支払計画の条件を表すPlanTermsオブジェクトが含まれています。
PlanTermsは、関連するPlanTermsオブジェクトへの参照をPaymentPlanに挿入する多対1マッピングカスケードセーブアップデートを使用してPaymentPlanにマップされます。
このモデルを使用すると、PlanTermsを独立して作成することができます。新しいPaymentPlanを顧客に追加すると、関連するPlanTermsオブジェクトに新しいPaymentPlanオブジェクトを作成し、それを関連する顧客のコレクションに追加します。最後に、Customerを保存して、nhibernateが保存操作をカスケードするようにします。
顧客オブジェクト、PaymentPlanオブジェクト、およびPlanTermsオブジェクトは、特定のPlanTerm(計画テーブル)をすべて遵守するPaymentPlans(詳細テーブル)のインスタンスを所有するCustomer(顧客テーブル)インスタンスで終わります。
必要に応じてマッピング構文の具体的な例がいくつかありますが、独自のモデルを使って作業するのが最善であり、特定の例を提供するのに十分な情報がデータベーステーブルにありません。
NHibernateの経験が限られている可能性があるのかどうかはわかりませんが、Detailテーブルに直接マップされているため、詳細のプロパティを持つBaseDetailクラスを作成できます。
次に、追加の親プランオブジェクトを持つBaseDetailクラスから継承する2番目のクラスを作成します。詳細行を作成してPlanIdを割り当てたいときにBaseDetailクラスを作成できますが、継承されたDetailクラスを使用することができる親プランオブジェクトで完全なDetailレコードを設定します。
これは意味があるのかどうかわかりませんが、私に知らせてください、そして私はさらに明確にします。
ここで問題になるのは、AcceptedOfferオブジェクトにPlanオブジェクトが含まれ、PlanオブジェクトにAcceptedOffersオブジェクトを含むAcceptedOffersコレクションが含まれているように見えることです。顧客と同じこと。オブジェクトがお互いの子であるという事実があなたの問題の原因です。
同様に、AcceptedOfferコンプレックスには2つの責任があります。これは、プランに含まれているオファーを示しています。これは、顧客による受諾を示します。それは単一責任原則に違反します。
プランの下にあるオファーと、顧客が受け入れたオファーを区別する必要があるかもしれません。
- 状態を持たない別のOfferオブジェクトを作成します。たとえば、顧客を持たず、ステータスもありません.OfferIdのみを持ちます。その属性として属しているプラン。
- プランオブジェクトにオファーコレクションが含まれるように変更します(オファーをその文脈で受け入れる必要はありません)。
- 最後に、Offer、Customer、およびStatusが含まれるようにAcceptedOfferオブジェクトを変更します。お客様は変わりません。
これは、NHibernateのマッピングとオブジェクトの保存の問題を十分に解消すると思います。 :)
NHibernateで役に立つかもしれないヒント:ビューがテーブルであるかのようにオブジェクトをビューに対してマップすることができます。ビュー名をテーブル名として指定するだけです。すべての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>
はおそらく(私だけのコレクションのための例示のマッピングを行ってきた、あなたは他のプロパティを追加する必要があります)トリックを行う必要があります。