JPA csatlakozási típusok
1. Áttekintés
Ebben az oktatóanyagban megvizsgáljuk a JPA által támogatott különböző csatlakozási típusokat.
Erre a célra a JPQL-t, a JPA lekérdezési nyelvét fogjuk használni.
2. Minta adatmodell
Nézzük meg a példákban használt mintadat-modellünket.
Először létrehozunk egy Munkavállaló entitás:
@Entity public class Employee {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) private long id; privát karakterlánc neve; privát int kor; @ManyToOne magánosztály osztály; @OneToMany (mappedBy = "alkalmazott") privát List telefonok; // szerelők és beállítók ...}
Minden egyes Munkavállaló csak egyhez lesz rendelve Osztály:
@Entity public class Department {@Id @GeneratedValue (strategy = GenerationType.AUTO) private long id; privát karakterlánc neve; @OneToMany (mappedBy = "osztály") magánlista alkalmazottai; // szerelők és beállítók ...}
Végül mindegyik Munkavállaló több lesz Telefons:
@Entity public class Phone {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) private long id; privát karakterlánc száma; @ManyToOne magánalkalmazotti alkalmazott; // szerelők és beállítók ...}
3. Belső csatlakozások
Belső csatlakozásokkal kezdjük. Ha két vagy több entitás belső összekapcsolt állapotban van, az eredményben csak a csatlakozási feltételnek megfelelő rekordok kerülnek összegyűjtésre.
3.1. Implicit belső csatlakozás az Egyértékű Egyesület Navigációval
A belső csatlakozások lehetnek beleértett. Ahogy a neve is mutatja, a fejlesztő nem határoz meg implicit belső csatlakozásokat. Amikor egy értékű társításban navigálunk, a JPA automatikusan implicit csatlakozást hoz létre:
@Test public void whenPathExpressionIsUsedForSingleValuedAssociation_thenCreatesImplicitInnerJoin () {TypedQuery query = entityManager.createQuery ("SELECT e.department FROM Employee e", Department.class); List resultList = lekérdezés.getResultList (); // Állítások ...}
Itt a Munkavállaló entitásnak sok az egyben kapcsolata van a Osztály entitás. Ha egy-ból navigálunk Munkavállaló entitás neki Osztály - meghatározása e. osztály - egyértékű egyesületben fogunk navigálni. Ennek eredményeként a JPA létrehoz egy belső csatlakozást. Ezenkívül a csatlakozási feltétel metaadatok leképezéséből származik.
3.2. Kifejezett belső csatlakozás az Egyértékű Egyesülettel
Ezután megnézzük kifejezett belső csatlakozik hova a JOIN kulcsszót használjuk JPQL lekérdezésünkben:
@Test public void whenJoinKeywordIsUsed_thenCreatesExplicitInnerJoin () {TypedQuery query = entityManager.createQuery ("SELECT d FROM Employee e JOIN e.department d", Department.class); List resultList = lekérdezés.getResultList (); // Állítások ...}
Ebben a lekérdezésben megadtunk egy JOIN kulcsszót és a hozzá tartozó kulcsszót Osztály entitás a FROM záradékban, míg az előzőben egyáltalán nem voltak meghatározva. Ezen a szintaktikai különbségen kívül az eredményül kapott SQL-lekérdezések nagyon hasonlóak lesznek.
Megadhatunk egy választható INNER kulcsszót is:
@Test public void whenInnerJoinKeywordIsUsed_thenCreatesExplicitInnerJoin () {TypedQuery query = entityManager.createQuery ("SELECT d FROM Employee e INNER JOIN e.department d", Department.class); List resultList = lekérdezés.getResultList (); // Állítások ...}
Tehát mivel a JPA hallgatólagosan csatlakozik egy belső csatlakozáshoz, mikor kellene egyértelművé válnunk?
Először, A JPA csak akkor hoz létre implicit belső összekapcsolást, ha megadunk egy útkifejezést. Például, ha csak a Munkavállalós amelyeknek van egy Osztály és nem használunk útvonal kifejezést - e. osztály -, a JOIN kulcsszót kell használnunk lekérdezésünkben.
Másodszor, amikor kifejezetten vagyunk, könnyebb lehet megismerni, mi történik.
3.3. Kifejezett belső csatlakozás a gyűjtemény által értékelt egyesületekhez
Egy másik helyen egyértelműnek kell lennünk a gyűjtemény által értékelt egyesületekkel.
Ha megnézzük adatmodellünket, a Munkavállaló egy a sokhoz viszonya van Telefon. Mint egy korábbi példában, megpróbálhatunk hasonló lekérdezést írni:
E-telefonok kiválasztása az alkalmazottak e-mailből
De ez nem fog igazán működni mint talán szándékoztuk. A kiválasztott egyesület óta - e.telefonok - gyűjteményértékű, kapunk egy listát Gyűjteménys helyett Telefon entitások:
@Test public void whenCollectionValuedAssociationIsSpecifiedInSelect_ThenReturnsCollections () {TypedQuery lekérdezés = entitásManager.createQuery ("E-telefonok kiválasztása az alkalmazottak e-től", Gyűjtemény.osztály); List resultList = lekérdezés.getResultList (); // állítások}
Sőt, ha szűrni akarunk Telefon entitások a WHERE záradékban, a JPA ezt nem engedélyezi. Ez azért van, mert az útkifejezés nem folytatható a gyűjtemény által értékelt társítástól. Tehát például e.phones.number nem érvényes.
Ehelyett létre kell hoznunk egy kifejezett belső összekapcsolást, és létre kell hoznunk egy aliast a Telefon entitás. Ezután megadhatjuk a Telefon entitás a SELECT vagy WHERE záradékban:
@Test public void whenCollectionValuedAssociationIsJoined_ThenCanSelect () {TypedQuery lekérdezés = entitásManager.createQuery ("KIVÁLASZTÁS F MEGKÖZELÍTŐTŐL E-CSATLAKOZÁS e. List resultList = lekérdezés.getResultList (); // Állítások ...}
4. Külső csatlakozás
Ha két vagy több entitás külsőleg csatlakozik, az eredménybe összegyűjtik azokat a rekordokat, amelyek megfelelnek a csatlakozási feltételnek, valamint a bal entitás rekordjait:
@Test public void whenLeftKeywordIsSpecified_thenCreatesOuterJoinAndIncludesNonMatched () {TypedQuery query = entityManager.createQuery ("SELECT DISTINCT d FROM Department d LEFT JOIN d.employees e", Department.class); List resultList = lekérdezés.getResultList (); // Állítások ...}
Itt az eredmény tartalmazni fogja Osztályamelyek társultak Munkavállalós azok is, amelyeknek nincsenek.
Ezt bal külső csatlakozásnak is nevezik. A JPA nem biztosít megfelelő csatlakozásokat ahol páratlan rekordokat is gyűjtünk a megfelelő entitástól. Bár szimulálhatjuk a jobb összekapcsolódásokat azáltal, hogy az FROM záradékban szereplő entitásokat felcseréljük.
5. Csatlakozik a WHERE záradékhoz
5.1. Feltétellel
Két entitást felsorolhatunk a FROM záradékban ésmajd adja meg a WHERE záradékban a csatlakozási feltételt.
Ez különösen akkor lehet hasznos, ha az adatbázis szintű idegen kulcsok nincsenek a helyükön:
@Test public void whenEntitiesAreListedInFromAndMatchedInWhere_ThenCreatesJoin () {TypedQuery query = entityManager.createQuery ("SELECT d FROM Employee e, Department d WHERE e.department = d", Department.class); List resultList = lekérdezés.getResultList (); // Állítások ...}
Itt csatlakozunk Munkavállaló és Osztály entitások, de ezúttal egy feltételt ad meg a WHERE záradékban.
5.2. Feltétel nélkül (derékszögű termék)
Hasonlóképpen, két entitást felsorolhatunk a FROM záradékban anélkül, hogy bármilyen csatlakozási feltételt megadnánk. Ebben az esetben, visszakapunk egy derékszögű terméket. Ez azt jelenti, hogy az első entitás minden rekordja párosul a második entitás minden más rekordjával:
@Test public void whenEntitiesAreListedInFrom_ThenCreatesCartesianProduct () {TypedQuery query = entityManager.createQuery ("SELECT d FROM Employee e, Department d", Department.class); List resultList = lekérdezés.getResultList (); // Állítások ...}
Mint sejtjük, az ilyen típusú lekérdezések nem fognak jól teljesíteni.
6. Többszörös csatlakozás
Eddig két entitást használtunk összekapcsolások végrehajtására, de ez nem szabály. Több entitást is összekapcsolhatunk egyetlen JPQL lekérdezésben:
@Test public void whenMultipleEntitiesAreListedWithJoin_ThenCreatesMultipleJoins () {TypedQuery query = entityManager.createQuery ("SELECT ph FROM Employee e JOIN e.department d JOIN e.phones ph WHERE d.name IS NULL"; Telefon.cl List resultList = lekérdezés.getResultList (); // Állítások ...}
Itt választjuk ki az összeset Telefonok mindenböl Alkalmazottak amelyeknek van egy Osztály. A többi belső összekapcsoláshoz hasonlóan mi sem adunk meg feltételeket, mivel a JPA kivonja ezeket az információkat a metaadatok feltérképezéséből.
7. Csatlakozás
Most beszéljünk a fetch csatlakozásokról. Elsődleges célja a lusta terhelésű társítások lelkes lekérése az aktuális lekérdezéshez.
Itt lelkesen fogunk tölteni Munkavállalóegyesület:
@Test public void whenFetchKeywordIsSpecified_ThenCreatesFetchJoin () {TypedQuery query = entityManager.createQuery ("SELECT d FROM Department d JOIN FETCH d.employees", Department.class); List resultList = lekérdezés.getResultList (); // Állítások ...}
Bár ez a lekérdezés nagyon hasonlít más lekérdezésekre, van egy különbség, és ez az a Munkavállalós mohón vannak terhelve. Ez azt jelenti, hogy ha egyszer hívunk getResultList a fenti tesztben a Osztály az entitásoknak meglesz a sajátjuk alkalmazottak mező betöltve, ezzel megtakarítva még egy utat az adatbázisba.
De vegye figyelembe a memória kompromisszumot. Lehet, hogy hatékonyabbak vagyunk, mert csak egy lekérdezést hajtottunk végre, de be is töltöttük az összeset Osztálys az alkalmazottaik egyszerre emlékeznek.
Végezhetjük a külső beolvasási csatlakozást a külső összekapcsolásokhoz hasonló módon, ahol a bal entitásból olyan rekordokat gyűjtünk, amelyek nem felelnek meg a csatlakozás feltételének. Ezenkívül lelkesen tölti be a megadott társítást:
@Test public void whenLeftAndFetchKeywordsAreSpecified_ThenCreatesOuterFetchJoin () {TypedQuery query = entityManager.createQuery ("SELECT d FROM Department d LEFT JOIN FETCH d.employees", Department.class); List resultList = lekérdezés.getResultList (); // Állítások ...}
8. Összefoglalás
Ebben a cikkben a JPA csatlakozási típusait ismertettük.
Mint mindig, a GitHubon megnézheti ennek és más oktatóanyagok összes mintáját.