2013-07-17 7 views
19

Mam klasę Cat i klasę Właściciel. Kot ma jednego właściciela, ale właściciel może mieć wiele kotów. Chcę zapytać: "zdobądź wszystkich właścicieli, którzy mają kota o niebieskich oczach".Kryteria Hibernowania dołącz jedno zapytanie do wielu

class Cat{ 
Owner owner; //referenced from Owner.id 
String eyeColor; 
} 

class Owner{ 
List<Cat> catList; 
} 

Próbowałem kilka kodów, ale naprawdę nie wiem, co robić.

Criteria criteria = getCurrentSession().createCriteria(cat.getClass(), "cat"); 
criteria.createAlias("cat.owner", "owner");  
criteria.add(Restrictions.eq("cat.eyeColor", "blue"); 
+1

Czy powiązanie jest dwukierunkowe? Dlaczego nie używać HQL do takiego statycznego zapytania? –

+0

@JBNizet przepraszam, ale nie mam wyboru, jak używać HQL. – hellzone

+0

Czy powiązanie jest dwukierunkowe? Dlaczego nie masz wyboru? To tak, jakby stolarz nie mógł używać młotka! –

Odpowiedz

34

Kryteria mogą tylko wybierać projekcje lub element główny. Nie jakiś połączony podmiot. Niektórych zapytań nie można w ten sposób wyrazić za pomocą Kryteriów (co jest jeszcze jednym dobrym powodem do użycia HQL, oprócz znacznie lepszej czytelności i zwięzłości).

Wszystko tutaj nie jest stracone, ponieważ twoje powiązanie jest dwukierunkowe. Więc wystarczy równowartość zapytania HQL

select distinct owner from Owner owner 
join owner.cats cat 
where cat.eyeColor = 'blue' 

Która jest

Criteria c = session.createCriteria(Owner.class, "owner"); 
c.createAlias("owner.cats", "cat"); 
c.add(Restrictions.eq("cat.eyeColor", "blue"); 
c.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY); 
+0

W moim przypadku kot zna właściciela, ale właściciel nie wie o swoich kotach. Jak wyglądałoby zapytanie o kryteria? – Kayser

+0

Jak napisano w mojej odpowiedzi, AFAIK (chyba że było to możliwe w nowszych wersjach), nie jest możliwe zastosowanie kryteriów. Użyj HQL lub użyj API kryteriów JPA2. –

+0

Ta operacja "odróżnia się" w pamięci. Jeśli jeden właściciel ma miliona niebieskookich kotów, pobierze je wszystkie z bazy danych, aby znaleźć jedynego właściciela (przykład prawdopodobnie nie jest prawdziwym przypadkiem pytającego, nie znamy prawdziwej liczności). Zobacz moją odpowiedź, jak można to zrobić w bazie danych. – Oliv

4

Spróbuj tego:

DetachedCriteria dc = DetachedCriteria.forClass(Cat.class, "inner") 
    .add(Restrictions.eq("eyeColor", "blue")) 
    .add(Restrictions.eqProperty("inner.owner", "outer.pk")); 

session.createCriteria(Owner.class, "outer") 
    .add(Subqueries.exists(dc)) 
    .list(); 

Można użyć indeksu w bazie danych i nie zrobi in -memory distinct operacja jak w wersji @JB Nizet (zobacz mój komentarz tam). Indeks będą:

CREATE INDEX idx_cat_owner_eyecolor ON Cat(fkOwner, eyeColor) 

Myśl z distinct działania (albo w SQL lub w pamięci), jak na zapach kodu. Jest rzadko używany i wielu początkujących programistów używa go do naprawienia problemu "dlaczego mam ten wiersz dwa razy". Można go prawie zawsze przepisać tak, jak w tym przypadku. Przypadki użycia, gdy jest to konieczne, są nieliczne.