使用 NHibernate 映射一对多的最小且正确的方法
Minimal and correct way to map one-to-many with NHibernate
我是 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
}
并且他们有从 Employee
到 Address
的 one-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>
- 这是正确的吗?
- 如果不是,那么我在这里缺少的似乎是
Address
中的一个字段
实体是对相应 Employee
实体的引用。但是如果
所以,我不明白为什么需要这样做:我不需要获取
Address
来自 Employee
,只是反过来...
你应该在one-to-many
关系中添加级联all-delete-orphan
,如果你删除Employee
地址也会被删除。
如果您不需要 Employee
引用,请创建一个 inverse=false
关系,如下所示:here
只是一些提示,总结了我在使用 NHibernate 时发现的最合适的标准。
1) 如果persitence(DB列)中有bi-directional引用,则用[=13表示=]代码bi-directional。
换句话说,如果child引用了parent,parent 应该参考 child.
public class Employee
{
...
public virtual IList<Address> { get; set; }
}
public class Address
{
...
public virtual Employee Employee { get; set; }
}
这按原样表示业务域。地址属于员工,员工属于地址。
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"
。这只能在我们映射两侧(如上所述)时使用,并且会导致更多 "expected and optimized" INSERT 和 UPDATE scritps
在此处阅读更多内容:
inverse = “true” example and explanation 作者:mkyong
3) 几乎无处不在地使用批量提取映射。这将避免在查询期间出现 1 + N 问题。阅读更多:
few details about batch fetching
4) 以防万一,那个 object (in our case Employee)
是 root
(另一个没有它就没有多大意义) - 使用级联。阅读更多:
映射片段中的规则 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
不要忘记分配关系的两边 (在创建过程中主要是关键)
原因是:
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.
所以这应该是创建新地址的标准代码
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# 的新手,所以请多关照!
我有以下两个 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
}
并且他们有从 Employee
到 Address
的 one-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>
- 这是正确的吗?
- 如果不是,那么我在这里缺少的似乎是
Address
中的一个字段 实体是对相应Employee
实体的引用。但是如果 所以,我不明白为什么需要这样做:我不需要获取Address
来自Employee
,只是反过来...
你应该在one-to-many
关系中添加级联all-delete-orphan
,如果你删除Employee
地址也会被删除。
如果您不需要 Employee
引用,请创建一个 inverse=false
关系,如下所示:here
只是一些提示,总结了我在使用 NHibernate 时发现的最合适的标准。
1) 如果persitence(DB列)中有bi-directional引用,则用[=13表示=]代码bi-directional。
换句话说,如果child引用了parent,parent 应该参考 child.
public class Employee
{
...
public virtual IList<Address> { get; set; }
}
public class Address
{
...
public virtual Employee Employee { get; set; }
}
这按原样表示业务域。地址属于员工,员工属于地址。
If we for some reasons really want to restrict that, we should rather
protected
modifier, but still keep the reference inC#
2) 使用inverse="true"
。这只能在我们映射两侧(如上所述)时使用,并且会导致更多 "expected and optimized" INSERT 和 UPDATE scritps
在此处阅读更多内容:
inverse = “true” example and explanation 作者:mkyong
3) 几乎无处不在地使用批量提取映射。这将避免在查询期间出现 1 + N 问题。阅读更多:
few details about batch fetching
4) 以防万一,那个 object (in our case Employee)
是 root
(另一个没有它就没有多大意义) - 使用级联。阅读更多:
映射片段中的规则 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
不要忘记分配关系的两边 (在创建过程中主要是关键)
原因是:
we instruct NHibernate - the other side (
Address
) is responsible for persisting relation. But to do that properly, thatAddress
needs to have reference toEmployee
- to be able to persist its ID into its column in Address table.
所以这应该是创建新地址的标准代码
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()
这样的方法来隐藏这种复杂性,但是设置两侧是一个很好的做法。