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.