使用NHibernate映射一对多的最小和正确方法

mar*_*gti 8 c# nhibernate orm nhibernate-mapping

我是NHibernate和C#的新手,所以请保持温柔!

我有以下两个NHibernate实体:

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

    //Properties...
}
Run Code Online (Sandbox Code Playgroud)

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

    //Properties
}
Run Code Online (Sandbox Code Playgroud)

他们有一个one-to-many从关系EmployeeAddress (每个员工可以在自己的记录多个地址).方便地忽略多个员工可能居住在同一地址的事实.

我从内存中的对象(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>
Run Code Online (Sandbox Code Playgroud)

// 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>
Run Code Online (Sandbox Code Playgroud)
  1. 它是否正确?
  2. 如果没有,似乎我在这里缺少的是Address 实体中的一个字段,它是对相应Employee实体的引用.但如果是这样,那么我无法理解为什么这是必需的:我不需要Address从一个方向取 一个Employee,只是反过来...

Rad*_*ler 8

只需几点提示,总结一下我在使用NHibernate时发现的最合适的标准.

1)如果在persitence (DB列)中存在双向引用,也可以在代码中双向表示它.C#

换句话说,如果孩子提到父母,父母应该提到孩子.

public class Employee
{
    ...
    public virtual IList<Address> { get; set; }
}
public class Address
{
    ...
    public virtual Employee Employee { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这代表了Business Domain.地址属于Employee,Employee属于Address.

如果由于某些原因我们真的想要限制它,我们应该更喜欢protected修饰符,但仍然保留引用C#

2)使用inverse="true".这只能在我们映射双方(如上所示)时使用,并且会导致更多"预期和优化的"INSERT和UPDATE scritps

在这里阅读更多:

inverse ="true"的例子和 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" />
Run Code Online (Sandbox Code Playgroud)

3)如果我们使用inverse="true不要忘记分配关系的两个方面(在创建期间主要是关键的)

原因是:

我们指示NHibernate - 另一方(Address)负责持久关系.但要正确地做到这一点,Address需要引用Employee- 能够将其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
Run Code Online (Sandbox Code Playgroud)

我们还可以介绍一些AddAddress()隐藏这种复杂性的方法,但设置双方都是一个很好的方法.