如何在NHibernate(不是FluentNHibernate)中映射这个类?

时间:2010-03-04 20:00:47

标签: nhibernate nhibernate-mapping

假设我有一个这样的数据库:

alt text

这是为了提供角色菜单权限。

请注意, 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应该怎么样?

3 个答案:

答案 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

修改

在整整一个晚上进行了研究之后,我得出了以下结论:

  1. 考虑NHibernate Best Practices,你想做的事情是不好的;
  2.   

    不要使用异国情调的关联映射。
      真正的多对多关联的良好用途很少见。大多数情况下,您需要存储在“链接表”中的其他信息。在这种情况下,使用两个一对多关联到中间链接类要好得多。事实上,我们认为大多数协会都是一对多和多对一,你在使用任何其他协会风格时应该小心,并问自己是否真的有必要。

    1. 作为一种编程哲学,我更喜欢保持简单而不是编写聪明的代码,即使我不会理解我在一段时间后所写的内容;
    2. 另外,我甚至考虑使用关联映射的子查询元素,如果我找到了一种方法来参数化它,如果可行的话,它会起作用,但它似乎不允许我用这个用户参数化查询实例的Id属性值;
    3. 在一个精心设计的OO模型的视觉中,一个孩子知道他父母的财产是好的,但父母访问一个孩子的财产没有任何感觉 - 设计气味;
    4. 我可能会理解,考虑到上下文暴露了直接从用户访问权限或MenuItemKey值的好处,我建议采用以下解决方案:

      • 创建一个用户定义的数据视图,该视图将保存与通过用户所属的角色获得的MenuItemKey Permission属性相关的值,如下所示:
    5.   

      创建视图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);
    }
}