NHibernate的多表關聯查詢做的還是很不錯的,除了比較BT的報表查詢,一般業務所需的多表查詢基本都能滿足,且效率也還算不錯,NHibernate多表查詢在資料庫訪問層上其實比較簡單。反而痛點在於如何把查詢出來的多層對象不轉換成DTO,直接傳輸到UI進行綁定,下篇講展現層的時候會詳細描述(註:關於DTO的使用和利弊這裡不多加討論,在本系統中的設計原則是盡量避免使用DTO)。
依然還是接之前字典維護的例子,我們需要在字典項目的表格裡顯示一列“類別”即字典項目所屬字典類別的名稱,這個欄位在字典項的表是不存在的,需要關聯查詢字典類別表才能得出。如(由於存在項目客戶實際資料,所以我圖改了一些...):
我們把上一篇已經具備分頁,排序,多條件組合查詢功能的方法public IList<Dictionary> GetPlistByCategoryId(...)再改一改:
代碼
//現在我具備分頁,排序,多條件查詢,多表關聯查詢,基本全乎了:)
public IList<Dictionary> GetPlistByCategoryId(string id, int start, int limit, string sort, string dir, List<DataFilter> filters, out long total)
{
sort = "d." + sort;
var strFilter = base.GetHqlstrByExtFilter(filters, "d");//通過剛才基類的方法把filters轉換成HQL字串
var query = Session.CreateQuery(@"select d from Dictionary as d left join fetch d.Category where d.Category.Id=:Id"//改了下這裡,搞成多表查詢
+ (string.IsNullOrEmpty(strFilter) ? string.Empty : " and " + strFilter)//這裡把組合查詢字串加進去
+ " order by " + sort + " " + dir)
.SetString("Id", id)
.SetFirstResult(start)
.SetMaxResults(limit);
total = Session.CreateQuery(@"select count(*) from Dictionary as d where d.Category.Id=:Id"
+ (string.IsNullOrEmpty(strFilter) ? string.Empty : " and " + strFilter))//別忘記這裡也要加喲
.SetString("Id", id)
.UniqueResult<long>();
return query.List<Dictionary>();
}
其實就是改了下HQL語句
以前是 var query = Session.CreateQuery(@"select d from Dictionary as d where d.Category.Id=:Id"。。。
修改後var query = Session.CreateQuery(@"select d from Dictionary as d left join fetch d.Category where d.Category.Id=:Id"。。。
加了" left join fetch d.Category" 解釋如下:
a.”left join“:HQL左關聯關鍵字,和SQL一樣的,大家應該很容易理解
b."fetch":HQL關鍵字,非常重要,表示不使用消極式載入一次性取出所關聯的類的資料。因為我們在Xml配置的時候設定的是lazy=true(消極式載入包含對象),預設的NHibernate不會把Dictionary下的Category屬性的資料取出來,而是在你需要用的時候再去現查資料庫取。這本來是件好事,可是當我們出集合的時候就會出現可怕的情況,出一條Dictionary資料讀一次資料庫去取Dictionary下的Category,比如我們這裡分頁15條資料,那麼會產生17條SQL語句(1次查總數+1次查字典項表+15次查每個字典項所對應的字典類別),恐怖吧...
所以我們用fetch 告訴NHibernate 在這裡你就一次性取出好了 不用消極式載入了,這樣只會像以前一樣產生2條SQL語句,如下
查總數total的和以前一樣不變:
select count(* ) as col_0_0_
from INFRA_DICTIONARY dictionary0_
where (dictionary0_.IsDelete = 0)
and dictionary0_.DICCATEGORY_ID = '48391bb4-471b-4499-899b-cea9748e1a7b' /* :p0 */
產生了關聯查詢的SQL語句(除了'select'與'from'之間的語句產生有點囧,其他的基本和咱手寫的也差不多):
代碼
select *
from (select dictionary0_.DICTIONARY_ID as DICTIONARY1_0_0_,
diccategor1_.DICCATEGORY_ID as DICCATEG1_1_1_,
dictionary0_.VERSION as VERSION0_0_,
dictionary0_.NAME as NAME0_0_,
dictionary0_.CODE as CODE0_0_,
dictionary0_.INPUT_CODE1 as INPUT5_0_0_,
dictionary0_.INPUT_CODE2 as INPUT6_0_0_,
dictionary0_.INPUT_CODE3 as INPUT7_0_0_,
dictionary0_.INDEX_FIELD as INDEX8_0_0_,
dictionary0_.DESCRIPTION as DESCRIPT9_0_0_,
dictionary0_.CREATETIME as CREATETIME0_0_,
dictionary0_.ISDELETE as ISDELETE0_0_,
dictionary0_.DICCATEGORY_ID as DICCATE12_0_0_,
diccategor1_.VERSION as VERSION1_1_,
diccategor1_.NAME as NAME1_1_,
diccategor1_.PARENT_ID as PARENT4_1_1_,
diccategor1_.TREE_CODE as TREE5_1_1_,
diccategor1_.LEAF as LEAF1_1_,
diccategor1_.DESCRIPTION as DESCRIPT7_1_1_,
diccategor1_.CREATETIME as CREATETIME1_1_,
diccategor1_.ISDELETE as ISDELETE1_1_,
diccategor1_.NODE_LEVEL as NODE10_1_1_
from INFRA_DICTIONARY dictionary0_
left outer join INFRA_DICCATEGORY diccategor1_
on dictionary0_.DICCATEGORY_ID = diccategor1_.DICCATEGORY_ID
where (dictionary0_.IsDelete = 0)
and dictionary0_.DICCATEGORY_ID = '48391bb4-471b-4499-899b-cea9748e1a7b' /* :p0 */
order by dictionary0_.INDEX_FIELD desc)
where rownum <= 15 /* :p1 */
還是在我們那個很土的測試方法裡測試一下,遍列訪問 Dictionary.Category.Name(自己加點測試資料),你開啟NHProfiler工具監視一下 ,然後把上面說的有點繞的"fetch"去掉,再看看,這樣就能明白了。測試代碼如下(寫的很土,能理解就行,時間關係我就沒有搞啥單元測試之類的了):
代碼
//測試一下
public ActionResult test3()
{
Demo.HIS.Infrastructure.Core.Repositories.IDictionaryRepository r = new Demo.HIS.Infrastructure.Repositories.Data.DictionaryRepositoryImpl();
long total;
var list = r.GetPlistByCategoryId("48391bb4-471b-4499-899b-cea9748e1a7b", 0, 15, "Index", "desc", null, out total);
string str;
foreach (var item in list)
{
str = item.Category.Name;//如果你不用fetch 每一次迴圈我都會去訪問資料庫,哈哈
}
return Content("");
}
如我們再複雜點多關聯幾個表,如此這般:
代碼
var query = Session.CreateQuery(@"select g from GroupedServiceItem g
left join fetch g.Item i
left join fetch i.ItemCategory
left join fetch i.MedicalCategory
left join fetch i.FeesCategory
where g.ParentItem.Id=:Id")
.SetString("Id", id);
以上代碼是我在項目中隨便摘取的,大概意思理解就可以了...
如上所述我們實現了NHibernate的多表查詢,但是我們查詢出的對象是包含嵌套的呀,如何把這樣的結構送到UI及Ext進行資料繫結呢?請看下篇...
源碼(上篇的源碼也在這裡,搞好資料庫,整點測試資料,就能像我上面描述的一樣運行測試):HISDemo-5.rar