JPA og Hibernate giver forskellige metoder til at fortsætte nye og opdatere eksisterende enheder. Du kan vælge mellem JPA ‘s persistate og flet og Hibernate’ s gemme-og opdateringsmetoder.
det ser ud til, at der er 2 par af 2 metoder, der gør det samme. Du kan bruge metoderne vedvarende og gem til at gemme en ny enhed, og metoderne flettes og opdateres for at gemme ændringerne af en løsrevet enhed i databasen. Derfor spekulerer mange udviklere på, hvilke af disse metoder de skal bruge. Lad os se nærmere på detaljerne og små forskelle i disse metoder.
særlig tak til Steve Ebersole (Lead Developer – Hibernate ORM), der gav sin feedback og stor indsigt i nogle af hibernates skjulte implementeringsdetaljer!
- Entity State Transitions
- vedvarende en ny enhed ved hjælp af vedvarende eller gem
- specifikation vs. Proprietær API
- Returtyper og udførelse af KVL-udsagn
- ikke genereret
- genereret med IDENTITETSSTRATEGI
- genereret med SEKVENSSTRATEGI
- genereret med TABELSTRATEGI
- hvilken skal man vælge?
- opdatering af en løsrevet enhed
- JPA ‘s fletningsmetode
- Hibernate ‘s opdateringsmetode
- hvilken skal man vælge?
- opdatering af en administreret enhed
Entity State Transitions
før vi kommer ind i detaljerne i disse 4 metoder, skal jeg give dig en hurtig introduktion til JPA ‘ s entity lifecycle states.
hvis en enhed er knyttet til den aktuelle persistenskontekst, har den livscyklustilstanden forvaltet. Det betyder, at det er kortlagt til en databasepost. Din persistensudbyder genererer de nødvendige Indsæt-og OPDATERINGSERKLÆRINGER for at udbrede alle ændringer. En administreret enhed gemmes også i cachen på 1.niveau.
når du opretter en ny enhed, er den i forbigående tilstand. Det forbliver i denne tilstand, indtil du vedhæfter det til den aktuelle vedholdenhedskontekst. Jeg vil vise dig, hvordan du kan gøre det med JPA ‘s persistate and Hibernate’ s save method, i det følgende afsnit. Så længe en enhed er i forbigående tilstand, kortlægges den ikke til en databasepost og styres ikke af nogen persistenskontekst.
enheder i den fritliggende livscyklustilstand styres ikke længere af persistenskonteksten. Det kan være tilfældet, fordi du lukkede persistenskonteksten, eller du eksplicit løsrev enheden fra den aktuelle kontekst. Jeg vil komme ind på flere detaljer om, hvordan du kan vedhæfte disse enheder igen med JPA ‘s Flet og Hibernate’ s opdateringsmetoder i en senere del af dette indlæg.
og den sidste livscyklustilstand fjernes. Disse enheder var tidligere i staten forvaltes, før du planlagt dem til fjernelse. Fjernelse af enheder er uden for rammerne af dette indlæg, så jeg vil ikke komme ind for mange detaljer om det. Du kan planlægge en enhed til fjernelse ved at kalde fjernelsesmetoden på EntityManager-grænsefladen.
vedvarende en ny enhed ved hjælp af vedvarende eller gem
når du opretter et nyt objekt, er det i tilstanden transient lifecycle. Det kortlægger ikke nogen databasepost.
Author a = new Author();a.setFirstName("Thorben");a.setLastName("Janssen");
du skal vedhæfte enheden til en persistenskontekst, så den styres og bliver vedvarende i databasen. Du kan enten bruge JPA ‘s persistate eller Hibernate’ s save-metode til at gøre det. Begge metoder synes at gøre det samme, men der er et par forskelle.
specifikation vs. Proprietær API
den mest åbenlyse forskel er, at JPA-specifikationen definerer vedvarende metode. Du kan bruge det med alle JPA implementeringer. Gem-metoden er på den anden side Dvalespecifik. Det er derfor ikke tilgængeligt i andre JPA-implementeringer.
men det er kun relevant, hvis du vil kunne erstatte Hibernate med en anden JPA-implementering, som Eclipse Link eller openjpa.
Returtyper og udførelse af KVL-udsagn
en anden åbenbar forskel mellem disse 2 metoder er deres returtype. JPA ‘ s vedvarende metode returnerer ugyldig, og hibernates gemte metode returnerer enhedens primære nøgle.
det kan virke som en enorm forskel, især når du kigger nærmere på Hibernate ‘s Javadoc og JPA-specifikationen:
- Javadoc af Hibernate’ s save-metode angiver, at den først genererer den primære nøgleværdi:
Fortsæt den givne forbigående forekomst, først tildele en genereret identifikator.
Javadoc Session.Gem (enhed) - du finder ingen oplysninger om dette i JPA-specifikationen. Det definerer ikke, hvornår den primære nøgleværdi skal tildeles. Så, persistensudbyderen kan gøre det til enhver tid mellem opkaldet til persistensmetoden og flush af persistenskonteksten.
i de fleste tilfælde gør det ingen forskel, hvis du kalder Gem eller vedvarende metode. Hibernate bruger navnet på objektklassen og den primære nøgleværdi til at gemme enheden i cachen på første niveau. Det har derfor brug for en primær nøgleværdi, når den udfører vedvarende metode.
i næsten alle situationer genererer Hibernate den primære nøgleværdi med det samme og udløser om nødvendigt en sætning, når du kalder metoden vedvarende eller gem.
men det er ikke tilfældet, hvis du bruger IDENTITETSSTRATEGIEN og forsøger at fortsætte en enhed uden en aktiv transaktion eller med FlushMode.MANUEL. Hvis du kalder vedvarende metode i en af disse situationer, Hibernate forsinker udførelsen af sætningen Indsæt og opretter en midlertidig primær nøgle værdi. Men hvis du kalder Gem-metoden, udfører Hibernate sætningen indsæt med det samme og henter den primære nøgleværdi fra databasen.
du kan derefter hente den som returværdien af Gem-metoden.
Author a = new Author();a.setFirstName("Thorben");a.setLastName("Janssen");Long id = (Long) em.unwrap(Session.class).save(a);
eller du kan ringe til getter-metoden for den primære nøgleattribut for din administrerede enhed, hvis du bruger JPA ‘ s vedvarende metode.
Author a = new Author();a.setFirstName("Torben");a.setLastName("Janssen");em.persist(a);Long id = a.getId();
Hibernate udfører de samme sætninger, når du kalder vedvare-eller gem-metoden. Hvilket og hvornår det gør det afhænger af din primære nøglegenereringsstrategi:
ikke genereret
hvis du indstiller den primære nøgleværdi programmatisk, f.eks. til en naturlig identifikator, udfører Hibernate kun en sætning, når den skyller persistenskonteksten.
14:08:34,979 INFO TestPersistSaveMerge:237 - Save entity14:08:35,052 INFO TestPersistSaveMerge:240 - Commit transaction14:08:35,123 DEBUG SQL:92 - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)
genereret med IDENTITETSSTRATEGI
hvis du bruger IDENTITETSSTRATEGIEN til at generere den primære nøgleværdi, skal Hibernate udføre sætningen indsæt, når du kalder metoden Gem eller fortsæt for at hente den primære nøgleværdi fra databasen.
14:09:28,264 INFO TestPersistSaveMerge:237 - Save entity14:09:28,336 DEBUG SQL:92 - insert into Author (firstName, lastName, version) values (?, ?, ?)14:09:28,354 INFO TestPersistSaveMerge:240 - Commit transaction
genereret med SEKVENSSTRATEGI
, og hvis du bruger sekvensen, udfører Hibernate en SELECT-sætning for at hente den næste værdi fra databasesekvensen. Hibernate forsinker derefter INSERT-sætningen, indtil den skyller persistenskonteksten. I dette eksempel sker flush, når transaktionen bliver begået.
14:10:27,994 INFO TestPersistSaveMerge:237 - Save entity14:10:28,002 DEBUG SQL:92 - select nextval ('hibernate_sequence')14:10:28,042 INFO TestPersistSaveMerge:240 - Commit transaction14:10:28,096 DEBUG SQL:92 - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)
genereret med TABELSTRATEGI
du bør ikke bruge TABELSTRATEGIEN, fordi den kræver rækkeniveaulåse på den primære nøgletabel og ikke skalerer godt. Hvis du alligevel bruger denne strategi, udfører Hibernate en SELECT-sætning for at hente den næste primære nøgleværdi fra databasen og skriver den nye værdi til databasetabellen. Det forsinker udførelsen af sætningen for den nye enhed, indtil den skyller vedholdenhedskonteksten.
14:11:17,368 INFO TestPersistSaveMerge:237 - Save entity14:11:17,482 DEBUG SQL:92 - select tbl.next_val from hibernate_sequences tbl where tbl.sequence_name=? for update of tbl14:11:17,531 DEBUG SQL:92 - insert into hibernate_sequences (sequence_name, next_val) values (?,?)14:11:17,534 DEBUG SQL:92 - update hibernate_sequences set next_val=? where next_val=? and sequence_name=?14:11:17,584 INFO TestPersistSaveMerge:240 - Commit transaction14:11:17,655 DEBUG SQL:92 - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)
hvilken skal man vælge?
du kan forvente, at gemme og fortsætte metode opfører sig anderledes, fordi der er et par forskelle mellem JPA specifikation og Javadoc af Hibernate proprietære metoder.
men næsten alle disse forskelle forsvinder, når du kigger på den interne implementering. De eneste, der er tilbage, er 2 hjørnesager, hvor dvaletilstand kan forsinke hentningen af den primære nøgle, metodens returtype og understøttelsen af andre JPA-implementeringer.
for de fleste applikationer gør det ingen forskel, hvis du får den genererede primærnøgleværdi som returtype for hibernates gemningsmetode eller fra getter-metoden i din primære nøgleattribut. Så længe du ikke bruger en udvidet persistenskontekst og udfører alle databasehandlinger med en aktiv transaktion, anbefaler jeg at bruge JPA ‘ s persistent-metode.
opdatering af en løsrevet enhed
når du lukker den aktuelle persistenskontekst eller eksplicit fjerner en enhed fra den ved at kalde metoderne Ryd eller løsn på entitymanager-grænsefladen, løsnes enheden. Det betyder, at den ikke længere er gemt i cachen på 1.niveau, og at Hibernate ikke replikerer nogen af de anvendte ændringer i databasen.
du kan bruge hibernates opdatering eller JPA ‘ s fletningsmetode til at knytte en løsrevet enhed til en persistenskontekst. Når du har gjort det, opdaterer Hibernate databasen baseret på objektattributværdierne.
effekten af opdaterings-og fletningsmetoden ser ud til at være den samme, men som du vil se i de følgende afsnit, er der en vigtig forskel.
JPA ‘s fletningsmetode
JPA’ s fletningsmetode kopierer tilstanden for en løsrevet enhed til en administreret forekomst af den samme enhed. Hibernate udfører derfor en SELECT-sætning for at hente en administreret enhed fra databasen. Hvis persistenskonteksten allerede indeholdt en administreret forekomst af enheden, bruger Hibernate den eksisterende i stedet. Derefter kopierer den alle attributværdier til den administrerede enhed og returnerer den til den, der ringer op.
Author managedAuthor = em.merge(a);
når du har aktiveret logningen af KVL-udsagn, kan du se de udførte SELECT and UPDATE-udsagn i logoutputtet.
11:37:21,172 DEBUG SQL:92 - select books0_.bookId as bookId1_2_0_, books0_.authorId as authorId2_2_0_, book1_.id as id1_1_1_, book1_.fk_author as fk_autho6_1_1_, book1_.format as format2_1_1_, book1_.publishingDate as publishi3_1_1_, book1_.title as title4_1_1_, book1_.version as version5_1_1_, author2_.id as id1_0_2_, author2_.firstName as firstNam2_0_2_, author2_.lastName as lastName3_0_2_, author2_.version as version4_0_2_ from BookAuthor books0_ inner join Book book1_ on books0_.authorId=book1_.id left outer join Author author2_ on book1_.fk_author=author2_.id where books0_.bookId=?11:37:21,180 INFO TestPersistSaveMerge:82 - Before commit11:37:21,182 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?
når Hibernate skyller persistenskonteksten til næste gang, kontrollerer dens beskidte kontrolmekanisme alle administrerede enheder. Hvis det registrerer, at fletningsoperationen ændrede en enhedsattributværdi, udløser den den krævede OPDATERINGSERKLÆRING.
der er en vigtig detalje, du skal vide, når du bruger JPA ‘ s fletningsmetode. Hibernate kopierer attributværdierne for den løsrevne enhed til den administrerede enhed. Dette overskriver alle ændringer, du har udført på denne enhed i den aktuelle Session.
Hibernate ‘s opdateringsmetode
Hibernate’ s opdateringsmetode udløser ikke et sæt valg. Det knytter bare enheden til den nuværende vedholdenhedskontekst. I modsætning til JPA ‘ s fletningsmetode kan du ikke miste nogen ændringer ved at kalde opdateringsmetoden. Hvis persistenskonteksten allerede indeholder en administreret forekomst af den enhed, du vil opdatere, kaster den en undtagelse.
em.unwrap(Session.class).update(a);
når Hibernate udfører den næste flush, udfører den ikke nogen beskidte kontrol. Det er ikke muligt, fordi Hibernate ikke læste den nyeste version af enheden fra databasen. Det udfører bare en opdatering erklæring for den reattached enhed.
11:38:28,151 INFO TestPersistSaveMerge:121 - Before commit11:38:28,153 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?
den manglende beskidte kontrol forårsager en unødvendig OPDATERINGSERKLÆRING, når enheden og den tilsvarende databasepost indeholder de samme værdier. Dette kan være et problem, hvis din DBA registrerede en opdateringsudløser til databasetabellen. I disse situationer kan du kommentere din enhed med @Selectforeupdate.
@Entity@SelectBeforeUpdatepublic class Author { ... }
der beder Hibernate om at vælge enheden og udføre en beskidt kontrol, før den genererer OPDATERINGSOPGØRELSEN. Som du kan se i logudgangen, svarer opdateringsmetodens opførsel nu til JPA ‘ s fletningsmetode.
19:08:16,530 INFO TestPersistSaveMerge:121 - Before commit19:08:16,531 DEBUG SQL:92 - select author_.id, author_.firstName as firstNam2_0_, author_.lastName as lastName3_0_, author_.version as version4_0_ from Author author_ where author_.id=?19:08:16,592 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?
men der er en signifikant forskel mellem de 2 metoder. Når du kalder opdateringsmetoden, vælger Hibernate kun den enhed, du har angivet som en metodeparameter. Men når du ringer til JPA ‘ s fletningsmetode, vælger Hibernate også alle foreninger med CascadeType.FUSIONERE. Du bør derfor foretrække JPA ‘ s fletningsmetode, hvis du genmonterer en enorm graf over enheder.
hvilken skal man vælge?
der er ikke noget generelt svar på disse spørgsmål. Som du har set, har begge metoder deres fordele og ulemper. Du skal beslutte for din specifikke brugssag, om Hibernate skal vælge enheden, før den udløser OPDATERINGSERKLÆRINGEN. Og hvis det er tilfældet, skal du også overveje dybden af din enhedsgraf og præstationsimplikationerne af den leverede hentningsadfærd.
opdatering af en administreret enhed
JPA og Hibernate gør det meget nemt at opdatere en administreret enhed. Hvis din enhed er i livscyklustilstand administreret, f.eks. fordi du hentede den med en ENTITYMANAGER-forespørgsel eller find-metoden, skal du bare ændre værdierne for dine enhedsattributter.
em = emf.createEntityManager();em.getTransaction().begin();a = em.find(Author.class, a.getId());a.setFirstName("Thorben");log.info("Before commit");em.getTransaction().commit();em.close();
når Hibernate beslutter at skylle vedholdenhedskonteksten, registrerer den beskidte kontrolmekanisme ændringen og udfører den krævede OPDATERINGSERKLÆRING.
11:41:49,178 DEBUG SQL:92 - select author0_.id as id1_0_0_, author0_.firstName as firstNam2_0_0_, author0_.lastName as lastName3_0_0_, author0_.version as version4_0_0_ from Author author0_ where author0_.id=?11:41:49,191 INFO TestPersistSaveMerge:335 - Before commit11:41:49,193 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?
det behøver du ikke, og du bør ikke ringe til Hibernate ‘ s Gem-metode, når du har opdateret en enhed. Det udløser en ekstra SaveOrUpdate-begivenhed uden at give nogen fordele. Når Hibernate beslutter at skylle vedholdenhedskonteksten, udfører den alligevel den beskidte kontrol for at registrere alle ændringer, før den udfører de krævede OPDATERINGSUDTALELSER.