2013-08-20 143 views
0

我寫的代碼實際上是它的工作,但它不是非常優化的。NHibernate:用左連接代替多對一

在我的例子,我有兩個很簡單的表

enter image description here

一個簡單的SQL查詢來獲取這個人的名字和相關的國家名稱(如果有的話)應該是這樣的

SELECT Person.Name AS Name, Country.CountryName AS Country FROM Person LEFT OUTER JOIN Country ON Country.CountryId = Person.CountryId 

enter image description here

而且一個構造良好的對象用NHibernate拾取數據看起來像這樣

enter image description here

夠簡單了吧。我的模型看起來像這樣

namespace NHibernateLeftJoin.Models 
{ 
    public class Country 
    { 
     public virtual int CountryId { get; set; } 
     public virtual string CountryName { get; set; } 
    } 

    public class Person 
    { 
     public virtual int PersonId { get; set; } 
     public virtual string Name { get; set; } 
     public virtual int CountryId { get; set; } 
     public virtual Country CountryObject { get; set; } 
    } 
} 

而且映射

<?xml version="1.0" encoding="utf-8" ?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true"> 
    <class name="NHibernateLeftJoin.Models.Country, NHibernateLeftJoin" lazy="true"> 
    <id name="CountryId"> 
     <generator class="native" /> 
    </id> 
    <property name="CountryName" /> 
    </class> 

    <class name="NHibernateLeftJoin.Models.Person, NHibernateLeftJoin" lazy="true"> 
    <id name="PersonId"> 
     <generator class="native" /> 
    </id> 
    <property name="Name" /> 
    <property name="CountryId" /> 
    <many-to-one name="CountryObject" class="NHibernateLeftJoin.Models.Country" lazy="false" 
       column="CountryId" outer-join="true" unique="true" not-null="false" cascade="none" /> 
    </class> 
</hibernate-mapping> 

這種方法的問題是,NHibernate的取一個「人」行所說的「Contry」表的每一行,如果我們有成千上萬的行不太好。似乎不可能像我們使用SQL一樣完美地執行此操作,或者它使用完全錯誤的方法。

的VS項目可以在這裏找到有興趣的人士https://dl.dropboxusercontent.com/u/6208162/NHibernateLeftJoin.zip

感謝

== ==編輯

查詢可能看起來像這樣

private static IList<Person> GetPersons() 
{ 
    using (var session = NHibernateHelper.OpenSession()) 
    { 
     //using (var transaction = session.BeginTransaction()) {} 
     IQuery query = session.CreateQuery("FROM Person"); 
     return query.List<Person>(); 
    } 
} 

的sql由此生成將看起來像這樣

NHibernate: select person0_.PersonId as PersonId1_, person0_.Name as Name1_, person0_.CountryId as CountryId1_ from Person person0_ 

NHibernate: SELECT country0_.CountryId as CountryId0_0_, country0_.CountryName as CountryN2_0_0_ FROM Country country0_ WHERE [email protected];@p0 = 1 [Type: Int32 (0)] 

NHibernate: SELECT country0_.CountryId as CountryId0_0_, country0_.CountryName as CountryN2_0_0_ FROM Country country0_ WHERE [email protected];@p0 = 2 [Type: Int32 (0)] 

NHibernate: SELECT country0_.CountryId as CountryId0_0_, country0_.CountryName as CountryN2_0_0_ FROM Country country0_ WHERE [email protected];@p0 = 3 [Type: Int32 (0)] 

這對我的數據非常合乎邏輯,第一個查詢取得所有人員,然後用三個不同的查詢(有三個唯一的國家映射到用戶)獲取CountryObject的數據。

感謝

回答

0

的解決方案是實現該方法的createQuery的加入如下

IQuery query = session.CreateQuery("FROM Person AS P LEFT JOIN FETCH P.CountryObject"); 
return query.List<Person>(); 

並寫出人的映射文件中像這樣

<?xml version="1.0" encoding="utf-8" ?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="true"> 
    <class name="NHibernateLeftJoin.Models.Person, NHibernateLeftJoin" lazy="true"> 
    <id name="PersonId"> 
     <generator class="native" /> 
    </id> 
    <property name="Name" /> 
    <property name="CountryId" /> 
    <many-to-one name="CountryObject" class="NHibernateLeftJoin.Models.Country" 
       column="CountryId" not-found="ignore" 
       cascade="none" /> 
    </class> 
</hibernate-mapping> 

在我看來我不明白爲什麼這個查詢添加當參數取=不自動寫入「加入」外聯接=「真」,以在映射文件中的多對的一個元件。

1

首先,刪除此屬性... public virtual int CountryId { get; set; }您 可以訪問CountryId通過... CountryObject.CountryId。我認爲你要找的是ManyToOne上的fetch="join"。其中告訴NH,每次查詢Person時外部連接到此表。這會讓ManyToOne非懶惰。

http://nhibernate.info/doc/nh/en/#mapping-declaration-manytoone

通常情況下,我會避免這個設置,並使用QueryOver手動加入或預先抓取任何ManyToOnes。如果我正在使用單個實體,則將爲ManyToOnes使用默認的延遲加載。

Session.QueryOver<Person>() 
    .Fetch(x => x.CountryObject).Eager 
    .List(); 
+0

有沒有嘗試,它仍然執行多個查詢 –

+0

如何與QueryOver解決這個任何想法? :) –

+0

@Eric赫利茨您可以發佈您的查詢代碼和生成的SQL? – dotjoe

0

很好的一種方式來解決這個問題是當你寫的查詢投射到一類。

LINQ查詢做這樣的事情可能是這樣的

var people = from p in _session.Query<Person>() 
select new 
{ 
    Name = p.Name, 
    Country = p.Country == null ? String.Empty : p.Country.Name; 
} 

而且你不需要CountryId Person對象上。您應該能夠抓住countryId這樣person.Country.Id如果一個人與一國有關。