Mange-til-mange-forhold i JPA

1. Introduksjon

I denne opplæringen ser vi flere måter å håndtere mange-til-mange relasjoner ved hjelp av JPA.

For å presentere ideene bruker vi en modell av studenter, kurs og ulike forhold mellom dem.

For enkelhets skyld viser vi i kodeeksemplene bare attributtene og JPA-konfigurasjonen som er relatert til mange-til-mange-forhold.

2. Grunnleggende mange-til-mange

2.1. Modellering av et forhold mellom mange og mange

Et forhold er en forbindelse mellom to typer enheter. I tilfelle et mange-til-mange forhold, kan begge sider forholde seg til flere forekomster av den andre siden.

Merk at det er mulig for enhetstyper å være i et forhold til seg selv. For eksempel når vi modellerer slektstrær: hver node er en person, så hvis vi snakker om foreldre-barn-forholdet, vil begge deltakerne være en person.

Det gjør imidlertid ikke så stor en forskjell om vi snakker om et forhold mellom enkelt- eller flere enhetstyper. Siden det er lettere å tenke på forholdet mellom to forskjellige enhetstyper, vil vi bruke det til å illustrere sakene våre.

For eksempel når studentene merker kursene de liker: en student kan like mange kurs, og mange studentene kan like det samme kurset:

Som vi vet, i RDBMSes kan vi skape relasjoner med utenlandske nøkler. Siden begge sider skal kunne referere til den andre, vi må lage en egen tabell for å holde de utenlandske nøklene:

Et slikt bord kalles a bli med på bordet. Merk at i en sammenføyningstabell vil kombinasjonen av de utenlandske tastene være den sammensatte primærnøkkelen.

2.2. Implementering i JPA

Modellering av et mange-til-mange forhold til POJOer det er lett. Vi burde inkluderer en Samling i begge klassene, som inneholder elementene til de andre.

Etter det må vi markere klassen med @Enhet, og hovednøkkelen med @Id for å gjøre dem til skikkelige JPA-enheter.

Vi bør også konfigurere forholdstypen. Derfor vi markerer samlingene med @ManyToMany kommentarer:

@Entity class Student {@Id Long id; @ManyToMany Set likte kurser; // tilleggsegenskaper // standardkonstruktører, getters og settere} @Entity class Course {@Id Long id; @ManyToMany Sett liker; // tilleggsegenskaper // standardkonstruktører, getters og setters}

I tillegg må vi konfigurere hvordan vi skal modellere forholdet i RDBMS.

Eiersiden er der vi konfigurerer forholdet, som vi for dette eksemplet velger Student klasse.

Vi kan gjøre dette med @JoinTable kommentar i Student klasse. Vi oppgir navnet på sammenføyningstabellen (kurs_lignende), og de utenlandske nøklene med @JoinColumn kommentarer. De bli med i Kolonne attributt vil koble seg til eiersiden av forholdet, og inverseJoinColumn til den andre siden:

@ManyToMany @JoinTable (name = "course_like", joinColumns = @JoinColumn (name = "student_id"), inverseJoinColumn = @JoinColumn (name = "course_id")) Sett likesCourses;

Merk at bruk av @JoinTable, eller @JoinColumn er ikke nødvendig: JPA vil generere tabell- og kolonnenavn for oss. Imidlertid vil ikke strategien JPA bruker alltid matche navngivningskonvensjonene vi bruker. Derav muligheten for å konfigurere tabell- og kolonnenavn.

På målsiden trenger vi bare å oppgi navnet på feltet, som kartlegger forholdet. Derfor setter vi kartlagt av attributt til @ManyToMany kommentar i Kurs klasse:

@ManyToMany (mappedBy = "likedCourses") Sett likes;

Merk at siden et mange-til-mange forhold har ikke eiersiden i databasen, kunne vi konfigurere sammenføyningstabellen i Kurs klasse og referere den fra Student klasse.

3. Mange-til-mange bruker en sammensatt nøkkel

3.1. Modelleringsforholdsattributter

La oss si at vi vil la studentene rangere kursene. En student kan rangere hvilket som helst antall kurs, og et hvilket som helst antall studenter kan rangere det samme kurset. Derfor er det også et mange-til-mange forhold. Det som gjør det litt mer komplisert er at det er mer i vurderingsforholdet enn at det eksisterer. Vi må lagre poengsummen studenten ga på kurset.

Hvor kan vi lagre denne informasjonen? Vi kan ikke legge det i Student enhet siden en student kan gi forskjellige karakterer til forskjellige kurs. Tilsvarende lagrer den i Kurs enhet ville heller ikke være en god løsning.

Dette er en situasjon når forholdet i seg selv har en attributt.

Ved å bruke dette eksemplet ser det slik ut i et ER-diagram å knytte et attributt til en relasjon:

Vi kan modellere det nesten på samme måte som vi gjorde med det enkle mange-til-mange-forholdet. Den eneste forskjellen er at vi legger til et nytt attributt til sammenføyningstabellen:

3.2. Opprette en sammensatt nøkkel i JPA

Implementeringen av et enkelt forhold mellom mange og mange var ganske greit. Det eneste problemet er at vi ikke kan legge til en eiendom i et forhold på den måten, fordi vi koblet enhetene direkte. Derfor, vi hadde ingen måte å legge til en eiendom i selve forholdet.

Siden vi tilordner DB-attributter til klassefelt i JPA, vi trenger å lage en ny enhetsklasse for forholdet.

Selvfølgelig trenger hver JPA-enhet en primærnøkkel. Fordi hovednøkkelen vår er en sammensatt nøkkel, må vi lage en ny klasse som inneholder de forskjellige delene av nøkkelen:

@ Embeddable class CourseRatingKey implementerer Serializable {@Column (name = "student_id") Long studentId; @Column (name = "course_id") Lang kursId; // standardkonstruktører, getters og setter // hashcode og tilsvarer implementering}

Merk at det er noen sentrale krav, som en sammensatt nøkkelklasse må oppfylle:

  • Vi må merke det med @Embeddable
  • Det må gjennomføres java.io Serialiserbar
  • Vi må gi en implementering av hashcode () og er lik() metoder
  • Ingen av feltene kan være en enhet selv

3.3. Bruke en sammensatt nøkkel i JPA

Ved å bruke denne sammensatte nøkkelklassen kan vi lage enhetsklassen, som modellerer sammenføyningstabellen:

@Entity class CourseRating {@EmbeddedId CourseRatingKey id; @ManyToOne @MapsId ("studentId") @JoinColumn (name = "student_id") Studentstudent; @ManyToOne @MapsId ("courseId") @JoinColumn (name = "course_id") Kurskurs; int vurdering; // standardkonstruktører, getters og setters}

Denne koden er veldig lik en vanlig enhetsimplementering. Vi har imidlertid noen viktige forskjeller:

  • vi brukte @EmbeddedId, for å merke hovednøkkelen, som er en forekomst av CourseRatingKey klasse
  • vi merket student og kurs felt med @MapsId

@MapsId betyr at vi knytter disse feltene til en del av nøkkelen, og de er de fremmede nøklene til et mange-til-ett forhold. Vi trenger det, for som vi nevnte ovenfor, i den sammensatte nøkkelen kan vi ikke ha enheter.

Etter dette kan vi konfigurere de omvendte referansene i Student og Kurs enheter som før:

class Student {// ... @OneToMany (mappedBy = "student") Angi rangeringer; // ...} kurs Kurs {// ... @OneToMany (mappedBy = "kurs") Angi rangeringer; // ...}

Merk at det er en alternativ måte å bruke sammensatte nøkler: @IdClass kommentar.

3.4. Ytterligere egenskaper

Vi konfigurerte forholdene til Student og Kurs klasser som @ManyToOne. Vi kunne gjøre dette fordi vi med den nye enheten strukturelt spaltet mange-til-mange-forholdet til to mange-til-en-forhold.

Hvorfor klarte vi å gjøre dette? Hvis vi inspiserer tabellene nøye i forrige tilfelle, kan vi se at den inneholdt to mange-til-en-forhold. Med andre ord, det er ikke noe mange-til-mange forhold i en RDBMS. Vi kaller strukturene vi lager med join-tabeller for mange-til-mange-forhold fordi det er det vi modellerer.

Dessuten er det tydeligere om vi snakker om mange-til-mange-forhold, for det er vår intensjon. I mellomtiden er et sammenføyningstabell bare en implementeringsdetalj; vi bryr oss egentlig ikke om det.

Videre har denne løsningen en tilleggsfunksjon vi ikke nevnte ennå. Den enkle mange-til-mange-løsningen skaper et forhold mellom to enheter. Derfor kan vi ikke utvide forholdet til flere enheter. Imidlertid har vi ikke denne grensen i denne løsningen: vi kan modellere forhold mellom et hvilket som helst antall enhetstyper.

For eksempel, når flere lærere kan undervise i et kurs, kan studentene rangere hvordan en bestemt lærer underviser i et bestemt kurs. På den måten vil en vurdering være et forhold mellom tre enheter: en student, et kurs og en lærer.

4. Mange-til-mange med en ny enhet

4.1. Modelleringsforholdsattributter

La oss si at vi vil la studentene registrere seg på kurs. Også, vi må lagre poenget da en student registrerte seg for et bestemt kurs. På toppen av det vil vi også lagre hvilken karakter hun fikk på kurset.

I en ideell verden kunne vi løse dette med den forrige løsningen, da vi hadde en enhet med en sammensatt nøkkel. Imidlertid er vår verden langt fra ideell, og studentene gjennomfører ikke alltid et kurs på første forsøk.

I dette tilfellet er det flere forbindelser mellom de samme studentkursparene, eller flere rader, med det samme student_id-kurs_id par. Vi kan ikke modellere den ved hjelp av noen av de tidligere løsningene, fordi alle primærnøklene må være unike. Derfor må vi bruke en egen primærnøkkel.

Derfor, vi kan introdusere en enhet, som vil inneholde attributtene til registreringen:

I dette tilfellet, Registreringsenheten representerer forholdet mellom de to andre enhetene.

Siden det er en enhet, vil den ha sin egen primære nøkkel.

Merk at i den forrige løsningen hadde vi en sammensatt primærnøkkel, som vi opprettet fra de to utenlandske nøklene. Nå vil ikke de to utenlandske nøklene være den delen av primærnøkkelen:

4.2. Implementering i JPA

Siden coure_registration ble et vanlig bord, kan vi lage en vanlig gammel JPA-enhet som modellerer den:

@Entity class CourseRegistration {@Id Long id; @ManyToOne @JoinColumn (name = "student_id") Studentstudent; @ManyToOne @JoinColumn (name = "course_id") Kurskurs; LocalDateTime registeredAt; int karakter; // tilleggsegenskaper // standardkonstruktører, getters og setter}

Vi må også konfigurere forholdene i Student og Kurs klasser:

klasse Student {// ... @OneToMany (mappedBy = "student") Angi registreringer; // ...} klasse Kurs {// ... @OneToMany (mappedBy = "kurs") Angi registreringer; // ...}

Igjen konfigurerte vi forholdet før. Derfor trenger vi bare å fortelle JPA, hvor kan den finne den konfigurasjonen.

Merk at vi kan bruke denne løsningen til å løse det forrige problemet: studenter vurderer kurs. Det føles imidlertid rart å lage en dedikert primærnøkkel med mindre vi må. Fra et RDBMS-perspektiv gir det dessuten ikke mye mening, siden kombinasjonen av de to utenlandske tastene utgjorde en perfekt komposittnøkkel. Utenom det sammensatt nøkkel hadde en klar betydning: hvilke enheter vi forbinder i forholdet.

Ellers er valget mellom disse to implementeringene ofte bare personlige preferanser.

5. Konklusjon

I denne veiledningen så vi hva et mange-til-mange forhold er, og hvordan kan vi modellere det i en RDBMS ved hjelp av JPA.

Vi så tre måter å modellere det på i JPA. Alle tre har forskjellige fordeler og ulemper når det gjelder:

  • kode klarhet
  • DB-klarhet
  • evne til å tildele attributter til forholdet
  • hvor mange enheter kan vi knytte til forholdet, og
  • støtte for flere forbindelser mellom de samme enhetene

Som vanlig er eksemplene tilgjengelige på GitHub.