【问题标题】:Minimal and correct way to map one-to-many with NHibernate使用 NHibernate 映射一对多的最小且正确的方法
【发布时间】:2015-07-11 04:25:14
【问题描述】:

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

我有以下两个 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
}

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

我从内存中对象的角度理解这一点(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,只是反过来...

【问题讨论】:

  • 在我看来,如果地址表中的地址是唯一的,那么您在这里需要多对多关系而不是一对多关系。这意味着存储员工 ID 和地址 ID 的多对多表。这样,一名员工可以与一个或多个地址相关联,并且该地址可以属于超过 1 名员工。
  • @ColeW 我不希望地址属于多个员工。这是故意的。
  • 如果是这样,您需要在地址表中添加一个员工 ID 以识别哪些地址属于哪些员工。因此,地址表中可能存在多个具有相同员工 ID 的地址。这就是 nhibernate 如何知道如何将 private IList&lt;Address&gt; _addresses; 填充到 Employee 对象中。我还建议您将Employee 对象作为Address 对象的一部分,因为导航到关系的双方通常会派上用场,即使从业务逻辑的角度来看它不一定总是有意义。
  • 你为什么要从地址而不是员工添加级联?
  • @ColeW a) 你能添加这个作为答案吗? b) 有没有办法在地址数据库表中添加 EmployeeID 列Address 实体中没有Employee 引用?

标签: c# nhibernate orm nhibernate-mapping


【解决方案1】:

只是一些提示,总结了我在使用 NHibernate 时发现的最合适的标准。

1)如果持久性中有双向引用(DB列),用C#代码表示双向 也是。

换句话说,如果 child 引用了 parent,则 parent 应该引用 child

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 脚本

在这里阅读更多:

inverse = “true” example and explanation by mkyong

3) 几乎在任何地方都使用批量获取映射。这将避免在查询期间出现 1 + N 问题。阅读更多:

few details about batch fetching

4) 如果一个对象 (in our case Employee)root (没有它另一个就没有多大意义) - 使用级联。阅读更多:

nhibernate - Create child by updating parent, or create explicitly?

映射 sn-ps 中的规则 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) 负责保持关系。但要正确执行此操作,Address 需要引用 Employee - 才能将其 ID 持久保存到地址表中的列中。

所以这应该是创建新地址的标准代码

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()这样的方法来隐藏这种复杂性,但是设置两边是一个很好的方法。

【讨论】:

  • NHibernate 在 C# 中的工作方式是否与 Hibernate 在 Java 中的工作方式完全相同?我问是因为 mkyong.com 上关于 inverse="true" 的文章使用的是 Java 示例。
  • 这段代码是一样的(从Java移植到C#)。最重要的是“逆灵”。因此,即使会有任何细微的差异,但概念是相同的。希望它有所帮助 ;) 享受强大的 NHibernate ;)
【解决方案2】:

你应该在one-to-many关系中添加级联all-delete-orphan,如果你删除Employee,地址也会被删除。

如果您不需要 Employee 引用,请创建一个 inverse=false 关系,如下所示:here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-18
    • 1970-01-01
    • 1970-01-01
    • 2011-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多