2011-04-07 58 views
1

我目前正在編寫一個訪問遺留數據庫的應用程序。 我使用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
  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

2

您可以更改惰性加載集合在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

1

不是直接回答你的問題明確設置它...

它可能更容易將其從一個側面,並從其他的逆多到許多映射作爲組分:

用戶:

<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