レガシー DB を扱う場合、NHibernate で多対 1 の関係をモデル化する最良の方法は?

StackOverflow https://stackoverflow.com/questions/10915

  •  08-06-2019
  •  | 
  •  

質問

警告 - 私は NHibernate を初めて使用します。この質問が単純であることはわかっています。そして、簡単な答えがあると確信していますが、私はこの問題についてしばらく考え続けてきました。私は構造的に変更できないレガシーデータベースを扱っています。顧客が受け入れた支払いプランをリストした詳細テーブルがあります。各支払いプランには ID があり、プランの利用規約などを取得するための参照テーブルにリンクされます。私のオブジェクト モデルには、AcceptedPlan クラスと Plan クラスがあります。当初、私は詳細テーブルから参照テーブルに戻る多対 1 の関係を使用して、NHibernate でこの関係をモデル化しました。また、Plan クラスから AcceptedPlan クラスへの逆方向の 1 対多の関係も作成しました。単にデータを読んでいるときはこれで問題ありませんでした。AcceptedPlan クラスのプロパティである Plan オブジェクトに移動して、プランの詳細を読み取ることができました。問題は、詳細テーブルへの新しい行の挿入を開始する必要があるときに発生しました。私の読んだところによると、新しい子オブジェクトを作成する唯一の方法は、それを親オブジェクトに追加してからセッションを保存することのようです。ただし、新しい詳細レコードを作成するたびに、新しい親 Plan オブジェクトを作成する必要はありません。これは不必要なオーバーヘッドのように思えます。私がこれについて間違った方法で取り組んでいるかどうか誰かが知っていますか?

役に立ちましたか?

解決

私は、論理的な親を含む子オブジェクトを持つことは避けたいと思います。それを行うと、すぐに非常に面倒になり、非常に再帰的になる可能性があります。そのようなことをする前に、ドメイン モデルをどのように使用するつもりなのかを調べてみたいと思います。ID 参照をテーブル内に保持し、マッピングしないままにしておくのは簡単です。

ここでは、正しい方向に導く可能性のあるマッピングの例を 2 つ示します。テーブル名などをアドリブする必要がありましたが、役立つかもしれません。おそらく、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 がどの PaymentPlan にマッピングされるかを確立するバッグを使用してマッピングされます。cascade all-delete-orphan を使用すると、顧客が削除された場合、詳細のエントリと顧客が所有していた PaymentPlan の両方が削除されます。

PaymentPlan オブジェクトには、支払い計画の条件を表す PlanTerms オブジェクトが含まれています。

PlanTerms は、関連する PlanTerms オブジェクトへの参照を PaymentPlan に挿入するだけの多対 1 マッピング カスケード保存更新を使用して PaymentPlan にマップされます。

このモデルを使用すると、PlanTerms を独立して作成し、新しい PaymentPlan を顧客に追加するときに、関連する PlanTerms オブジェクトを渡す新しい PaymentPlan オブジェクトを作成し、それを関連する Customer のコレクションに追加できます。最後に Customer を保存し、nhibernate で保存操作をカスケードさせます。

最終的に、Customer オブジェクト、PaymentPlan オブジェクト、PlanTerms オブジェクトが作成され、Customer (顧客テーブル) が PaymentPlans (詳細テーブル) のインスタンスを所有し、これらはすべて特定の PlanTerms (プラン テーブル) に準拠します。

必要に応じて、マッピング構文のより具体的な例をいくつか用意していますが、おそらく独自のモデルを使用して作業するのが最善であり、具体的な例を提供するのに十分なデータベース テーブルの情報がありません。

おそらく私の NHibernate の経験が限られているためかどうかはわかりませんが、Detail テーブルに直接マッピングされる Detail のプロパティのみを持つ BaseDetail クラスを作成していただけますか。

次に、追加の親プラン オブジェクトを持つ BaseDetail クラスを継承する 2 番目のクラスを作成します。これにより、Detail 行を作成してそれに PlanId を割り当てるだけで、完全な Detail を設定する必要がある場合に BaseDetail クラスを作成できます。親プラン オブジェクトでレコードを作成すると、継承された Detail クラスを使用できます。

意味があるかどうかはわかりませんが、お知らせください。さらに詳しく説明します。

ここで発生している問題は、AcceptedOffer オブジェクトに Plan オブジェクトが含まれており、Plan オブジェクトに AcceptedOffer オブジェクトを含む AcceptedOffers コレクションが含まれているように見えることだと思います。顧客についても同様です。オブジェクトが互いの子であるという事実が問題の原因だと思います。

同様に、AcceptedOffer が複雑になるのは、次の 2 つの責任があるためです。それはプランに含まれているオファーを示し、顧客が受け入れたことを示します。それは単一責任原則に違反します。

プランに基づいたオファーと、顧客が受け入れたオファーを区別する必要がある場合があります。それで、私がやろうとしていることは次のとおりです。

  1. 状態を持たない別個の Offer オブジェクトを作成します。たとえば、顧客もステータスも持たず、属性として OfferId とそれが属するプランのみを持ちます。
  2. Plan オブジェクトを変更して Offers コレクションを含めます (コンテキスト内で受け入れられたオファーがある必要はありません)。
  3. 最後に、Offer、Customer、Status が含まれるように AcceptedOffer オブジェクトを変更します。顧客はそのままです。

これで、NHibernate マッピングとオブジェクト保存の問題が十分に解決されると思います。:)

NHibernate で役に立つ (または役に立たない) ヒント:ビューがテーブルであるかのように、オブジェクトをビューに対してマッピングできます。ビュー名をテーブル名として指定するだけです。すべての NOT NULL フィールドがビューとマッピングに含まれている限り、正常に機能します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top