使用 NHibernate 映射一对多的最小且正确的方法 [英] Minimal and correct way to map one-to-many with NHibernate

查看:18
本文介绍了使用 NHibernate 映射一对多的最小且正确的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am new to NHibernate and C#, so please be gentle!

我有以下两个 NHibernate 实体:

I have the following two NHibernate entities:

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
}

并且它们具有从 EmployeeAddress (每个员工的记录中可以有多个地址).方便地忽略多个员工可能居住在同一地址这一事实.

and they have a one-to-many relationship from Employee to Address (each employee can have multiple addresses in their record). Conveniently ignoring the fact that more than one employee may reside at the same address.

我是从内存中的对象(NHibernate实体).我正在努力解决的是映射文件(我正在使用简单的例子在这里).这是我目前想到的:

I understand this from the perspective of objects in memory (the NHibernate entities). What I am struggling with are the mapping files (and I am taking an easy example here). This is what I have come up with so far:

// 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,反之亦然...
  1. Is this correct?
  2. If not, it seems like what I am missing here is a field in the Address entity that is a reference to the corresponding Employee entity. But if so, then I can't understand why this is required: I don't need to fetch an Address from an Employee, only the other way round...

推荐答案

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

Just few hints, summarizing the most suitable standards I found out when working with NHibernate.

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

1) If there is a bi-directional reference in persitence (DB column), express it in C# code bi-directional as well.

换句话说,如果 child 引用 parentparent 应该引用 child.

Other words, if a child has reference to parent, parent should have reference to child.

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

这代表业务领域.地址属于雇员,雇员属于地址.

This represents Business Domain as is. Address belongs to Employee and Employee belongs to Address.

如果我们出于某些原因真的想限制它,我们应该使用 protected 修饰符,但仍然保留 C#

If we for some reasons really want to restrict that, we should rather protected modifier, but still keep the reference in C#

2) 使用 inverse="true".仅当我们映射双方时才可以使用(如上所述),并且将导致更多预期和优化"的 INSERT 和 UPDATE 脚本

2) Use inverse="true". This could be used only if we mapped both sides (as above), and will lead to more "expected and optimized" INSERT and UPDATE scritps

在此处阅读更多信息:

inverse = true" 示例和解释mkyong

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

3) Use batch fetching mapping almost everwhere. That will avoid 1 + N issues during querying. Read more:

关于批量抓取的一些细节

4) 如果一个对象 (在我们的例子中是 Employee)root (没有它另一个就没有多大意义) - 使用级联.阅读更多:

4) In case, that one object (in our case Employee) is root (the other does not make so much sense without it) - use cascading. Read more:

nhibernate - 通过更新父级来创建子级,还是显式创建?

映射片段中的规则 2、3、4:

Rules 2,3,4 in a mapping snippets:

<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 不要忘记分配关系的两边(在创建过程中最重要)

3) if we use inverse="true do not forget to assign both sides of relation (mostly critical during creation)

原因是:

我们指示NHibernate - 另一端(Address) 负责保持关系.但是要正确地做到这一点,Address 需要引用 Employee - 以便能够将其 ID 持久化到 Address 表中的列中.

we instruct NHibernate - the other side (Address) is responsible for persisting relation. But to do that properly, that Address needs to have reference to Employee - to be able to persist its ID into its column in Address table.

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

So this should be the standard code to create new 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() 这样的方法来隐藏这种复杂性,但是设置两边是一个很好的方法.

We can also introduce some method like AddAddress() which will hide this complexity, but setting both sides is a good prectice.

这篇关于使用 NHibernate 映射一对多的最小且正确的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆