0

私は次のマッピングを持っています:

<set name="People" lazy="true" table="ProjectPeople">
  <key column="ProjectId" />
  <composite-element class="PersonRole">
    <many-to-one name="Person" column="PersonId" cascade="save-update" not-null="true" />
    <many-to-one name="Role" column="RoleId" cascade="save-update" not-null="true"  />
  </composite-element>
</set>

さて、ドメイン内のRoleに個別のクラスを持たせたくはありません。必要なのは、Role名だけです。ただし、DBでは、ロールは別のテーブルに正規化する必要がありますRole (Id, Name)

Peopleが次のPersonRoleクラスを使用するようにマップするにはどうすればよいですか?

public class PersonRole {
    public virtual Person Person { get; set; }
    public virtual string Role { get; set; }
}

更新:賞金を追加しました。私だけでなく役立つ質問のようです。

4

4 に答える 4

2

それが不可能であるという理由だけで、あなたは実際にあなたが望む答えを得ることができません。(N)Hibernateはオブジェクトリレーショナルマッピングフレームワークであり、次の3種類のマッピング戦略をサポートします。

  • クラス階層ごとのテーブル
  • サブクラスごとのテーブル
  • 具体的なクラスごとのテーブル

formulaまた、 orなどを使用してこれから逸脱することもできますがsql-insert、ご存知のとおり、これらは最終的にはより多くの苦痛を引き起こし、Hibernateコミュニティによって奨励されておらず、コードの保守性に悪影響を及ぼします。

解決?

実際、それは非常に簡単です。のクラスを使用したくないRole。Role型のクラスを公開したくないこと、および常に型指定する必要がないことを意味していると思いますprObject.Role.Name。ただprObject.Role、文字列を返す必要があります。いくつかのオプションがあります。

  1. たとえば、PersonRoleで内部クラスを使用します。このクラスは、内部クラスでもプライベートクラスでもかまいません。メンバーフィールドを設定および更新するプロパティRoleを追加します。
  2. 内部クラスを使用します。メンバーフィールドを設定および更新するプロパティRoleを追加します。

オプション2を調べてみましょう。

// mapped to table Role, will not be visible to users of your DAL
// class can't be private, it's on namespace level, it can when it's an inner class
internal class Role 
{
    // typical mapping, need not be internal/protected when class is internal
    // cannot be private, because then virtual is not possible
    internal virtual int Id { get; private set; }
    internal virtual string Name { get; set; }
}

// the composite element
public class PersonRole
{
    // mapped properties public
    public virtual Person Person { get; set; }

    // mapped properties hidden
    internal virtual Role dbRole { get; set; }

    // not mapped, but convenience property in your DAL
    // for clarity, it is actually better to rename to something like RoleName
    public string Role     /* need not be virtual, but can be */
    { 
        get
        {
            return this.dbRole.Name;
        }
        set
        {
            this.dbRole.Name = value;    /* this works and triggers the cascade */
        }
    }
}

そして、マッピングは期待どおりに見える可能性があります。結果:クラスごとに1つのテーブルのルールに違反していません(編集:askerは明示的にそのルールに違反したいと言っており、Hibはそれをサポートしています、これは正しいです)が、オブジェクトを変更およびアクセスから非表示にしました典型的なオブジェクト指向技術を使用することによって。すべてのNH機能(カスケードなど)は引き続き期待どおりに機能します。

(N)Hibernateは、このタイプの決定に関するものです。明確さ、簡潔さ、保守性を犠牲にしたり、OOまたはORMのルールに違反したりせずに、データベースに対してよく考え抜かれた安全な抽象化レイヤーを作成する方法です。


更新(q。が閉じられた後)

このタイプの問題に対処するときに私がよく使用する他の優れたアプローチは次のとおりです。

  • 通常どおりにマッピングを作成し(つまり、テーブルごとに1つのクラス、気に入らないことはわかっていますが、最適です)、拡張メソッドを使用します。

     // trivial general example
     public static string GetFullName(this Person p)
     {
         return String.Format("{0} {1}", p.FirstName, p.LastName);
     }
    
     // gettor / settor for role.name
     public static string GetRoleName(this PersonRole pr)
     {
         return pr.Role == null ? "" : pr.Role.Name;
     }
     public static SetRoleName(this PersonRole pr, string name)
     {
         pr.Role = (pr.Role ?? new Role());
         pr.Role.Name = name;
     }
    
  • 通常どおりマッピングを作成しますが、partial classesを使用すると、クラスを好きなように「装飾」できます。利点:生成されたテーブルのマッピングを使用する場合、必要な頻度で再生成します。もちろん、部分的なクラスは別々のファイルに入れる必要があるので、「肥大化」を減らしたいというあなたの願いを考えると、これはおそらく現在のところ良いシナリオではありません。

     public partial class PersonRole
     {
         public string Role {...}
     }
    
  • おそらく最も単純です。をオーバーロードするだけで、友人や友人での使用に適していますToString()が、もちろん割り当て可能にはなりません。デフォルトでは、各エンティティクラスまたはPOCOにはとにかくオーバーロードが必要です。RoleString.FormatToString()

NHibernateを使用してこれを直接行うことは可能ですが、q。私がそれを見る時間がなくなる前に閉鎖されました(誰のせいでもありません、私には時間がありませんでした)。アプローチに同意しませんが、HibernateHBMマッピングを介してそれを行う時間が見つかった場合は更新します。最終結果が他のプログラマーにとってあまり明確でなく、全体的にあまり明確でない場合、Hibの高度な概念に取り組むのは良くありません(そのテーブルはどこに行きましたか?なぜそのテーブルのIDao抽象化がないのですか?NHibernateのベストプラクティスS#arp)。ただし、それでも演習は興味深いものです。

「ベストプラクティス」に関するコメントを検討します。通常の状況では、「テーブルごとに1つのクラス」だけでなく、テーブルごとに1つのIDaoXXX、1つのDaoConcreteXXX、および1つのGetDaoXXXを指定し、クラス/インターフェイス階層を使用して区別します。読み取り専用および読み取り/書き込みテーブル。これは、テーブルごとに最低4つのクラス/コード行です。これは通常自動生成されますが、データレイヤー(dal)への非常に明確なアクセスレイヤー(dao)を提供します。データレイヤーは、可能な限り質素に保つのが最適です。Role.Nameこれらの「ベストプラクティス」のいずれも、に移動するための拡張メソッドまたは部分メソッドの使用を妨げるものではありませんRole

これらはベストジェネラルプラクティスです。特定の特別な場所や典型的な場所では、常に可能または実行可能であるとは限りません。

于 2009-11-03T01:55:30.093 に答える
1

もし私があなただったら、モデルにRoleクラスを追加するのであれば、多対1をプリミティブ型にマップすることは不可能だと思います。

于 2009-10-28T13:25:25.307 に答える
1

個人的には、YassirのようなRoleクラスを作成します

ただし、現在の構造を使用する場合は、個人テーブルへの外部キーとロールの説明を含むビューを作成します。

新しいビューを指すように[マッピングの設定]テーブルを変更します。次に、ロールマッピングを変更して、多対1のマッピングではなくプロパティになるようにします。

ただし、このアプローチを採用すると、ビューを再表示しているため、役割を更新できないことを意味すると思います。

編集:<sql-insert>,<sql-update> and <sql-delete>カスケードすべてが機能するように、マッピングファイルに追加できる役割を更新します

于 2009-10-29T05:30:14.410 に答える
0

これは、OOの純粋主義者全体の最大のターンオフです。確かに、目標は機能するアプリケーションを用意することです。完全なクラス階層の一部のバージョンではありません。したがって、「prObject.Role」の代わりに「prObject.Role.Name」をコーディングする必要がある場合はどうでしょうか。これは、より信頼性の高いプログラムを作成するのにどのように役立ちますか?

アプリケーション設計の純粋主義者の観点から、あなたが望むものはまったく間違っています。人は複数の役割を持つことができ、役割は通常複数の人に割り当てることができます。遅延のないデータモデルが1人あたり多くの役割であるのに、なぜこのすべての問題に取り組み、1人あたり1つの役割を非現実的なクラス階層に強制するのでしょうか。

実際に「1人につき1つの役割のみ」のルールがある場合は、基礎となるデータモデルに反映させる必要があります。

于 2009-11-04T04:08:12.087 に答える