假设我有一个这样的数据库:
这是为了提供角色菜单权限。
请注意, User
- 表与Permission
- 表没有直接关系。
那么我该如何将这个类映射到数据库表?
class User
{
public int ID { get; set; }
public string Name { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public bool? IsActive { get; set; }
public IList<Role> RoleItems { get; set; }
public IList<Permission> PermissionItems { get; set; }
public IList<string> MenuItemKeys { get; set; }
}
这意味着,
(1)每个用户都有Role
个。
(2)每个用户都有一些Permission
s(取决于Role
s。)
(3)每个用户都有一些允许的MenuItemKey
s(根据Permission
s。)
我的User.hbm.xml
应该怎么样?
答案 0 :(得分:3)
在应用程序中可能会大量访问角色和权限。它们很可能位于二级缓存中,这意味着我们可以期望有效地迭代User.RoleItems和Role.Permissions。
这样做的好处是我们通常可以期望在迭代这些集合时不执行任何查询。
您可以按如下方式映射类。
属性User.PermissionItems和User.MenuItemKeys派生自持久性实体,因此不会出现在映射中。
<class name="User" table="user">
<id name="ID">
<generator class="native"/>
</id>
<property name="Name"/>
<property name="Username"/>
<property name="Password"/>
<property name="IsActive"/>
<bag name="RoleItems" table="userrole" lazy="true">
<key column="userid" />
<many-to-many class="Role" column="roleid"/>
</bag>
</class>
<class name="Role" table="role">
<id name="ID">
<generator class="native"/>
</id>
<property name="RoleName"/>
<property name="IsActive"/>
<bag name="Permissions" table="permission">
<key column="roleid" />
<one-to-many class="Permission"/>
</bag>
</class>
<class name="Permission" table="permission">
<id name="ID">
<generator class="native"/>
</id>
<property name="MenuItemKey"/>
</class>
我会将您在User上的2个额外列表添加到派生枚举中。如果它们是列表,则没有明确的方法插入它们,因为您无法知道该值适用于哪个角色。此外,角色不归用户所有。
更新:现在使用Diego的这些属性的改进版本。
class User
{
public virtual IEnumerable<Permission> PermissionItems
{
get {
return RoleItems.SelectMany(role => role.PermissionItems);
}
}
public virtual IEnumerable<string> MenuItemKeys
{
get {
return RoleItems.SelectMany(role => role.PermissionItems,
(role, permission) => permission.MenuItemKey);
}
}
}
答案 1 :(得分:1)
这是一个链接:Chapter 6. Collection mapping 这是另一个有用的链接:Chapter 7. Association Mappings
修改强>
在整整一个晚上进行了研究之后,我得出了以下结论:
不要使用异国情调的关联映射。
真正的多对多关联的良好用途很少见。大多数情况下,您需要存储在“链接表”中的其他信息。在这种情况下,使用两个一对多关联到中间链接类要好得多。事实上,我们认为大多数协会都是一对多和多对一,你在使用任何其他协会风格时应该小心,并问自己是否真的有必要。
我可能会理解,考虑到上下文暴露了直接从用户访问权限或MenuItemKey值的好处,我建议采用以下解决方案:
创建视图udvUsersPermissions AS
选择UR.UserID,P.ID为N'ID',P.MenuItemKey
来自用户U
INNER JOIN UsersRoles UR ON UR.UserID = U.ID
INNER JOIN角色R ON R.ID = UR.RoleID
内部联接权限P ON P.RoleID = R.ID
GO
然后,根据您的User.hbm.xml文件映射它:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="User" table="Users">
<id name="Id" column="ID">
<generator class="identity"/>
</id>
<property name="Name" length="100"/>
<property name="UserName" length="10" not-null="true"/>
<property name="Password" length="10" not-null="true"/>
<property name="IsActive" not-null="true"/>
<list name="Roles" table="UsersRoles" access="private-property" lazy="true">
<key column="UserID" foreign-key="FK_UR_U"/>
<list-index column="UserID"/>
<many-to-many class="Role" column="RoleID" />
</list>
<!-- Here mapping Permissions granted to User. -->
<list name="Permissions" table="udvUsersPermissions" lazy="true">
<key column="UserID"/>
<list-index column="MenuItemKey"/>
<many-to-many column="ID" class="Permission"/>
</list>
</class>
</hibernate-mapping>
在这里,我会告诉你有关subselect解决方案,以防它按照我没想到的方式工作。
<list name="Permissions" lazy="true">
<subselect> <!-- see section 7.6, Chapter 7 - Association mappings -->
select U.ID, P.ID, P.MenuItemKey
from Users U
inner join UsersRoles UR ON UR.UserID = U.ID
inner join Roles R ON R.ID = UR.RoleID
inner join Permissions P ON P.RoleID = R.ID
group by U.ID, P.ID, P.MenuItemKey
order by P.MenuItemKey
</subselect>
<key column="U.ID"/>
<list-index column="P.MenuItemKey"/>
<many-to-many class="Permission" column="P.ID"/>
</list>
现在,我希望我带来了足够的细节,以便它可以帮助您实现您想要做的事情或者实现目标。 =)
答案 2 :(得分:1)
Lachlan发布的地图是最佳选择。您 可以 使用执行每个集合的所有联接的查询,但这样做只会出于实际目的而读取。
有一种更简单的方法来实现属性代码,但这可能有助于您做出决定:
public IEnumerable<Permission> PermissionItems
{
get
{
return RoleItems.SelectMany(role => role.PermissionItems);
}
}
public IEnumerable<string> MenuItemKeys
{
get
{
return RoleItems.SelectMany(role => role.PermissionItems,
(role, permission) => permission.MenuItemKey);
}
}