Vrste pridruživanja JPA

1. Pregled

U ovom ćemo uputstvu pogledati različite tipove spajanja koje podržava JPA.

U tu svrhu koristit ćemo JPQL, jezik upita za JPA.

2. Uzorak modela podataka

Pogledajmo naš model podataka koji ćemo koristiti u primjerima.

Prvo ćemo stvoriti Zaposlenik entitet:

@Entity zaposlenik u javnoj klasi {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) private long id; privatni naziv niza; privatno int doba; @ManyToOne privatni odjel odjela; @OneToMany (mappedBy = "zaposlenik") privatni popis telefona; // geteri i postavljači ...}

Svaki Zaposlenik bit će dodijeljen samo jednom Odjel:

@ Odjel javne klase entiteta {@Id @GeneratedValue (strategy = GenerationType.AUTO) private long id; privatni naziv niza; @OneToMany (mappedBy = "odjel") privatni popis zaposlenika; // geteri i postavljači ...}

Na kraju, svaki Zaposlenik imat će višestruke Telefons:

@ Entity javna klasa Telefon {@Id @GeneratedValue (strategy = GenerationType.IDENTITY) private long id; privatni broj niza; @ManyToOne zaposlenik privatnog zaposlenika; // geteri i postavljači ...}

3. Unutarnji spojevi

Počet ćemo s unutarnjim spajanjima. Kada su dva ili više entiteta međusobno spojena, u rezultat se prikupljaju samo zapisi koji odgovaraju uvjetima pridruživanja.

3.1. Implicitno unutarnje pridruživanje s jednostranom navigacijskom asocijacijom

Unutarnji spojevi mogu biti implicitno. Kao što naziv govori, programer ne navodi implicitne unutarnje spojeve. Kad god krenemo jednostrukom asocijacijom, JPA automatski kreira implicitno pridruživanje:

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

Evo, Zaposlenik entitet ima mnogo-prema-jedan odnos s Odjel entitet. Ako se krećemo iz Zaposlenik entitet njoj Odjel - precizirajući e.odjela - mi ćemo se kretati jednostrukom asocijacijom. Kao rezultat toga, JPA će stvoriti unutarnji spoj. Nadalje, uvjet pridruživanja izveden je iz preslikavanja metapodataka.

3.2. Eksplicitno unutarnje pridruživanje s jednostrukom vrijednošću

Dalje ćemo pogledati eksplicitan unutarnji spaja gdje koristimo ključnu riječ JOIN u našem JPQL upitu:

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

U ovom upitu, naveli smo ključnu riječ JOIN i pridruženo Odjel entitet u klauzuli FROM, dok u prethodnom uopće nisu bili navedeni. Međutim, osim ove sintaktičke razlike, rezultirajući SQL upiti bit će vrlo slični.

Možemo odrediti i opcionalnu ključnu riječ INNER:

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

Dakle, budući da će JPA implicitno pristupiti unutarnjem pridruživanju, kada bismo trebali biti eksplicitni?

Prvo, JPA stvara implicitno unutarnje spajanje samo kada odredimo izraz staze. Na primjer, kada želimo odabrati samo Zaposlenikkoji imaju Odjel i ne koristimo izraz puta - e.odjela -, trebali bismo koristiti JOIN ključnu riječ u našem upitu.

Drugo, kad smo eksplicitni, može biti lakše znati što se događa.

3.3. Eksplicitno unutarnje udruživanje s udruženjima vrijednim kolekcije

Drugo mjesto moramo biti eksplicitni s asocijacijama vrijednim kolekcije.

Ako pogledamo naš model podataka, Zaposlenik ima odnos jedan prema mnogima Telefon. Kao u ranijem primjeru, možemo pokušati napisati sličan upit:

ODABERITE e.fone od zaposlenika e

Ali ovo neće baš uspjeti kao što smo možda i namjeravali. Budući da je odabrana udruga - e. telefoni - vrednuje se kao kolekcija, dobit ćemo popis Kolekcijas, umjesto Telefon entiteta:

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

Štoviše, ako želimo filtrirati Telefon entiteta u klauzuli WHERE, JPA to neće dopustiti. Ovo je zbog izraz puta ne može se nastaviti iz asocijacije vrijedne zbirke. Na primjer, e. telefoni.broj ne vrijedi.

Umjesto toga, trebali bismo stvoriti eksplicitno unutarnje spajanje i stvoriti zamjensko ime za Telefon entitet. Tada možemo odrediti Telefon entitet u klauzuli SELECT ili WHERE:

@Test public void whenCollectionValuedAssociationIsJoined_ThenCanSelect () {TypedQuery query = entityManager.createQuery ("SELECT ph FROM Employee e PRIDRUŽITE se e.phone ph WHERE ph LIKE '1%'", Phone.class); Popis rezultataList = query.getResultList (); // Tvrdnje ...}

4. Vanjsko pridruživanje

Kada su dva ili više entiteta spojena spolja, zapisi koji zadovoljavaju uvjet pridruživanja, a također i zapisi u lijevom entitetu, prikupljaju se u rezultatu:

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

Ovdje će rezultat sadržavati Odjels koje su se udružile Zaposleniki one koji ih nemaju.

To se također naziva lijevim vanjskim spojem. JPA ne osigurava pravi spoj gdje također prikupljamo nepodudarne zapise od pravog entiteta. Iako možemo simulirati ispravna spajanja zamjenom entiteta u klauzuli FROM.

5. Pridružuje se klauzuli WHERE

5.1. Uz uvjet

U klauzuli FROM možemo navesti dva entiteta izatim navedite uvjet pridruživanja u klauzuli WHERE.

To može biti korisno pogotovo kada strani ključevi na razini baze podataka nisu na mjestu:

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

Evo, pridružujemo se Zaposlenik i Odjel entiteta, ali ovaj put specificirajući uvjet u klauzuli WHERE.

5.2. Bez uvjeta (kartezijanski proizvod)

Slično tome, možemo navesti dva entiteta u klauzuli FROM bez navođenja bilo kojeg uvjeta pridruživanja. U ovom slučaju, vratit ćemo kartezijanski proizvod. To znači da je svaki zapis u prvom entitetu uparen sa svakim drugim zapisom u drugom entitetu:

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

Kao što možemo pretpostaviti, ove vrste upita neće imati dobru izvedbu.

6. Višestruki pristupi

Do sada smo koristili dva entiteta za izvođenje spajanja, ali to nije pravilo. Također se možemo pridružiti više entiteta u jednom JPQL upitu:

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

Evo, odabiremo sve Telefoni od svega Zaposlenici koji imaju Odjel. Slično ostalim unutarnjim spajanjima, ne specificiramo uvjete jer JPA izdvaja ove podatke iz metapodataka mapiranja.

7. Dohvati pridruživanja

Sada, razgovarajmo o dohvaćanju pridruživanja. Primarno se koristi za dohvaćanje lijeno učitanih asocijacija željno trenutnog upita.

Evo, željno ćemo utovariti Zaposlenikudruženje:

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

Iako ovaj upit izgleda vrlo slično ostalim upitima, postoji jedna razlika, a to je ta the Zaposleniks su nestrpljivo natovareni. To znači da kad jednom nazovemo getResultList u gornjem testu, Odjel entiteti će imati svoje zaposlenici polje učitano, čime smo uštedjeli još jedan put do baze podataka.

Ali budite svjesni kompromisa s memorijom. Možda smo učinkovitiji jer smo izvršili samo jedan upit, ali smo i učitali sve Odjeli njihovi zaposlenici odjednom u sjećanje.

Spoj za vanjsko dohvaćanje možemo izvršiti na sličan način kao i vanjski spojevi, gdje prikupljamo zapise s lijevog entiteta koji se ne podudaraju s uvjetima pridruživanja. I dodatno, željno učitava navedenu asocijaciju:

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

8. Sažetak

U ovom smo članku razmotrili vrste pridruživanja JPA.

Kao i uvijek, sve uzorke za ovaj i druge tutorijale možete pogledati na GitHubu.