web-dev-qa-db-ja.com

NHibernateで1対多をマッピングするための最小限で正しい方法

NHibernateとC#は初めてなので、優しくしてください。

私は次の2つのNHibernateエンティティを持っています:

Employee
{
    private long _id;
    private String _name;
    private String _empNumber;
    private IList<Address> _addresses;

    //Properties...
}

そして

Address
{
    private long _id;
    private String _addrLine1;
    private String _addrLine2;
    private String _city;
    private String _country;
    private String _postalCode;

    //Properties
}

そして彼らはone-to-manyEmployeeからAddressへの関係(各従業員はレコードに複数のアドレスを持つことができます)。複数の従業員が同じ住所に住んでいる可能性があるという事実を無視すると便利です。

私はこれをメモリ内のオブジェクト(NHibernateエンティティ)の観点から理解しています。私が苦労しているのはマッピングファイルです(そしてここでは簡単な例を取り上げています)。これは私がこれまでに思いついたものです:

// Intentionally left out XML and <hibernate-mapping> 
// Mappings for class 'Employee'. -->
<class name="Employee" table="Employees">
    <id name="ID">
        <generator class="native">
    </id>

    <property name="Name" />
    <property name="EmpNumber" />

    <bag name="Addresses">
        <key column="AddressId" />
        <one-to-many class="Address" />
    </bag>
</class>

そして

// Intentionally left out XML and <hibernate-mapping> .
// Mappings for class 'Address'
<class name="Address" table="Addresses">
    <id name="ID">
        <generator class="native">
    </id>

    // Intentionally left out name="Employee" 
    // as I don't have corresponding field in Address entity.
    <many-to-one class="Employee" column="EmployeeID" cascade="all" />

    <property name="AddrLine1" />
    <property name="AddrLine2" />
    <property name="City" />
    <property name="Country" />
    <property name="PostalCode" />
</class>
  1. これは正しいです?
  2. そうでない場合、ここで欠落しているのは、対応するAddressエンティティへの参照であるEmployeeエンティティのフィールドのようです。しかし、そうであれば、なぜこれが必要なのか理解できません。AddressからEmployeeをフェッチする必要はなく、その逆だけです...
8
markvgti

NHibernateを使用して作業したときに見つけた最も適切な標準を要約した、ほんの少しのヒント。

1)persitence(DB列)双方向参照がある場合は、 _C#_コード双方向も同様です。

言い換えると、を参照している場合、への参照。

_public class Employee
{
    ...
    public virtual IList<Address> { get; set; }
}
public class Address
{
    ...
    public virtual Employee Employee { get; set; }
}
_

これは、ビジネスドメインをそのまま表します。住所は従業員に属し、従業員は住所に属します。

何らかの理由でそれを本当に制限したい場合は、protected修飾子を使用する必要がありますが、参照は_C#_に保持します。

2)_inverse="true"_を使用します。これは、(上記のように)両側をマッピングした場合にのみ使用でき、より「期待され最適化された」INSERTおよびUPDATEスクリプトにつながります。

詳細はこちら:

逆=「真の」例と説明 mkyong

3)ほぼすべての場所でバッチフェッチマッピングを使用します。これにより、クエリ中の1 + Nの問題が回避されます。続きを読む:

バッチフェッチに関する詳細はほとんどありません

4)一方のオブジェクト_(in our case Employee)_がrootである場合(もう一方はそれなしではあまり意味がありません)-カスケードを使用します。続きを読む:

nhibernate-親を更新して子を作成しますか、それとも明示的に作成しますか?

マッピングスニペットのルール2、3、4:

_<class name="Employee" ... batch-size="25">
  ...
  <bag name="Addresses"
       lazy="true" 
       inverse="true" 
       batch-size="25" 
       cascade="all-delete-Orphan" >
    // wrong! This columns is the same as for many-to-one
    //<key column="AddressId" />
    // it is the one column expressing the relation
    <key column="EmployeeId" />
    <one-to-many class="Address" />
  </bag>

<class name="Address" ... batch-size="25">
  ...
  <many-to-one not-null="true" name="Employee" column="EmployeeID" />
_

3)_inverse="true_を使用する場合は、関係の両側を割り当てることを忘れないでください(作成時に最も重要)

その理由は:

nHibernateに指示します-反対側Addressは関係を維持する責任があります。ただし、これを適切に行うには、そのAddressEmployeeを参照している必要があります。これにより、IDをAddressテーブルの列に保持できます。

したがって、これは新しいアドレスを作成するための標準コードである必要があります

_Employee employee = ... // load or create new
Address address = new Address 
{
    ...
    Employee = employee, // this is important
};
Employee.Addresses.Add(address);
session.SaveOrUpdate(employee);  // cascade will trigger the rest
_

この複雑さを隠すAddAddress()のようなメソッドを導入することもできますが、両側を設定することは良い方法です。

8
Radim Köhler

カスケードall-delete-Orphanone-to-many関係に追加する必要があります。Employeeを削除すると、アドレスも削除されます。

Employee参照が必要ない場合は、次のようなinverse=false関係を作成します。 ここ

1
Najera