日期:2009-01-07  浏览次数:20385 次

在数据库的多对多关系中, 实现方法是通过一个额外的关联表将两个主表关联起来,关联表中只保存两个主表的主健,显然, 主表与关联表是一对多的关系. 这样两个主表就通过这个关联表构成了一个多对多的关系.
典型的例子就是用户和权限了, 每个用户可以有多个权限, 而每个权限也可以分配给多个用户. 通过一个用户权限表就可以实现这样的要求.


因为关联表并没有其它任何信息, 所以在NH中它不能算是一个持久对象, 没必要像处理one-to-many的情况那样.在NH中, 通过值集合映射来处理这种情况.

下面以用户/权限为例再看看实际的处理情况.

测试代码

[Test]
public void TestUserPermission() {
User u = new User();
u.Name = "test name";
u.Password = "test password";
u.AddPermission( 10 );

session.Save(u);
session.Close();

User u2 = session.Load( typeof(User), u.UserId );
Assertion.AssertNotNull( "add permission fail!", u2.Permissions[ 10 ] );

u2.AddPermission( 20 );
u2.RemovePermission( 10 );

session.Save(u2);
session.Close();

User u3 = session.Load( typeof(User), u.UserId );
Assertion.AssertNull( "remove permission fail!", u3.Permission[ 10 ] );
Assertion.AssertNotNull( "add permission fail!", u3.Permission[ 20 ] );
}
以上测试代码中session的相关操作请查看相关文档。
在这里给User类定义了两个操作权限的方法: AddPermission和RemovePermission.

类定义

public class User {

public User() {
}

public void AddPermission( int permissionId ) {
permissions[ permissionId ] = dummyObject;
}

public void RemovePermission( int permissionId ) {
permissions.Remove( permissionId );
}

#region O/R Mapping Fields.

private int userId;
public int UserId
{
get { return userId; }
set { userId = value; }
}

private string name;
public string Name
{
get { return name; }
set { name = value; }
}

private string password;
public string Password
{
get { return password; }
set { password = value; }
}

private IDictionary permissions = new Hashtable();
public IDictionary Permissions
{
get { return permissions; }
set { permissions = value; }
}

#endregion

private readonly object dummyObject = new object();
}
在User类中,定义了一个Permissions集合用来保存用户的权限. 因为使用了IDictionary, 所以也定义了一个哑对象.

映射文件

<class name="User, AssemblyName " table="Users">
<id name="UserId" column="user_id" type="Int32" unsaved-value="0">
<generator class="identity"/>
</id>

<property name="Name" column="name" type="String"/>
<property name="Password" column="password" type="String"/>

<set name="Permissions" table="UserPermissions">
<key column="user_id"/>
<element column="permission_id" type="Int32"/>
</set>
</class>
在set定义中, 指定值集合的名称,以及关联表的名称, 再指定key值, 也即在关联表的健, 一般是主健.
element元素指定产生值集合的列和类型.

值集合的开销远小于对象集合的开销, 在有些时候, 可以考虑将one-to-many简化为集集合, 这样我们只取得many的id值, 在需要访问many对象时再载入, 这也算是延迟加载的一个变相实现吧.