Odnos mnogi prema mnogima u JPA

1. Uvod

U ovom uputstvu vidjet ćemo više načina za nositi se s odnosima mnogo prema mnogo koristeći JPA.

Za predstavljanje ideja poslužit ćemo se modelom studenata, tečajeva i različitih odnosa među njima.

Radi jednostavnosti, u primjerima koda prikazat ćemo samo atribute i JPA konfiguraciju koja je povezana s odnosima mnogo prema mnogo.

2. Osnovni Mnogi-prema-mnogima

2.1. Modeliranje odnosa mnogi-prema-mnogima

Odnos je veza između dvije vrste entiteta. U slučaju odnosa mnogo-prema-više, obje se strane mogu povezati s više slučajeva druge strane.

Imajte na umu da je moguće da tipovi entiteta budu u vezi sami sa sobom. Na primjer, kada modeliramo obiteljska stabla: svaki čvor je osoba, pa ako govorimo o odnosu roditelj-dijete, oba će sudionika biti osoba.

Međutim, nema tolike razlike govorimo li o odnosu između jednog ili više entiteta. Budući da je lakše razmišljati o odnosima između dvije različite vrste entiteta, to ćemo koristiti za ilustraciju svojih slučajeva.

Na primjer, kada studenti označe tečajeve koji im se sviđaju: student može voljeti puno tečajevi i puno studentima se može svidjeti isti tečaj:

Kao što znamo, u RDBMS-ovima možemo stvoriti odnose s inozemnim ključevima. Budući da bi obje strane mogle imati referencu na drugu, moramo stvoriti zasebnu tablicu za držanje stranih ključeva:

Takva se tablica naziva a pridružiti se stolu. Imajte na umu da će u tablici spajanja kombinacija stranih ključeva biti njezin kompozitni primarni ključ.

2.2. Provedba u JPA

Modeliranje odnosa mnogo-prema-mnogo s POJO-ima je lako. Trebali bismo uključuju a Kolekcija u oba razreda, koji sadrži elemente ostalih.

Nakon toga moramo razred označiti s @ Entitet, a primarni ključ s @Iskaznica kako bi ih učinili odgovarajućim JPA entitetima.

Također, trebali bismo konfigurirati vrstu veze. Stoga obilježavamo kolekcije sa @ManyToMany napomene:

@ Entity class Student {@Id Long id; @ManyToMany Set likedCourses; // dodatna svojstva // standardni konstruktori, getteri i postavljači} @Entity class Course {@Id Long id; @ManyToMany Set lajkova; // dodatna svojstva // standardni konstruktori, getteri i postavljači}

Uz to, moramo konfigurirati kako modelirati odnos u RDBMS-u.

Vlasnička strana je mjesto na kojem konfiguriramo odnos, a koji ćemo u ovom primjeru odabrati Student razred.

To možemo učiniti s @JoinTable bilješka u Student razred. Navodimo naziv tablice za pridruživanje (naravno_nalik), a strani ključevi s @JoinColumn bilješke. The joinColumn atribut će se povezati s vlasničkom stranom odnosa, a inverseJoinColumn na drugu stranu:

@ManyToMany @JoinTable (name = "course_like", joinColumns = @JoinColumn (name = "student_id"), inverseJoinColumns = @JoinColumn (name = "course_id")) Postavi označene tečajeve;

Imajte na umu da pomoću @JoinTable, ili čak @JoinColumn nije potrebno: JPA će generirati imena tablica i stupaca za nas. Međutim, strategija koju JPA koristi neće se uvijek podudarati s konvencijama imenovanja koje koristimo. Stoga postoji mogućnost konfiguriranja imena tablica i stupaca.

Na ciljnoj strani moramo navesti samo ime polja koje mapira odnos. Stoga smo postavili mapiranBy atribut @ManyToMany bilješka u Tečaj razred:

@ManyToMany (mappedBy = "likedCourses") Postavi lajkove;

Imajte na umu, da od odnos mnogo prema mnogima nema vlasničku stranu u bazi podataka, mogli bismo konfigurirati tablicu pridruživanja u Tečaj klase i referenciraju se iz Student razred.

3. Mnogo prema mnogima pomoću kompozitnog ključa

3.1. Modeliranje atributa odnosa

Recimo da želimo pustiti studente da ocjenjuju tečajeve. Student može ocijeniti bilo koji broj tečajeva, a bilo koji broj studenata može ocijeniti isti tečaj. Stoga je to i odnos mnogo-prema-mnogima. Ono što ga čini malo složenijim je to u odnosu na ocjenu postoji više od činjenice da on postoji. Moramo pohraniti ocjenu koju je student dao na tečaju.

Gdje možemo pohraniti ove podatke? Ne možemo ga staviti u Student entitet jer student može dati različite ocjene različitim tečajevima. Slično tome, pohranjivanje u Tečaj entitet ne bi bilo dobro rješenje.

Ovo je situacija kada sama veza ima atribut.

Koristeći ovaj primjer, pridruživanje atributa relaciji izgleda ovako u ER dijagramu:

Možemo ga modelirati gotovo na isti način kao što smo to učinili s jednostavnim odnosom mnogo-prema-mnogima. Jedina razlika je što smo pridružili novi atribut tablici spajanja:

3.2. Stvaranje kompozitnog ključa u JPA

Provedba jednostavnog odnosa mnogi prema mnogima bila je prilično jednostavna. Jedini je problem što na taj način ne možemo dodati svojstvo u odnos, jer smo entitete izravno povezali. Stoga, nismo imali načina da dodamo svojstvo samoj vezi.

Budući da DB atribute mapiramo u polja klase u JPA, moramo stvoriti novu klasu entiteta za odnos.

Naravno, svaki JPA entitet treba primarni ključ. Budući da je naš primarni ključ složeni ključ, moramo stvoriti novu klasu koja će sadržavati različite dijelove ključa:

@Embeddable class CourseRatingKey implementira serializable {@Column (name = "student_id") Long studentId; @Column (name = "course_id") Long courseId; // standardni konstruktori, getteri i postavljači // hashcode i jednako provođenje}

Imajte na umu da ih ima ključni zahtjevi koje mora ispuniti složena klasa ključeva:

  • Moramo ga označiti s @Embeddable
  • Mora provesti java.io.Serializable
  • Moramo osigurati provedbu hashcode () i jednako () metode
  • Nijedno polje ne može biti entitet

3.3. Korištenje kompozitnog ključa u JPA

Pomoću ove složene klase ključeva možemo stvoriti klasu entiteta koja modelira tablicu spajanja:

@Entity class CourseRating {@EmbeddedId CourseRatingKey id; @ManyToOne @MapsId ("studentId") @JoinColumn (name = "student_id") Student student; @ManyToOne @MapsId ("courseId") @JoinColumn (name = "course_id") Tečaj tečaja; int ocjena; // standardni konstruktori, getteri i postavljači}

Ovaj je kod vrlo sličan uobičajenoj implementaciji entiteta. Međutim, imamo neke ključne razlike:

  • koristili smo @EmbeddedId, za označavanje primarnog ključa, koji je instanca CourseRatingKey razred
  • obilježili smo student i tečaj polja sa @MapsId

@MapsId znači da ta polja vežemo za dio ključa, a oni su strani ključevi odnosa više-prema-jednom. Potreban nam je, jer kao što smo gore spomenuli, u složenom ključu ne možemo imati entitete.

Nakon toga možemo konfigurirati inverzne reference u Student i Tečaj entiteti kao prije:

razred Student {// ... @OneToMany (mappedBy = "student") Postavi ocjene; // ...} tečaj klase {// ... @OneToMany (mappedBy = "course") Postavljanje ocjena; // ...}

Imajte na umu da postoji alternativni način upotrebe složenih tipki: @IdClass bilješka.

3.4. Daljnje karakteristike

Konfigurirali smo odnose na Student i Tečaj klase kao @ManyToOne. To bismo mogli učiniti jer smo s novim entitetom strukturno razgradili odnos mnogo prema mnogima na dva odnosa mnogo prema jednom.

Zašto smo to mogli? Ako pažljivo pregledamo tablice u prethodnom slučaju, možemo vidjeti da su sadržavale dvije veze mnogo-prema-jednom. Drugim riječima, u RDBMS-u ne postoji odnos mnogo-prema-mnogima. Strukture koje stvaramo pomoću tablica udruživanja nazivamo odnosima mnogo prema mnogim, jer to je ono što modeliramo.

Osim toga, jasnije je govorimo li o vezama mnogo prema mnogo, jer to nam je namjera. U međuvremenu, tablica spajanja samo je detalj implementacije; nas zapravo nije briga za to.

Štoviše, ovo rješenje ima dodatnu značajku koju još nismo spomenuli. Jednostavno rješenje mnogo-do-mnogih stvara odnos između dva entiteta. Stoga ne možemo proširiti odnos na više entiteta. Međutim, u ovom rješenju nemamo ovo ograničenje: možemo modelirati odnose između bilo kojeg broja vrsta entiteta.

Na primjer, kada više nastavnika može predavati tečaj, studenti mogu ocijeniti kako određeni učitelj predaje određeni tečaj. Na taj bi način ocjena bila odnos između tri entiteta: studenta, tečaja i nastavnika.

4. Mnogo-do-mnogih s novim entitetom

4.1. Modeliranje atributa odnosa

Recimo da želimo dopustiti studentima da se prijave na tečajeve. Također, moramo pohraniti točku kada se student prijavio za određeni tečaj. Povrh toga, želimo pohraniti i ocjenu koju je dobila na tečaju.

U idealnom svijetu to bismo mogli riješiti prethodnim rješenjem, kada smo imali entitet sa složenim ključem. Međutim, naš je svijet daleko od ideala i studenti ne prolaze tečaj uvijek iz prvog pokušaja.

U ovom slučaju postoje višestruke veze između istih parova studenata i kolegija, ili više redaka, s istim student_id-course_id parovi. Ne možemo ga modelirati pomoću bilo kojeg od prethodnih rješenja, jer svi primarni ključevi moraju biti jedinstveni. Stoga moramo koristiti zasebni primarni ključ.

Stoga, možemo uvesti entitet, koji će sadržavati atribute registracije:

U ovom slučaju, entitet za registraciju predstavlja odnos između druga dva entiteta.

Budući da je entitet, imat će svoj primarni ključ.

Imajte na umu da smo u prethodnom rješenju imali složeni primarni ključ koji smo stvorili od dva strana ključa. Sada dva strana ključa neće biti dio primarnog ključa:

4.2. Provedba u JPA

Budući da je coure_registration postala redovita tablica, možemo stvoriti obični stari JPA entitet koji je modelira:

@Entity class CourseRegistration {@Id Long id; @ManyToOne @JoinColumn (name = "student_id") Student student; @ManyToOne @JoinColumn (name = "course_id") Tečaj tečaja; LocalDateTime registeredAt; int ocjena; // dodatna svojstva // standardni konstruktori, getteri i postavljači}

Također, moramo konfigurirati odnose u Student i Tečaj klase:

razred Student {// ... @OneToMany (mappedBy = "student") Postavite registracije; // ...} tečaj klase {// ... @OneToMany (mappedBy = "courses") Postavite registracije; // ...}

Opet smo odnos konfigurirali prije. Stoga JPA-i moramo reći samo gdje može pronaći tu konfiguraciju.

Imajte na umu da bismo ovo rješenje mogli koristiti za rješavanje prethodnog problema: ocjenjivanje studenata. Međutim, čudno se čini stvoriti namjenski primarni ključ ako to ne moramo. Štoviše, iz perspektive RDBMS-a to nema puno smisla, jer je kombinacija dva strana ključa savršena kompozitna tipka. Osim toga složeni ključ imao je jasno značenje: koje cjeline povezujemo u odnosu.

Inače, izbor između ove dvije implementacije često je jednostavno osobna preferencija.

5. Zaključak

U ovom uputstvu vidjeli smo što je odnos mnogo-prema-mnogima i kako ga možemo modelirati u RDBMS-u koristeći JPA.

U JPA smo vidjeli tri načina za modeliranje. Sve tri imaju različite prednosti i nedostatke kada su u pitanju:

  • jasnoća koda
  • DB jasnoća
  • sposobnost dodjeljivanja atributa vezi
  • koliko entiteta možemo povezati s vezom i
  • podrška za višestruke veze između istih entiteta

Kao i obično, primjeri su dostupni na GitHubu.