Typy spojenia JPA

1. Prehľad

V tomto tutoriáli sa pozrieme na rôzne typy spojení podporované JPA.

Na tento účel použijeme JPQL, dotazovací jazyk pre JPA.

2. Vzorový dátový model

Pozrime sa na náš vzorový dátový model, ktorý použijeme v príkladoch.

Najskôr vytvoríme Zamestnanec subjekt:

@Entity public class Employee {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) private long id; súkromné ​​meno reťazca; súkromný int vek; @ManyToOne oddelenie súkromného oddelenia; @OneToMany (mappedBy = "zamestnanec") súkromné ​​telefóny so zoznamom; // zakladatelia a zakladatelia ...}

Každý Zamestnanec bude pridelený iba jednému Oddelenie:

@Entity Public Class Department {@Id @GeneratedValue (strategy = GenerationType.AUTO) súkromné ​​dlhé ID; súkromné ​​meno reťazca; @OneToMany (mappedBy = "department") zamestnanci súkromného zoznamu; // zakladatelia a zakladatelia ...}

Na záver každý Zamestnanec bude mať viac Telefóns:

@Entity verejná trieda Telefón {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) súkromné ​​dlhé ID; súkromné ​​číslo reťazca; @ManyToOne súkromný zamestnanec; // zakladatelia a zakladatelia ...}

3. Vnútorné spojenia

Začneme vnútornými spojeniami. Keď sú dve alebo viac entít vnútorne spojené, vo výsledku sa zhromaždia iba záznamy, ktoré zodpovedajú podmienke spojenia.

3.1. Implicitné vnútorné spojenie s navigáciou s jednou hodnotou asociácie

Vnútorné spojenia môžu byť implicitné. Ako už z názvu vyplýva, vývojár nešpecifikuje implicitné vnútorné spojenia. Kedykoľvek prechádzame asociáciu s jednou hodnotou, JPA automaticky vytvorí implicitné spojenie:

@Test public void whenPathExpressionIsUsedForSingleValuedAssociation_thenCreatesImplicitInnerJoin () {TypedQuery query = entityManager.createQuery ("SELECT e.department FROM Employee e", Department.class); Zoznam resultList = query.getResultList (); // tvrdenia ...}

Tu je Zamestnanec subjekt má vzťah typu „jedna k jednej“ s Oddelenie subjekt. Ak navigujeme z Zamestnanec subjekt pre ňu Oddelenie - upresnenie e. oddelenie - budeme navigovať v asociácii s jednou hodnotou. Vo výsledku vytvorí JPA vnútorné spojenie. Podmienka spojenia bude ďalej odvodená z mapovania metadát.

3.2. Explicitné vnútorné spojenie s jednohodnotovým združením

Ďalej sa pozrieme na výslovne vnútorné sa spája kde v našom dotaze JPQL používame kľúčové slovo JOIN:

@Test public void whenJoinKeywordIsUsed_thenCreatesExplicitInnerJoin () {TypedQuery query = entityManager.createQuery ("SELECT d FROM Employee e JOIN e.department d", Department.class); Zoznam resultList = query.getResultList (); // tvrdenia ...}

V tomto dotaze zadali sme kľúčové slovo JOIN a súvisiace Oddelenie entita v klauzule FROM, zatiaľ čo v predchádzajúcom neboli vôbec špecifikované. Okrem tohto syntaktického rozdielu však budú výsledné dotazy SQL veľmi podobné.

Môžeme tiež určiť voliteľné kľúčové slovo INNER:

@Test public void whenInnerJoinKeywordIsUsed_thenCreatesExplicitInnerJoin () {TypedQuery query = entityManager.createQuery ("SELECT d FROM Employee e INNER JOIN e.department d", Department.class); Zoznam resultList = query.getResultList (); // tvrdenia ...}

Pretože teda JPA bude implicitne spojené s vnútorným spojením, kedy by sme museli byť výslovní?

Po prvé, JPA vytvára implicitné vnútorné spojenie iba vtedy, keď zadáme výraz cesty. Napríklad, keď chceme vybrať iba Zamestnanecktoré majú a Oddelenie a nepoužívame výraz cesty - e. oddelenie -, mali by sme v našom dotaze použiť kľúčové slovo JOIN.

Po druhé, keď to hovoríme výslovne, môže byť jednoduchšie vedieť, o čo ide.

3.3. Explicitné vnútorné spojenie s asociáciami s hodnotnou zbierkou

Iné miesto musíme to urobiť výslovne so združeniami s hodnotou zbierky.

Ak sa pozrieme na náš dátový model, Zamestnanec má vzťah typu one-to-many Telefón. Rovnako ako v predchádzajúcom príklade sa môžeme pokúsiť napísať podobný dotaz:

VYBERTE e.fóny OD Zamestnanca e

Ale to nebude celkom fungovať ako sme možno zamýšľali. Od vybratého združenia - e.fóny - má hodnotu zbierky, dostaneme zoznam Zbierkas, namiesto Telefón subjekty:

@Test public void whenCollectionValuedAssociationIsSpecifiedInSelect_ThenReturnsCollections () {TypedQuery query = entityManager.createQuery ("SELECT e.phones FROM Employee e", Collection.class); Zoznam resultList = query.getResultList (); // tvrdenia}

Navyše, ak chceme filtrovať Telefón subjekty v klauzule WHERE, JPA to neumožňuje. To je preto, že výraz cesty nemôže pokračovať od asociácie s hodnotou zbierky. Napríklad e.phones.number nie je platný.

Namiesto toho by sme mali vytvoriť explicitné vnútorné spojenie a vytvoriť alias pre Telefón subjekt. Potom môžeme určiť Telefón entita v klauzule SELECT alebo WHERE:

@Test public void whenCollectionValuedAssociationIsJoined_ThenCanSelect () {TypedQuery query = entityManager.createQuery ("SELECT ph FROM Employee e JOIN e.phones ph WHERE ph LIKE '1%'", Phone.class); Zoznam resultList = query.getResultList (); // tvrdenia ...}

4. Vonkajší spoj

Keď sú dva alebo viac subjektov spojené von, záznamy, ktoré vyhovujú podmienke spojenia, a tiež záznamy v ľavej entite sa zhromažďujú vo výsledku:

@Test public void whenLeftKeywordIsSpecified_thenCreatesOuterJoinAndIncludesNonMatched () {TypedQuery query = entityManager.createQuery ("SELECT DISTINCT d FROM Department d LEFT JOIN d.employees e", Department.class); Zoznam resultList = query.getResultList (); // tvrdenia ...}

Tu bude výsledok obsahovať Oddelenies ktoré sa združili Zamestnanecs a tiež také, ktoré žiadne nemajú.

Toto sa označuje aj ako ľavé vonkajšie spojenie. JPA neposkytuje správne spojenia kde tiež zhromažďujeme bezkonkurenčné záznamy od správnej entity. Aj keď môžeme simulovať pravé spojenia výmenou entít v klauzule FROM.

5. Pripája sa k klauzule WHERE

5.1. S Podmienkou

V klauzule FROM a môžeme uviesť dve entitypotom v klauzule WHERE zadajte podmienku spojenia.

To môže byť užitočné, najmä keď nie sú zavedené cudzie kľúče na úrovni databázy:

@Test public void whenEntitiesAreListedInFromAndMatchedInWhere_ThenCreatesJoin () {TypedQuery query = entityManager.createQuery ("SELECT d FROM Employee e, Department d WHERE e.department = d", Department.class); Zoznam resultList = query.getResultList (); // tvrdenia ...}

Tu sa pripájame Zamestnanec a Oddelenie entít, tentokrát však s uvedením podmienky v klauzule WHERE.

5.2. Bez podmienky (karteziánsky súčin)

Podobne môžeme uviesť dve entity v klauzule FROM bez uvedenia akejkoľvek podmienky spojenia. V tomto prípade, dostaneme späť kartézsky produkt. To znamená, že každý záznam v prvej entite je spárovaný s každým ďalším záznamom v druhej entite:

@Test public void whenEntitiesAreListedInFrom_ThenCreatesCartesianProduct () {TypedQuery query = entityManager.createQuery ("SELECT d FROM Employee e, Department d", Department.class); Zoznam resultList = query.getResultList (); // tvrdenia ...}

Ako môžeme hádať, tieto druhy dotazov nebudú mať dobrý výkon.

6. Viacnásobné pripojenie

Doteraz sme na vykonávanie spojení používali dve entity, ale nie je to pravidlo. Môžeme tiež spojiť viac entít v jednom dotaze JPQL:

@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 NOT NULL", Phone.class) Zoznam resultList = query.getResultList (); // tvrdenia ...}

Tu vyberáme všetky Telefóny zo všetkých Zamestnanci ktoré majú a Oddelenie. Podobne ako pri iných vnútorných spojeniach, ani my nešpecifikujeme podmienky, pretože JPA extrahuje tieto informácie z mapovania metadát.

7. Načítajte spojenia

Teraz si povieme niečo o načítaní spojení. Jeho primárne využitie je na nedočkavé načítanie lenivo načítaných asociácií pre aktuálny dotaz.

Tu sa horlivo naložíme Zamestnaneczdruženie:

@Test public void whenFetchKeywordIsSpecified_ThenCreatesFetchJoin () {TypedQuery query = entityManager.createQuery ("SELECT d FROM Department d JOIN FETCH d.employees", Department.class); Zoznam resultList = query.getResultList (); // tvrdenia ...}

Aj keď tento dopyt vyzerá veľmi podobne ako iné dotazy, je tu jeden rozdiel, a to je to the Zamestnanecsú nedočkavo naložené. To znamená, že akonáhle zavoláme getResultList vo vyššie uvedenom teste: Oddelenie subjekty budú mať svoje zamestnancov pole načítané, čím nám ušetrí ďalší výlet do databázy.

Ale pozor na kompromis pamäte. Možno budeme efektívnejší, pretože sme vykonali iba jeden dotaz, ale tiež sme načítali všetky Oddelenies a ich zamestnancom naraz do pamäte.

Môžeme tiež vykonať vonkajšie načítanie spojenia podobným spôsobom ako vonkajšie spojenia, kde zhromažďujeme záznamy od ľavej entity, ktoré sa nezhodujú s podmienkou spojenia. A navyše dychtivo načíta zadanú asociáciu:

@Test public void whenLeftAndFetchKeywordsAreSpecified_ThenCreatesOuterFetchJoin () {TypedQuery query = entityManager.createQuery ("SELECT d FROM Department d LEFT JOIN FETCH d.employees", Department.class); Zoznam resultList = query.getResultList (); // tvrdenia ...}

8. Zhrnutie

V tomto článku sme sa venovali typom spojenia JPA.

Ako vždy, môžete si pozrieť všetky ukážky tohto a ďalších tutoriálov na GitHub.


$config[zx-auto] not found$config[zx-overlay] not found