Mest effektive måde at kortlægge et @OneToMany-forhold med JPA og Hibernate

Jeg valgte at åbne dette indlæg med dette citat, fordi jeg er fan af Linus Torvalds.😉

Dette er min allerførste artikel. I denne vil jeg dække alle mulige tilfælde i One-to-Many/Many-to-One entity association. Resterende Many-to-Many og One-to-One vil blive dækket i næste artikler.

Jeg håber, at dette helt sikkert vil hjælpe alle nybegyndere, der ønsker at lære jpa/hibernate, Læs venligst hele stykket 😛

NOTE:

Her har jeg dækket alle mulige tilfælde af One-to-Many/Many-to-One mapping. Blandt alle , Bidirectional `@OneToMany` association er den bedste måde at mappe en one-to-many database relation.

Hibernate association klassificeret i One-to-One, One-to-Many/Many-to-One og Many-to-Many.

  • Retningen af en relation kan enten være bidirektionel eller unidirektionel.
  • En bidirektionel relation har både en ejerside og en omvendt side.
  • En unidirektionel relation har kun en ejerside. Den ejende side af en relation bestemmer, hvordan Persistence-køretiden foretager opdateringer af relationen i databasen.
  • En unidirektionel er en relation, hvor den ene side ikke kender til relationen.
  • I en unidirektionel relation er det kun den ene enhed, der har et relationsfelt eller en egenskab, der henviser til den anden. Linjepost vil f.eks. have et relationsfelt, der identificerer Produkt, men Produkt vil ikke have et relationsfelt eller en egenskab for Linjepost. Med andre ord kender Line Item til Product, men Product ved ikke, hvilke Line Item-instanser der henviser til den.

Bidirektionelle relationer:

  • Bidirektionelle relationer giver navigationsadgang i begge retninger, så du kan få adgang til den anden side uden eksplicitte forespørgsler.
  • I en bidirektionel relation har hver enhed et relationsfelt eller en egenskab, der henviser til den anden enhed. Via relationsfeltet eller -egenskaben kan en entitetsklasses kode få adgang til det relaterede objekt. Hvis en entitet har et relateret felt, siges det, at entiteten “kender” til sit relaterede objekt. Hvis Order f.eks. ved, hvilke Instanser af Line Item den har, og hvis Line Item ved, hvilken Order den tilhører, har de en tovejsrelation.

Bidirektionelle relationer skal følge disse regler.

  • Den omvendte side af en tovejsrelation skal henvise til sin ejerside (Entity, der indeholder den fremmede nøgle) ved at bruge mappedBy-elementet i annotationen @OneToOne, @OneToMany eller @ManyToMany. mappedBy-elementet angiver den egenskab eller det felt i den enhed, som er ejer af relationen.
  • Den mange side af @ManyToOne bidirektionelle relationer må ikke definere mappedBy-elementet. Den mange side er altid den ejende side af relationen.
  • For @OneToOne bidirektionelle relationer svarer den ejende side til den side, der indeholder @JoinColumn, dvs. den tilsvarende fremmednøgle.
  • For @ManyToMany tovejsrelationer kan begge sider være den ejende side.

@OneToMany-relation med JPA og Hibernate

Simpelt sagt betyder one-to-many-mapping, at én række i en tabel er mappet til flere rækker i en anden tabel.

Hvornår skal man bruge en til mange-mapping

Brug en til mapping for at skabe 1…N-relation mellem enheder eller objekter.

Vi skal skrive to enheder, dvs. Company og Branch således, at flere filialer kan være tilknyttet en enkelt virksomhed, men en enkelt filial kan ikke deles mellem to eller flere virksomheder.

Hibernate en til mange mappingsløsninger:

  1. En til mange mapping med fremmednøgletilknytning
  2. En til mange mapping med join tabel

Dette problem kan løses på to forskellige måder:

Den ene er at have en fremmednøglekolonne i branchetabel dvs. company_id. Denne kolonne vil henvise til primærnøglen i tabellen Firma. På denne måde kan to filialer ikke være tilknyttet flere virksomheder.

Den anden metode er at have en fælles jointabel, lad os sige Company_Branch, Denne tabel vil have to kolonner, dvs. company_id, som vil være en fremmed nøgle, der henviser til primærnøglen i virksomhedstabellen, og tilsvarende branch_id, som vil være en fremmed nøgle, der henviser til primærnøglen i filialtabellen.

Hvis @OneToMany/@ManyToOne ikke har en spejlende @ManyToOne/@OneToMany-forbindelse på henholdsvis barnets side, så er @OneToMany/@ManyToOne-forbindelsen ensrettet.

@OneToMany Ensrettet Forbindelse

I denne tilgang vil en hvilken som helst enhed være ansvarlig for at skabe forbindelsen og vedligeholde den. Enten erklærer virksomheden forholdet som en til mange, eller filialen erklærer forholdet fra sin ende som mange til en.

CASE 1: (Kortlægning med fremmednøgleforbindelse)

Hvis vi kun bruger @OneToMany, vil der være 3 tabeller. Såsom company, branch og company_branch.

NOTAT:
I ovenstående eksempel har jeg brugt udtryk som cascade, orphanRemoval, fetch og targetEntity, som jeg vil forklare i mit næste indlæg.

Tabel company_branch vil have to fremmednøgler company_id og branch_id.

Nu, hvis vi persisterer en virksomhed og to filial(er):

Hibernate vil udføre følgende SQL-statements:

  • Forbindelsen @OneToMany er pr. definition en parent(ikke-ejende)-forbindelse, selv om det er en ensrettet eller en tovejs-forbindelse. Kun den overordnede side af en association giver mening at kaskade dens entitetstilstandsovergange til børn.
  • Når man persisterer Company-entiteten, vil kaskaden også propagere persist-operationen til de underliggende grenbørn. Ved fjernelse af en Branch fra branchesamlingen slettes foreningsrækken fra forbindelsestabellen, og orphanRemoval-attributten udløser også en fjernelse af en gren.

De ensrettede foreninger er ikke særlig effektive, når det drejer sig om at fjerne underordnede enheder. I dette særlige eksempel sletter Hibernate alle databaseunderordnede poster i databasen og genindsætter dem, der stadig findes i persistenskonteksten i hukommelsen, når persistensammenhængen skylles.

På den anden side er en tovejs @OneToMany-association meget mere effektiv, fordi den underordnede enhed kontrollerer associeringen.

CASE 2: (Kortlægning med fremmednøgleassociation)

Hvis vi kun bruger @ManyToOne, vil der være 2 tabeller. Såsom firma, filial.

Denne ovenstående kode vil generere 2 tabeller Company(company_id,name) og Branch(branch_id,name,company_company_id). Her er Branch den ejende side, da den har en fremmed nøgleforbindelse.

CASE 3: (Mapping med fremmed nøgleforbindelse)

Hvis vi bruger både @ManyToOne og @OneToMany, vil det skabe 3 tabeller Company(id,name), Branch(id,name,company_id), Company_Branch(company_id,branch_id)

Denne nedenstående mapping ser måske ud som bi-direktionel, men det er det ikke. Det definerer ikke én bi-direktionel relation, men to separate uni-direktionelle relationer.

CASE 4: (Unidirectional @OneToMany med @JoinColumn)(Mapping med fremmednøgleforbindelse)

Hvis vi bruger @OneToMany med @JoinColumn, vil der være 2 tabeller. Såsom virksomhed, filial

I ovenstående enhed inde i @JoinColumn henviser navn til fremmednøglekolonnens navn, som er companyId i.e company_id, og rferencedColumnName angiver den primære nøgle, dvs. id, for den enhed (Company), som den fremmede nøgle companyId henviser til.

CASE 5: (Kortlægning med fremmednøgleforbindelse)

Hvis vi bruger @ManyToOne med @JoinColumn, vil der være to tabeller. Såsom firma,filial.

Anmærkningen @JoinColumn hjælper Hibernate med at finde ud af, at der er en company_id Foreign Key-kolonne i filialtabellen, der definerer denne association.

Branch vil have den fremmede nøgle company_id, så det er ejersiden.

Nu, hvis vi persisterer 1 virksomhed og 2 filial(er):

når vi fjerner den første post fra børnesamlingen:

company.getBranches().remove(0);

Hibernate udfører to udsagn i stedet for ét :

  • Først vil den gøre fremmednøglefeltet til null(for at bryde forbindelsen med forælder), derefter vil den slette posten.
Update branch set branch_id = null where where id = 1 delete from branch where id = 1 ;

CASE 6: (Mapping med join-tabel)

Lad os nu se på one-to-many-relationen, hvor Person(id,name) bliver associeret med flere Veichle(id,name,number), og flere køretøjer kan tilhøre den samme person.

En dag, mens benzinsherif Carlos var på motorvejen, fandt han nogle få forladte køretøjer, sandsynligvis stjålne. Nu skal sheriffen opdatere køretøjsoplysningerne (nummer, navn) i deres database, men det største problem er, at der ikke er nogen ejer til disse køretøjer, så person_id(foreign key )feltet forbliver nul.

Nu gemmer sheriffen to stjålne køretøjer i db som følger:

Hibernate vil udføre følgende SQL-anvisninger:

insert into vehicle (id, name, person_id) values (1, "ford", null); insert into vehicle (id, name, person_id) values (2, "gmc", null);id |name|person_id|
-----|----|---------|
1 |ford|NULL |
2 |benz|NULL |
---------------------

Denne ovenstående strategi vil tvinge os til at sætte nulværdier i kolonnen for at håndtere valgfrie relationer.

Typisk tænker vi på many-to-many-relationer, når vi overvejer en join table, men ved at bruge en join-tabel kan vi i dette tilfælde eliminere disse nulværdier:

Denne strategi bruger en join-tabel til at gemme associationerne mellem filial- og virksomhedsenhederne. @JoinTable annotation er blevet brugt til at lave denne association.

I dette eksempel har vi anvendt @JoinTable på Vehicle side(Many Side).

Lad os se, hvordan databaseskemaet vil se ud:

Ovenstående kode vil generere 3 tabeller som person(id,name), vehicle(id,name) og vehicle_person(vehicle_id,person_id). Her vil vehicle_person have en fremmednøglerelation til både person- og køretøjsenhederne.

Så når sheriffen gemmer køretøjsoplysninger, skal der ikke persisteres nogen nulværdi til køretøjstabellen, fordi vi har en fremmednøglerelation i vehicle_person-tabellerne og ikke i køretøjstabellen.

Vehicle vehicle1 = new Vehicle("ford", 1);
Vehicle vehicle2 = new Vehicle("gmc", 2);
List<Vehicle> vehicles = new ArrayList<>(); vehicles.add(vehicle1);
vehicles.add(vehicle2);
entityManager.persist(veichles);

Hibernate vil udføre følgende SQL-angivelser:

insert into vehicle (id, name) values (1, "ford"); insert into vehicle (id, name) values (2, "gmc");id |name|
-----|----|
1 |ford|
2 |benz|
-----------

Dette ovenstående eksempel viser, hvordan vi slap for indsættelse af nulværdi.

@OneToMany Bi-directional Relationship

  • Den bidirektionale @OneToMany-association kræver også en @ManyToOne-association på børnesiden. Selv om domænemodellen udsætter to sider til at navigere i denne association, har den relationelle database bag kulisserne kun én fremmed nøgle for denne relation.
  • Alle tovejsforbindelser skal kun have én ejerside (barnesiden), idet den anden side kaldes den omvendte (ellermappedBy) side.
  • Hvis vi bruger @OneToMany med attributten mappedBy indstillet, har vi en tovejsforbindelse, hvilket betyder, at vi skal have en @ManyToOne-forbindelse på den børneside, som mappedBy refererer til.
  • Elementet mappedBy definerer en tovejsforbindelse. Denne attribut gør det muligt at henvise til de tilknyttede entiteter fra begge sider.

Den bedste måde at kortlægge en @OneToMany-association på er at stole på @ManyToOne-siden til at propagere alle ændringer i entitetens tilstand.

Hvis vi persisterer 2 Branch(s)

Hibernate genererer kun én SQL-anvisning for hver persisteret Branch-entitet:

insert into company (name, id) values ("company1",1); 
insert into branch (company_id, name, id) values (1,"branch1",1); insert into branch (company_id, name, id) values (1,"branch2",2);

Hvis vi fjerner en Branch:

Company company = entityManager.find( Company.class, 1L ); Branch branch = company.getBranches().get(0); company.removeBranches(branch);

Der er kun én delete SQL-anvisning, der udføres:

delete from Branch where id = 1

Så den tovejs @OneToMany-association er den bedste måde at mappe en one-to-many-databasesammenhæng på, når vi virkelig har brug for samlingen på den overordnede side af associationen.

@JoinColumn Angiver en kolonne til sammenføjning af en entitetsforening eller en elementopsamling. Annotationen @JoinColumn angiver, at denne entitet er ejeren af relationen. Det vil sige, at den tilsvarende tabel har en kolonne med en fremmed nøgle til den tabel, der henvises til.

I ovenstående eksempel har ejeren Entity Branch, en Join Column ved navn company_id, der har en fremmed nøgle til den ikke-ejende enhed Company.

Når der dannes en tovejsforbindelse, skal programudvikleren sørge for, at begge sider hele tiden er synkroniserede. addBranches() og removeBranches() er hjælpeværktøjer metoder, der synkroniserer begge ender, når et underordnet element (dvs. Branch) tilføjes eller fjernes.

I modsætning til den ensrettede @OneToMany er den tovejsforbindelse meget mere effektiv, når det drejer sig om at administrere samlingens persistenstilstand.

Hver elementfjernelse kræver kun en enkelt opdatering (hvor den fremmede nøglekolonne sættes til NULL), og hvis den underordnede enheds livscyklus er bundet til dens ejende forælder, så barnet ikke kan eksistere uden sin forælder, kan vi annotere tilknytningen med attributten orphan-removal, og hvis vi ophæver tilknytningen af barnet, udløser det også en sletteanvisning på den faktiske barntabellerække.

BEMÆRK:

  • I ovenstående dobbeltrettede eksempel henviser udtrykket Parent og Child i Entity/OO/Model (dvs. i java-klassen) til henholdsvis Non-woning/Inverse og Owning side i SQL.

In java point of view Company is the Parent and branch is the child here. Da en filial ikke kan eksistere uden forælder.

I SQL er filial ejersiden, og firmaet er non-woning-siden (omvendt). Da der er 1 virksomhed for N filialer, indeholder hver filial en fremmed nøgle til den virksomhed, den tilhører, hvilket betyder, at filialen “ejer” (eller bogstaveligt talt indeholder) forbindelsen (oplysningerne). Dette er præcis det modsatte fra OO/modelverdenen.

@OneToMany Bi-directional Relationship(Mapping with join table)

Fra CASE 6 har vi Unidirectional mapping med @JoinTable, så hvis vi tilføjer mappedBy attribut til Person entitet så vil relationen blive bi-directional.

SUMMARY

Der er flere ting at bemærke om førnævnte mapping:

  • Associeringen @ManyToOne bruger FetchType.LAZY, fordi vi ellers ville falde tilbage til EAGER-fetching, hvilket er dårligt for ydelsen.
  • De tovejsforbindelser skal altid opdateres på begge sider, og derfor skal Forældre-siden indeholde kombinationen addChild og removeChild(Forældre-siden Firmaet indeholder to nyttemetoder addBranches og removeBranches). Disse metoder sikrer, at vi altid synkroniserer begge sider af foreningen for at undgå problemer med objekt- eller relationelle datakorruption.
  • Den underordnede enhed, Branch, implementerer metoderne equals og hashCode. Da vi ikke kan stole på en naturlig identifikator til lighedskontrol, er vi nødt til at bruge identifikatoren for enheden i stedet. Vi skal dog gøre det korrekt, så ligheden er konsistent på tværs af alle overgange af entitetstilstande. Da vi er afhængige af lighed for removeBranches, er det god praksis at tilsidesætte equals og hashCode for den underordnede enhed i en bidirektionel association.
  • At @OneToMany association er pr. definition en overordnet association, selv om det er en unidirektionel eller en bidirektionel. Det er kun forældresiden af en association, der giver mening at kaskade dens entitetstilstandsovergange til børn.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.