使用NHibernate进行一对多映射的最小和正确方法 [英] Minimal and correct way to map one-to-many with 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
}
,并且它们在Employee
到Address
之间具有one-to-many
关系(每个
员工可以在其记录中包含多个地址).方便地忽略
一个以上的员工可能居住在同一地址的事实.
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>
- 这正确吗?
- 如果没有,似乎我在这里缺少的是
Address
中的一个字段 实体,该实体是对相应Employee
实体的引用.但是如果 所以,那么我不明白为什么要这样做:我不需要获取Address
中的Address
,反之亦然...
- Is this correct?
- If not, it seems like what I am missing here is a field in the
Address
entity that is a reference to the correspondingEmployee
entity. But if so, then I can't understand why this is required: I don't need to fetch anAddress
from anEmployee
, only the other way round...
推荐答案
只有几个提示,总结了我在使用NHibernate时发现的最合适的标准.
Just few hints, summarizing the most suitable standards I found out when working with NHibernate.
1)如果在持久性(数据库列)中存在双向引用,请以C#
代码双向表示>也是如此.
1) If there is a bi-directional reference in persitence (DB column), express it in C#
code bi-directional as well.
换句话说,如果 child 引用了 parent ,则 parent 应当引用了 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 inC#
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)如果一个对象(in our case 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:
映射摘要中的规则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保留在地址表的列中.
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.
因此,这应该是创建新地址的标准代码
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屋!