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对象。
是否有任何人谁可以:
- 向我解释,为什么出现这种情况
- 帮我找出如何防止 这一点。即我想告诉NHibernate,UserRoles表上额外的 查询是 不是必需的。
非常感谢! 问候, LDX
- 它可能appens因为你没有在你的映射类中指定不使用懒负载的关系。
-
为1点,你可以指定你的映射类不使用延迟加载像
公共MyClassMap()
{
表( “...”);
Id(...);
Map(...);
HasMany(x => x ....)。KeyColumn(“...”)。LazyLoad();
}
在您的查询,如果您正在使用QueryOver声明,您也可以使用。未来();而不是.List();
我的洞很有帮助。
您可以更改惰性加载集合在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);
这会有所帮助。用户和UserRoles上的查询(即前两个查询)现在已加入。但是我仍然在UserRoles上获得额外的三个查询。我认为这与UserRoles有一个组合ID有关。第一个查询只使用该键的一半,所以我认为nhibernate将这些结果视为“可能不是唯一的”,因此它使用composite-id的两个字段进行新的查询。这可能吗?那么有没有什么办法可以告诉nhibernate那不需要? :-) – ldx 2011-04-07 15:24:17
顺便说一下,流利的语法是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
)
好的,这似乎是可以接受的,但在我去和改变所有的代码之前,你能告诉我如果我访问User.UserRoles将会发生什么?它会导致对UserRoles表的单个查询吗?或者UserRoles表中的每行一个查询,其中user_id = users.user_id?或者我必须通过尝试找出答案? :-) – ldx 2011-04-08 06:48:34
您可能*需要在与角色的多对一关系中使用fetch =“join”。我不知道是否需要。然后你应该为每个角色(N + 1)得到一个查询 - 除非你对它做了一些事情,例如。使用批量大小。你以(N + 1)/批量大小结束,这通常是相当不错的。 – 2011-04-08 13:04:42
嗨,我试过这个,但他们不改变我得到的查询量,只是他们执行的时间。在我第一次访问Users.UserRoles列表时执行查询之前。现在,当我查询用户对象时,查询得到执行 – ldx 2011-04-07 15:15:45
@Idx:very strage ... Not.LazyLoad()方法用于提取具有唯一查询的所有数据。我在某个查询中使用了它,它完美地工作。我不明白毫无意义......无论如何,如果你想只有一个查询,你不使用LazyLoad。祝你好运。 – Faber 2011-04-07 15:24:30