2013-08-20 7 views
5

Używam NHibernate 3.3.1 z FluentNhibernate 1.3 dla warstwy danych.NHibernate: Jak chętniej pobierać elementy podrzędne z filtrem nad podległym obiektem za pomocą jednego zapytania sql bez lazyload?

Mam następujące podmioty: Schemat

enter image description here

Baza: enter image description here

muszę metodę, która pobiera produktów ubocznych MediaCategory z Medias Produktów. Chcę NHibernate wysłać tylko jedno zapytanie do db i pobrać wszystkie podrzędne właściwości produktów.

Chcę NHibernate wysłać zapytanie tak:

declare @mediaCategoryId int = 13 
select * 
from Product p 
inner join Media m on m.ProductId=p.Id 
inner join MediaCategoryMedia mcm on mcm.MediaId=m.Id 
inner join MediaCategory mc on mc.Id=mcm.MediaCategoryId 
left join ProductSeller ps on ps.ProductId=p.Id 
left join Seller s on ps.SellerId=s.Id 
where [email protected] 

Próbowałem następujących opcji, aby rozwiązać ten problem;

  1. sesja .QueryOver<ProductEntity>() ...
    Próbowałem Inner.JoinQueryOver<..>().Fetch.Eager ... ale nie mógł pobrać wszystkie jednostki podrzędne.

  2. session.CreateCriteria<ProductEntity>().SetFetchMode("",FetchMode.Eager) ... W tym przypadku działa luźne obciążenie i nie chcę lazyload. Jeśli wyłączyłem lazyload z odwzorowań, NH wysyła wiele zapytań. To, czego chcę, to spore obciążenie jednym zapytaniem, które pobiera wszystkie podsieci.

  3. session.Query<ProductEntity>().FetchMany(p=>p.MediaList).ThenFetchMany(m=>m.SellerList) ... Nie można utworzyć aliasu, aby przekazać filtr mediaCategoryId w tym przypadku. Zamiast tego użyłem .Where(x=>x.MediaList.Any(m=>m.CategoryList.Any(...))), a wygenerowane zapytanie również nie jest optymalne.

  4. (od P w session.Query < ProductEntity>()
    z mw p.MediaList
    zc w m.MediaCategoryList
    gdzie c.Id == 23
    wybrać P) .Fetch (x => x.MediaList);

    to nie działa jak chciałem, zbyt ..

  5. var [email protected]"select p from ProductEntity as p join fetch p.MediaList as m join fetch m.MediaCategoryList as mc left join fetch p.SellerList as s where mc.Id=:catId ";
    to działa z "join fetch" w HQL.
    Potrzebuję najlepszej praktyki w tej sprawie, jednak Hql jest królem.

    Czy możemy obsłużyć tę sprawę z session.Query<>() lub session.CreateCriteria, lub QueryOver?

+0

Potrzebujesz również "Media.MediaCategoryList", aby powrócić do programu? A może interesuje Cię tylko 'Product.MediaList' i' Product.SellerList'? –

Odpowiedz

6

Dla bezpośrednim tłumaczeniu zapytaniu ...

Media mediaAlias = null; 
MediaCategory categoryAlias = null; 

return session.QueryOver<Product>() 
    .JoinAlias(x => x.Medias,() => mediaAlias) 
    .JoinAlias(() => mediaAlias.Categories,() => categoryAlias) 
    .Fetch(x => x.Sellers).Eager 
    .Where(() => categoryAlias.Id == mediaCategoryId) 
    .List(); 

JoinAlias robi sprzężenie wewnętrzne domyślnie i Fetch(...).Eager robi lewe sprzężenie zewnętrzne. JoinAlias pozwala nam przekopać się przez Media do kategorii, a także chętnie pobiera dane.

Należy zauważyć, że istnieje produkt kartezjański między sprzedawcami i mediami w tym zapytaniu.Jeśli na jednym produkcie jest 20 Mediów i 20 Sprzedawców, to zapytanie zwróci 20 * 20 = 400 wierszy, co nie jest idealne dla wydajności. Możesz rozwiązać ten problem, dzieląc pobieranie mediów i sprzedawca pobierając je na osobne zapytania, ale wsadowe je razem w jednym obiegu do bazy danych za pomocą Future(), co oznacza, że ​​zapytanie zwróci 20 + 20 = 40 wierszy. Dużo lepiej.

Ponadto zapytanie to nie zwróci wszystkich kategorii powiązanych z mediami. Jeśli potrzebujesz tego, powinieneś zastosować ograniczenie mediaCategoryId w pod-zapytaniu Exists.

+1

Tworzenie dwóch zapytań z kontraktami terminowymi jest najlepszą odpowiedzią tutaj, jeśli potrzebujesz stronicowania. To jest blog, który zawsze mam na myśli dwa. http://ayende.com/blog/4367/eagerly-loading-entity-associations-efficiently-with-nhibernate – Rippo

+0

Jednak dobre jest to rozwiązanie, a co jeśli każdy Media ma inne subkrybie, takie jak MediaSubitems i chcę je przywieźć z niecierpliwością to samo zapytanie. Dzięki metodzie Fetch (x => x ...) mogę tylko określić elementy podrzędne Produktu, które mają być pobrane z niecierpliwością, w jaki sposób mogę dowolnie określić wszystkie MediaSubitem każdego Media? – mecek

+0

Ja * właśnie * odpowiedziałem na inne pytanie dotyczące tej samej rzeczy: http://stackoverflow.com/a/18478819/221708. Aby szybko pobrać MediaSubitems, użyj 'JoinAlias ​​(() => mediaAlias.SubItems,() => mediaSubItemAlias, JoinType.LeftOuterJoin) zamiast' Fetch'. –

Powiązane problemy