Właściwie odpowiedź na tę sytuację jest prostsza, niż mogłoby się wydawać. Jest to prosta sprawa użycia odpowiedniego narzędzia do właściwej pracy. JPA nie zostało zaprojektowane do implementacji skomplikowanych zapytań SQL, do czego służy SQL! Potrzebujesz więc sposobu, aby JPA uzyskało dostęp do kwerend SQL na poziomie produkcyjnym;
em.createNativeQuery
W twoim przypadku, to co chcesz zrobić, to uzyskać dostęp do tabeli AB, szukając tylko pola id. Po pobraniu zapytania, weź pole id i wyszukaj obiekt Java, używając pola id. To drugie wyszukiwanie prawdziwe, ale trywialne według standardów SQL.
Załóżmy, że szukasz obiektu A w zależności od tego, ile razy obiekt B go odwołuje. Załóżmy, że potrzebujesz złożonej (ale typowej) kwerendy SQL do grupowania obiektów typu A na podstawie liczby obiektów B w kolejności malejącej. Byłoby to typowe zapytanie o popularność, które możesz chcieć wdrożyć zgodnie z wymaganiami projektu.
Zapytanie SQL rodzimy byłby jako takie:
select a_id as id from AB group by a_id order by count(*) desc;
Teraz to, co chcesz zrobić, to powiedzieć JPA oczekiwać listę id na powrót w formie, że WZP może zaakceptować. Musisz utworzyć dodatkowy podmiot JPA. Tego, który nigdy nie będzie używany w normalnym trybie WZP. Ale JPA potrzebuje sposobu, aby odzyskać z powrotem poszukiwane przedmioty. Użytkownik utworzyłby jednostkę dla tego zapytania jako takiego;
@Entity
public class IdSearch {
@Id
@Column
Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
Teraz zaimplementowałeś trochę kodu, aby połączyć dwie technologie;
@SuppressWarnings("unchecked")
public List<IdSearch> findMostPopularA() {
return em.createNativeQuery("select a_id as id from AB group by a_id
order by count(*) desc", IdSearch.class).getResultList();
}
To wszystko, co musisz zrobić, aby JPA mogło pomyślnie zakończyć Twoją kwerendę. Aby dostać się do obiektów A, wystarczyłoby odwołać się do swojej listy A przy użyciu tradycyjnego podejścia JPA;
List<IdSearch> list = producer.getMostPopularA();
Iterator<IdSearch> it = list.iterator();
while (it.hasNext()) {
IdSearch a = it.next();
A object = em.find(A.class,a.getId());
// your in business!
Nadal nieco więcej udoskonalenia powyższego może uprościć rzeczy nieco dalej, biorąc pod uwagę wiele możliwości struktury projektu SQL.Nieco bardziej skomplikowane zapytanie SQL zapewni jeszcze bardziej bezpośredni interfejs JPA do rzeczywistych danych;
@SuppressWarnings("unchecked")
public List<A> findMostPopularA() {
return em.createNativeQuery("select * from A, AB
where A.id = AB.a_id
group by a_id
order by count(*) desc", A.class).getResultList();
}
W ten sposób usuwa się konieczność znalezienia tablicy wyników wyszukiwania id!
List<A> list = producer.getMostPopularA();
Iterator<A> it = list.iterator();
while (it.hasNext()) {
A a = it.next();
// your in business!
Co może nie być jasne tot gołym okiem jest cudownie uproszczony sposób JPA pozwala na wykorzystanie skomplikowanych struktur SQL wewnątrz interfejsu JPA. Wyobraź sobie, jeśli SQL w następujący sposób;
SELECT array_agg(players), player_teams
FROM (
SELECT DISTINCT t1.t1player AS players, t1.player_teams
FROM (
SELECT
p.playerid AS t1id,
concat(p.playerid,':', p.playername, ' ') AS t1player,
array_agg(pl.teamid ORDER BY pl.teamid) AS player_teams
FROM player p
LEFT JOIN plays pl ON p.playerid = pl.playerid
GROUP BY p.playerid, p.playername
) t1
INNER JOIN (
SELECT
p.playerid AS t2id,
array_agg(pl.teamid ORDER BY pl.teamid) AS player_teams
FROM player p
LEFT JOIN plays pl ON p.playerid = pl.playerid
GROUP BY p.playerid, p.playername
) t2 ON t1.player_teams=t2.player_teams AND t1.t1id <> t2.t2id
) innerQuery
GROUP BY player_teams
Chodzi o to, że z interfejsem createNativeQuery, nadal można odzyskać dokładnie dane szukasz prosto do żądanego obiektu dla łatwego dostępu Java.
@SuppressWarnings("unchecked")
public List<A> findMostPopularA() {
return em.createNativeQuery("SELECT array_agg(players), player_teams
FROM (
SELECT DISTINCT t1.t1player AS players, t1.player_teams
FROM (
SELECT
p.playerid AS t1id,
concat(p.playerid,':', p.playername, ' ') AS t1player,
array_agg(pl.teamid ORDER BY pl.teamid) AS player_teams
FROM player p
LEFT JOIN plays pl ON p.playerid = pl.playerid
GROUP BY p.playerid, p.playername
) t1
INNER JOIN (
SELECT
p.playerid AS t2id,
array_agg(pl.teamid ORDER BY pl.teamid) AS player_teams
FROM player p
LEFT JOIN plays pl ON p.playerid = pl.playerid
GROUP BY p.playerid, p.playername
) t2 ON t1.player_teams=t2.player_teams AND t1.t1id <> t2.t2id
) innerQuery
GROUP BY player_teams
", A.class).getResultList();
}
Cheers,
Perry
[email protected]
Nie jestem pewien JPQL bez obiektu podmiotu. dlaczego nie SQL? – vels4j
Sql może to zrobić łatwo, ale chcę zachować spójność (używać tylko JPA) w moim projekcie. Ale oczywiście, jeśli nie ma sposobu, aby to zrobić, wybiorę SQL – Neron