vad är skillnaden mellan att fortsätta, spara, slå samman och uppdatera? Vilken ska du använda?

JPA och Hibernate tillhandahåller olika metoder för att fortsätta nya och uppdatera befintliga enheter. Du kan välja mellan JPA: s persist och merge och hibernates spara och uppdatera metoder.

det verkar som om det finns 2 par 2 metoder som gör detsamma. Du kan använda metoderna kvarstår och spara för att lagra en ny entitet och metoderna sammanfoga och uppdatera för att lagra ändringarna av en fristående entitet i databasen. Därför undrar många utvecklare vilka av dessa metoder de ska använda. Låt oss ta en närmare titt på detaljerna och små skillnader i dessa metoder.

särskilt tack till Steve Ebersole (Lead Developer – Hibernate ORM) som gav sin feedback och stora insikter om några av hibernates dolda implementeringsdetaljer!

Entity State Transitions

innan vi går in på detaljerna i dessa 4-metoder måste jag ge dig en snabb introduktion till JPA: s entitetslivscykeltillstånd.

om en enhet är kopplad till det aktuella uthållighetskontexten hanteras livscykeltillståndet. Det betyder att den är mappad till en databaspost. Din persistens leverantör genererar de nödvändiga SQL infoga och uppdatera uttalanden att sprida alla ändringar. En hanterad enhet lagras också i cachen på 1: A nivån.

när du skapar en ny enhet är den i övergående tillstånd. Det förblir i detta tillstånd tills du bifogar det till det aktuella uthållighetskontexten. Jag kommer att visa dig hur du kan göra det med JPA: s persist och hibernates spara metod, i följande avsnitt. Så länge en enhet är i övergående tillstånd mappas den inte till en databaspost och hanteras inte av något uthållighetskontext.

enheter i det fristående livscykeltillståndet hanteras inte längre av uthållighetskontexten. Det kan vara fallet eftersom du stängde uthållighetskontexten eller du uttryckligen avskilde enheten från det aktuella sammanhanget. Jag kommer att få mer information om hur du kan fästa dessa enheter igen med JPA: s sammanslagning och hibernates uppdateringsmetoder i en senare del av det här inlägget.

och det senaste livscykeltillståndet tas bort. Dessa enheter var tidigare i staten hanteras, innan du planerat dem för borttagning. Att ta bort enheter ligger utanför ramen för detta inlägg, så jag kommer inte att komma in för många detaljer om det. Du kan schemalägga en enhet för borttagning genom att anropa metoden Ta bort i EntityManager-gränssnittet.

kvarstå en ny entitet med hjälp av kvarstå eller spara

när du skapar ett nytt entitetsobjekt är det i transient lifecycle state. Det kartlägger inte någon databaspost.

Author a = new Author();a.setFirstName("Thorben");a.setLastName("Janssen");

du måste bifoga entiteten till ett uthållighetskontext så att den hanteras och kvarstår i databasen. Du kan antingen använda JPA: s persist eller hibernates spara metod för att göra det. Båda metoderna verkar göra detsamma, men det finns några skillnader.

Specifikation vs. Proprietär API

den mest uppenbara skillnaden är att JPA-specifikationen definierar metoden persistent. Du kan använda den med alla JPA-implementeringar. Spara-metoden är å andra sidan Hibernate-specifik. Det är därför inte tillgängligt i andra JPA-implementeringar.

men det är bara relevant om du vill kunna ersätta Hibernate med en annan JPA-implementering, som Eclipse Link eller OpenJPA.

Returtyper och utförande av SQL-satser

en annan uppenbar skillnad mellan dessa 2-metoder är deras returtyp. JPA: s persist-metod returnerar void och hibernates save-metod returnerar primärnyckeln för entiteten.

det kan verka som en enorm skillnad, särskilt när du tittar närmare på hibernates Javadoc och JPA-specifikationen:

  • Javadoc i hibernates spara metod anger att den genererar primärnyckelvärdet först:

    fortsätt den givna övergående instansen, först tilldela en genererad identifierare.
    Javadoc Session.spara (enhet)

  • du hittar ingen information om detta i JPA-specifikationen. Det definierar inte när primärnyckelvärdet måste tilldelas. Så, persistensleverantören kan göra det när som helst mellan samtalet från persistensmetoden och spolningen av persistenssammanhanget.

i de flesta fall gör det ingen skillnad om du kallar spara eller fortsätta metoden. Hibernate använder namnet på entitetsklassen och primärnyckelvärdet för att lagra entiteten i cachen på första nivån. Det behöver därför ett primärnyckelvärde när det kör metoden persistent.

i nästan alla situationer genererar Hibernate primärnyckelvärdet omedelbart och utlöser ett SQL-uttryck om det behövs när du anropar metoden Fortsätt eller spara.

men det är inte fallet om du använder IDENTITETSSTRATEGIN och försöker fortsätta en enhet utan en aktiv transaktion eller Med FlushMode.MANUELL. Om du anropar metoden fortsätt i en av dessa situationer fördröjer viloläge exekveringen av SQL INSERT-satsen och skapar ett tillfälligt primärnyckelvärde. Men om du ringer till spara-metoden utför Hibernate SQL INSERT-satsen omedelbart och hämtar primärnyckelvärdet från databasen.

du kan sedan hämta det som returvärde för spara metoden.

Author a = new Author();a.setFirstName("Thorben");a.setLastName("Janssen");Long id = (Long) em.unwrap(Session.class).save(a);

eller så kan du anropa getter-metoden för primärnyckelattributet för din hanterade enhet Om du använder JPA: s persist-metod.

Author a = new Author();a.setFirstName("Torben");a.setLastName("Janssen");em.persist(a);Long id = a.getId();

Hibernate kör samma SQL-satser när du anropar metoden persist eller save. Vilket Och när det gör det beror på din primära nyckelgenereringsstrategi:

inte genererad

om du ställer in primärnyckelvärdet programmatiskt, t.ex. till en naturlig identifierare, utför Hibernate bara en SQL INSERT-sats när den spolar uthållighetskontexten.

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 (?, ?, ?, ?)
YouTube video

genereras med IDENTITETSSTRATEGI

om du använder IDENTITETSSTRATEGIN för att generera primärnyckelvärdet måste Hibernate köra INSERT-satsen när du anropar metoden spara eller kvarstå för att hämta primärnyckelvärdet från 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
YouTube video

genereras med SEKVENSSTRATEGI

och om du använder sekvensen utför Hibernate en SQL SELECT-sats för att hämta nästa värde från databassekvensen. Hibernate fördröjer sedan INSERT-uttalandet tills det spolar uthållighetskontexten. I det här exemplet sker färgningen när transaktionen begås.

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 (?, ?, ?, ?)

genererad med TABELLSTRATEGI

du bör inte använda TABELLSTRATEGIN eftersom den kräver radnivålås på primärnyckeltabellen och inte skala bra. Om du ändå använder den här strategin utför Hibernate en SQL SELECT-sats för att hämta nästa primärnyckelvärde från databasen och skriver det nya värdet till databastabellen. Det fördröjer exekveringen av SQL INSERT-satsen för den nya enheten tills den spolar uthållighetskontexten.

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 (?, ?, ?, ?)

vilken ska man välja?

du kan förvänta dig att save and persist-metoden beter sig annorlunda eftersom det finns några skillnader mellan JPA-specifikationen och Javadoc för hibernates proprietära metoder.

men nästan alla dessa skillnader försvinner när du tittar på den interna implementeringen. De enda som finns kvar är 2 hörnfall där Hibernate kan fördröja hämtningen av primärnyckeln, metodens returtyp och stödet från andra JPA-implementeringar.

för de flesta applikationer gör det ingen skillnad om du får det genererade primärnyckelvärdet som returtyp för hibernates sparmetod eller från gettermetoden för ditt primära nyckelattribut. Så länge du inte använder ett utökat uthållighetskontext och utför alla databasoperationer med en aktiv transaktion, rekommenderar jag att du använder JPA: s persist-metod.

uppdatera en fristående entitet

när du stänger det aktuella uthållighetskontexten eller uttryckligen tar bort en entitet från den genom att anropa clear eller ta bort metoderna i EntityManager-gränssnittet, blir entiteten fristående. Det betyder att det inte längre lagras i 1: A nivå cache och att Hibernate inte kommer att replikera någon av de tillämpade ändringarna i databasen.

du kan använda hibernates uppdatering eller JPA: s sammanslagningsmetod för att associera en fristående enhet med ett uthållighetskontext. När du har gjort det uppdaterar Hibernate databasen baserat på entitetsattributvärdena.

effekten av uppdaterings-och sammanslagningsmetoden verkar vara densamma, men som du kommer att se i följande avsnitt är det en viktig skillnad.

JPA: s sammanslagningsmetod

JPA: s sammanslagningsmetod kopierar tillståndet för en fristående enhet till en hanterad instans av samma enhet. Hibernate kör därför en SQL SELECT-sats för att hämta en hanterad enhet från databasen. Om uthållighetskontexten redan innehöll en hanterad instans av entiteten använder Hibernate den befintliga istället. Den kopierar sedan alla attributvärden till den hanterade enheten och returnerar den till den som ringer.

Author managedAuthor = em.merge(a);

när du har aktiverat loggningen av SQL-satser kan du se de exekverade SELECT – och UPDATE-satserna i loggutmatningen.

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 spolar uthållighetskontexten för nästa gång kontrollerar dess smutsiga kontrollmekanism alla hanterade enheter. Om den upptäcker att sammanfogningsoperationen ändrade något entitetsattributvärde, utlöser den nödvändiga SQL-uppdateringsuttalandet.

det finns en viktig detalj du behöver veta när du använder JPA: s sammanslagningsmetod. Hibernate kopierar attributvärdena för den fristående entiteten till den hanterade entiteten. Detta skriver över alla ändringar som du utförde på den här enheten under den aktuella sessionen.

hibernates uppdateringsmetod

hibernates uppdateringsmetod utlöser inte ett SQL SELECT-uttalande. Det fäster bara enheten till det nuvarande uthållighetskontexten. Till skillnad från JPA: s sammanslagningsmetod kan du inte förlora några ändringar genom att anropa uppdateringsmetoden. Om uthållighetskontexten redan innehåller en hanterad instans av den enhet du vill uppdatera, kastar den ett undantag.

em.unwrap(Session.class).update(a);

när Hibernate utför nästa spolning utförs inga smutsiga kontroller. Det är inte möjligt eftersom Hibernate inte läste den senaste versionen av entiteten från databasen. Det kör bara ett SQL-UPPDATERINGSUTTALANDE för den återanslutna enheten.

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 saknade smutsiga kontrollen orsakar ett onödigt SQL-UPPDATERINGSUTTALANDE när enheten och motsvarande databaspost innehåller samma värden. Det här kan vara ett problem om din DBA registrerade en uppdateringsutlösare för databastabellen. I dessa situationer kan du kommentera din enhet med @SelectBeforeUpdate.

@Entity@SelectBeforeUpdatepublic class Author { ... }

som berättar Hibernate att välja entiteten och utföra en smutsig kontroll innan den genererar SQL UPDATE-uttalandet. Som du kan se i loggutmatningen liknar uppdateringsmetodens beteende nu JPA: s sammanslagningsmetod.

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 det finns en signifikant skillnad mellan 2-metoderna. När du anropar uppdateringsmetoden väljer Hibernate bara den enhet som du angav som metodparameter. Men när du ringer JPA: s sammanslagningsmetod väljer Hibernate också alla föreningar med CascadeType.SAMMAN. Du bör därför föredra JPA: s sammanslagningsmetod om du sätter tillbaka en enorm Graf över enheter.

vilken ska man välja?

det finns inget allmänt svar på dessa frågor. Som du har sett har båda metoderna sina fördelar och nackdelar. Du måste bestämma för ditt specifika användningsfall om Hibernate måste välja entiteten innan den utlöser SQL – uppdateringsuttalandet. Och om så är fallet måste du också överväga djupet i din entitetsgraf och prestandaimplikationerna för det angivna hämtningsbeteendet.

uppdatering av en hanterad enhet

JPA och Hibernate gör det mycket enkelt att uppdatera en hanterad enhet. Om din entitet är i livscykeltillståndet hanterat, t.ex. för att du hämtade det med en jpql-fråga eller sökmetoden för EntityManager, behöver du bara ändra värdena för dina entitetsattribut.

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 bestämmer sig för att spola uthållighetskontexten, kommer den smutsiga kontrollmekanismen att upptäcka ändringen och utföra det nödvändiga SQL-uppdateringsuttalandet.

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=?

du behöver inte, och du bör inte ringa hibernates spara metod efter att du uppdaterat en enhet. Det utlöser ytterligare en SaveOrUpdate-händelse utan att ge några fördelar. När Hibernate bestämmer sig för att spola uthållighetskontexten, kommer den att utföra den smutsiga kontrollen ändå för att upptäcka alla ändringar innan den kör de nödvändiga SQL-uppdateringsuttalandena.

You might also like

Lämna ett svar

Din e-postadress kommer inte publiceras.