NHibernate CompositeId =>太多查询

问题描述:

我目前正在编写一个访问遗留数据库的应用程序。 我使用nhibernate作为我的ORM。NHibernate CompositeId =>太多查询

DB中有三个表格表示(几乎)经典的多对多关系。 由于链接表还包含额外数据的视觉差异。

的代码看起来是这样的:

public class User 
{ 
    public virtual string Login { get; set;} 
    public virtual string Name { get; set;} 
    public virtual IList<UserRole> UserRoles { get; set;} 
} 

public class Role 
{ 
    public virtual int Id { get; set;} 
    public virtual string Description { get; set;} 
    public virtual IList<UserRole> UserRoles { get; set;} 
} 

public class UserRole 
{ 
    public virtual User User { get; set;} 
    public virtual Role Role { get; set;} 
    public virtual bool Active { get; set;} 
} 

public class UserMap : ClassMap<User> 
{ 
    public UserMap() 
    { 
    Table("Users"); 
    Id(u => u.Login).Column("USER_LOGIN").GeneratedBy.Assigned(); 
    Map(u => u.Name).Column("USER_NAME"); 

    HasMany(u => u.UserRoles).KeyColumn("USER_LOGIN"); 
    } 
} 

public class RoleMap : ClassMap<Role> 
{ 
    public RoleMap() 
    { 
    Table("Roles"); 
    Id(r => r.Id).Column("ROLE_ID").GeneratedBy.Assigned(); 
    Map(r => r.Description).Column("ROLE_DESCR"); 

    HasMany(r => r.UserRoles).KeyColumn("ROLE_ID"); 
    } 
} 


public class UserRoleMap : ClassMap<UserRole> 
{ 
    public UserRoleMap() 
    { 
    Table("UserRoles"); 

    CompositeId() 
     .KeyReference(x => x.User, "USER_LOGIN") 
     .KeyReference(x => x.Role, "ROLE_ID"); 

    Map(x => x.Active).Column("ROLE_IS_ACTIVE"); 
    } 
} 

因此用户可以有多个角色和角色有多个用户。如我所说,经典的多对多。 但是,UserRoles表中还包含一个“活动”字段,我的应用程序 需要正确操作。

一切工作正常,但恕我直言nhibernate生成方式太多的查询。 当我选择一个用户和访问它的角色,这些都是在NHibernate的探查显示 查询:

SELECT user0_.USER_LOGIN  as USER_LOGIN0_0_, 
    user0_.USER_NAME   as USER_NAME_0_ 
FROM Users user0_ 
WHERE user0_.USER_ID = 'testuser' 
-- Fetch the user 

SELECT userrole0_.USER_LOGIN as USER_LOGIN1_, 
    userrole0_.ROLE_ID   as ROLE_ID1_, 
    userrole0_.ROLE_ID   as ROLE_ID1_0_, 
    userrole0_.USER_LOGIN  as USER_LOGIN1_0_, 
    userrole0_.ROLE_IS_ACTIVE as ROLE_IS_ACTIVE1_0_ 
FROM UserRoles userrole0_ 
WHERE userrole0_.USER_LOGIN = 'testuser' 
-- Fetch the roles for that user (why are some fields selected twice?) 
-- returns three rows: roleids: 1, 2 and 3 

SELECT userrole0_.ROLE_ID as ROLE_ID1_0_, 
    userrole0_.USER_LOGIN  as USER_LOGIN1_0_, 
    userrole0_.ROLE_IS_ACTIVE as ROLE_IS_ACTIVE1_0_ 
FROM UserRoles userrole0_ 
WHERE userrole0_.USER_LOGIN = 'testuser' 
     and userrole0_.ROLE_ID = 1 

SELECT userrole0_.ROLE_ID as ROLE_ID1_0_, 
    userrole0_.USER_LOGIN  as USER_LOGIN1_0_, 
    userrole0_.ROLE_IS_ACTIVE as ROLE_IS_ACTIVE1_0_ 
FROM UserRoles userrole0_ 
WHERE userrole0_.USER_LOGIN = 'testuser' 
     and userrole0_.ROLE_ID = 2 

SELECT userrole0_.ROLE_ID as ROLE_ID1_0_, 
    userrole0_.USER_LOGIN  as USER_LOGIN1_0_, 
    userrole0_.ROLE_IS_ACTIVE as ROLE_IS_ACTIVE1_0_ 
FROM UserRoles userrole0_ 
WHERE userrole0_.USER_LOGIN = 'testuser' 
     and userrole0_.ROLE_ID = 3 
-- Fetch all rows again separately but with full key?!?!!? 

因此,NHibernate的开始与获取我的用户:OK 接下来,获取角色为该用户:OK 但是,然后,第二个查询返回的每一行都会再次从DB中检索 ! 我不知道为什么会发生这种情况,因为从第二个 查询返回的数据实际上包含足够的数据让NHibernate填充我的整个UserRole对象。

是否有任何人谁可以:

  1. 向我解释,为什么出现这种情况
  2. 帮我找出如何防止 这一点。即我想告诉NHibernate,UserRoles表上额外的 查询是 不是必需的。

非常感谢! 问候, LDX

  1. 它可能appens因为你没有在你的映射类中指定不使用懒负载的关系。
  2. 为1点,你可以指定你的映射类不使用延迟加载像

    公共MyClassMap()

    {

    表( “...”);

    Id(...);

    Map(...);

    HasMany(x => x ....)。KeyColumn(“...”)。LazyLoad();

    }

在您的查询,如果您正在使用QueryOver声明,您也可以使用。未来();而不是.List();

我的洞很有帮助。

+0

嗨,我试过这个,但他们不改变我得到的查询量,只是他们执行的时间。在我第一次访问Users.UserRoles列表时执行查询之前。现在,当我查询用户对象时,查询得到执行 – ldx 2011-04-07 15:15:45

+0

@Idx:very strage ... Not.LazyLoad()方法用于提取具有唯一查询的所有数据。我在某个查询中使用了它,它完美地工作。我不明白毫无意义......无论如何,如果你想只有一个查询,你不使用LazyLoad。祝你好运。 – Faber 2011-04-07 15:24:30

您可以更改惰性加载集合在NHibernate中的抓取行为。 你可以在你的映射通过指定fetch属性:

<many-to-one name="UserRoles" column="ROLE_ID" class="Roles" fetch="select" lazy="false" not-null="true" cascade="save-update" /> 

我相信你可以用流利的做到这一点为好,但我不知道由心脏的方法。

您还可以覆盖你已经在你的mappping指定的取行为,通过在ICriteria

criteria.SetFetchMode ("UserRoles", FetchMode.Join); 
+0

这会有所帮助。用户和UserRoles上的查询(即前两个查询)现在已加入。但是我仍然在UserRoles上获得额外的三个查询。我认为这与UserRoles有一个组合ID有关。第一个查询只使用该键的一半,所以我认为nhibernate将这些结果视为“可能不是唯一的”,因此它使用composite-id的两个字段进行新的查询。这可能吗?那么有没有什么办法可以告诉nhibernate那不需要? :-) – ldx 2011-04-07 15:24:17

+0

顺便说一下,流利的语法是HasMany(u => u.UserRoles).KeyColumn(“USER_LOGIN”)。Fetch.Join(); – ldx 2011-04-07 15:25:59

不是直接回答你的问题明确设置它...

它可能更容易将其从一个侧面,并从其他的逆多到许多映射作为组分:

用户:

<bag name="UserRoles" table="UserRoles"> 
    <key name="USER_LOGIN"/> 
    <composite-element> 
    <many-to-one name="Role" column="ROLE_ID"/> 
    <property name="Active" /> 
    </composite-element> 
</bag> 

角色:

<bag name="Users" inverse="true" table="UserRoles"> 
    <key name="ROLE_ID"/> 
    <many-to-many class="User" column="USER_LOGIN"/> 
</bag> 
  • 你不需要复合ID
  • 你不需要到单独的UserRole映射
  • 您可以在两个方向导航(但你看到Active国旗只从User.UserRoles
+0

好的,这似乎是可以接受的,但在我去和改变所有的代码之前,你能告诉我如果我访问User.UserRoles将会发生什么?它会导致对UserRoles表的单个查询吗?或者UserRoles表中的每行一个查询,其中user_id = users.user_id?或者我必须通过尝试找出答案? :-) – ldx 2011-04-08 06:48:34

+0

您可能*需要在与角色的多对一关系中使用fetch =“join”。我不知道是否需要。然后你应该为每个角色(N + 1)得到一个查询 - 除非你对它做了一些事情,例如。使用批量大小。你以(N + 1)/批量大小结束,这通常是相当不错的。 – 2011-04-08 13:04:42