Hibernate: spara, kvarstå, uppdatera, sammanfoga, saveOrUpdate

introduktion

i den här artikeln kommer vi att diskutera skillnaderna mellan flera metoder i Sessionsgränssnittet: spara, kvarstå, uppdatera, sammanfoga, saveOrUpdate.

Detta är inte en introduktion till Hibernate och du bör redan känna till grunderna för konfiguration, objektrelationell kartläggning och arbeta med entitetsinstanser. För en inledande artikel till viloläge, besök vår handledning om viloläge 4 med våren.

vidare läsning:

radera objekt med Hibernate

Snabbguide för att radera en enhet i Hibernate.
Läs mer →

lagrade procedurer med Hibernate

den här artikeln diskuterar kort hur man ringer till butiksprocedurer från Hibernate.
Läs mer →

en översikt över identifierare i Hibernate/JPA

lär dig hur du mappar entitetsidentifierare med Hibernate.
Läs mer →

Session som en uthållighet sammanhang genomförande

Sessionsgränssnittet har flera metoder som så småningom resulterar i att spara data till databasen: kvarstå, spara, uppdatera, sammanfoga, saveOrUpdate. För att förstå skillnaden mellan dessa metoder måste vi först diskutera syftet med sessionen som ett uthållighetskontext och skillnaden mellan tillstånden för entitetsinstanser i förhållande till sessionen.

vi bör också förstå historien om viloläge utveckling som ledde till några delvis duplicerade API-metoder.

2.1. Hantera Entitetsinstanser

förutom objektrelationell kartläggning i sig är ett av problemen som Hibernate var avsett att lösa problemet med att hantera enheter under körning. Begreppet ”uthållighetskontext” är hibernates lösning på detta problem. Persistence context kan ses som en behållare eller en cache på första nivån för alla objekt som du laddade eller sparade i en databas under en session.

sessionen är en logisk transaktion, vilka gränser definieras av din applikations affärslogik. När du arbetar med databasen genom ett uthållighetskontext och alla dina entitetsinstanser är kopplade till det här sammanhanget bör du alltid ha en enda instans av entitet för varje databaspost som du har interagerat med under sessionen.

i viloläge representeras uthållighetskontexten av org.viloläge.Session instans. För JPA är det javax.uthållighet.EntityManager. När vi använder Hibernate som en JPA-leverantör och arbetar via EntityManager-gränssnittet, implementerar implementeringen av detta gränssnitt i princip det underliggande Sessionsobjektet. Hibernate Session ger dock ett rikare gränssnitt med fler möjligheter så ibland är det användbart att arbeta med Session direkt.

2.2. Tillstånd för Entitetsinstanser

alla entitetsinstanser i din ansökan visas i ett av de tre huvudtillstånden i förhållande till sessionens uthållighetskontext:

  • transient-den här instansen är inte, och har aldrig varit, kopplad till en Session; den här instansen har inga motsvarande rader i databasen; Det är vanligtvis bara ett nytt objekt som du har skapat för att spara i databasen;
  • persistent — denna instans är associerad med ett unikt Sessionsobjekt; vid spolning av sessionen till databasen garanteras denna enhet att ha en motsvarande konsekvent post i databasen;
  • fristående — denna instans var en gång kopplad till en Session (i ett beständigt tillstånd), men nu är det inte; en instans går in i detta tillstånd om du vräker det från sammanhanget, rensar eller stänger sessionen eller sätter instansen genom serialisering/deserialiseringsprocess.

här är ett förenklat tillståndsdiagram med kommentarer om Sessionsmetoder som får tillståndsövergångarna att hända.

2016-07-11_13-38-11

när entitetsinstansen är i beständigt tillstånd tillämpas alla ändringar som du gör i de mappade fälten i den här instansen på motsvarande databasposter och fält när sessionen spolas. Den ihållande instansen kan betraktas som” online”, medan den fristående instansen har gått” offline ” och övervakas inte för ändringar.

det betyder att när du ändrar fält för ett beständigt objekt behöver du inte ringa spara, uppdatera eller någon av dessa metoder för att få dessa ändringar i databasen: allt du behöver är att begå transaktionen eller spola eller stänga sessionen när du är klar med den.

2.3. Överensstämmelse med JPA-specifikationen

Hibernate var den mest framgångsrika Java ORM-implementeringen. Inte konstigt att specifikationen för Java persistence API (JPA) påverkades starkt av Hibernate API. Tyvärr fanns det också många skillnader: några stora, lite mer subtila.

för att fungera som en implementering av JPA-standarden måste Hibernate API: er revideras. Flera metoder lades till i Sessionsgränssnittet för att matcha EntityManager-gränssnittet. Dessa metoder tjänar samma syfte som de” ursprungliga ” metoderna, men överensstämmer med specifikationen och har därmed vissa skillnader.

skillnader mellan operationerna

det är viktigt att förstå från början att alla metoder (persistent, save, update, merge, saveOrUpdate) inte omedelbart resulterar i motsvarande SQL UPDATE eller INSERT-satser. Det faktiska sparandet av data i databasen sker när transaktionen utförs eller när sessionen spolas.

de nämnda metoderna hanterar i princip tillståndet för entitetsinstanser genom att överföra dem mellan olika tillstånd längs livscykeln.

som en exempelenhet använder vi en enkel annoteringsmappad Entitetsperson:

@Entitypublic class Person { @Id @GeneratedValue private Long id; private String name; // ... getters and setters}

3.1. Persist

metoden persist är avsedd att lägga till en ny entitetsinstans i persistenskontexten, dvs. överföra en instans från transient till persistent state.

vi brukar kalla det när vi vill lägga till en post i databasen (kvarstår en entitetsinstans):

Person person = new Person();person.setName("John");session.persist(person);

vad händer efter att metoden persistent kallas? Personobjektet har övergått från övergående till ihållande tillstånd. Objektet är i uthållighetskontexten nu, men ännu inte sparat i databasen. Genereringen av INSERT-uttalanden kommer endast att ske vid transaktionen, spolning eller stängning av sessionen.

Lägg märke till att metoden kvarstår har ogiltig returtyp. Den fungerar på det passerade objektet ”på plats” och ändrar sitt tillstånd. Personvariabeln refererar till det faktiska kvarhållna objektet.

denna metod är ett senare tillägg till Sessionsgränssnittet. Den huvudsakliga differentierande egenskapen hos denna metod är att den överensstämmer med JSR-220-specifikationen (EJB persistens). Semantiken för denna metod är strikt definierad i specifikationen, som i grunden säger att:

  • en övergående instans blir beständig (och operationen kaskader till alla dess relationer med cascade=persistent eller cascade=ALL),
  • om en instans redan är beständig, har detta samtal ingen effekt för den här instansen (men det kaskader fortfarande till dess relationer med cascade=persistent eller cascade=ALL),
  • om en instans är fristående bör du förvänta dig ett undantag, antingen när du anropar den här metoden eller när du begår eller spolar sessionen.

Lägg märke till att det inte finns något här som rör identifieraren för en instans. Specifikationen anger inte att id kommer att genereras direkt, oavsett id-genereringsstrategin. Specifikationen för persist-metoden gör det möjligt för implementeringen att utfärda uttalanden för att generera id på commit eller flush, och id är inte garanterat att vara icke-null efter att ha anropat den här metoden, så du borde inte lita på den.

du kan ringa den här metoden på en redan ihållande instans, och ingenting händer. Men om du försöker att fortsätta en fristående instans, genomförandet är skyldig att kasta ett undantag. I följande exempel fortsätter vi enheten, vräker den från sammanhanget så att den lossnar och försöker sedan fortsätta igen. Det andra samtalet till sessionen.kvarstår () orsakar ett undantag, så följande kod fungerar inte:

Person person = new Person();person.setName("John");session.persist(person);session.evict(person);session.persist(person); // PersistenceException!

3.2. Spara

spara-metoden är en ”original” viloläge-metod som inte överensstämmer med JPA-specifikationen.

dess syfte är i princip detsamma som kvarstår, men det har olika implementeringsdetaljer. Dokumentationen för denna metod säger strikt att den kvarstår instansen, ”först tilldela en genererad identifierare”. Metoden är garanterad att returnera det Serialiserbara värdet för denna identifierare.

Person person = new Person();person.setName("John");Long id = (Long) session.save(person);

effekten av att spara en redan bestående instans är densamma som med persist. Skillnaden kommer när du försöker spara en fristående instans:

Person person = new Person();person.setName("John");Long id1 = (Long) session.save(person);session.evict(person);Long id2 = (Long) session.save(person);

id2-variabeln skiljer sig från id1. Samtalet spara på en fristående instans skapar en ny ihållande instans och tilldelar den en ny identifierare, vilket resulterar i en dubblett post i en databas vid begå eller spolning.

3.3. Sammanfoga

huvudsyftet med sammanfogningsmetoden är att uppdatera en beständig entitetsinstans med nya fältvärden från en fristående entitetsinstans.

anta till exempel att du har ett vilsamt gränssnitt med en metod för att hämta ett JSON-serialiserat objekt med sitt id till den som ringer och en metod som tar emot en uppdaterad version av det här objektet från den som ringer. En enhet som passerade genom sådan serialisering/deserialisering kommer att visas i ett fristående tillstånd.

när du har avserialiserat den här entitetsinstansen måste du hämta en beständig entitetsinstans från ett uthållighetskontext och uppdatera dess fält med nya värden från den här fristående instansen. Så sammanslagningsmetoden gör exakt det:

  • hittar en entitetsinstans med id som tagits från det passerade objektet (antingen hämtas en befintlig entitetsinstans från uthållighetskontexten eller en ny instans som laddas från databasen);
  • kopierar fält från det skickade objektet till den här instansen;
  • returnerar nyligen uppdaterad instans.

i följande exempel vräker vi (tar bort) den sparade enheten från sammanhanget, ändrar namnfältet och sammanfogar sedan den fristående enheten.

Person person = new Person(); person.setName("John"); session.save(person);session.evict(person);person.setName("Mary");Person mergedPerson = (Person) session.merge(person);

Observera att sammanfogningsmetoden returnerar ett objekt — det är mergedperson-objektet som laddades in i uthållighetskontext och uppdaterades, inte det personobjekt som du passerade som ett argument. Det är två olika objekt, och personobjektet behöver vanligtvis kasseras (hur som helst, räkna inte med att det är knutet till uthållighetskontext).

som med persist-metoden specificeras sammanslagningsmetoden av JSR-220 för att ha viss semantik som du kan lita på:

  • om entiteten är fristående kopieras den på en befintlig beständig entitet;
  • om entiteten är övergående kopieras den på en nyskapad beständig entitet;
  • denna operation kaskader för alla relationer med cascade=MERGE eller cascade=ALL mapping;
  • om entiteten är beständig, har denna metodanrop inte effekt på den (men kaskadningen sker fortfarande).

3.4. Update

som med persist and save är uppdateringsmetoden en ”original” Hibernate-metod som fanns långt innan sammanslagningsmetoden lades till. Dess semantik skiljer sig åt i flera viktiga punkter:

  • det verkar på passerat objekt (dess returtyp är ogiltig); uppdateringsmetoden övergår det passerade objektet från fristående till ihållande tillstånd;
  • denna metod kastar ett undantag om du skickar det till en övergående enhet.

i följande exempel sparar vi objektet, sedan vräker (tar bort) det från sammanhanget, ändrar sedan dess namn och anropar uppdatering. Observera att vi inte lägger resultatet av uppdateringsoperationen i en separat variabel, eftersom uppdateringen sker på personobjektet själv. I grund och botten sätter vi tillbaka den befintliga entitetsinstansen till uthållighetskontexten — något som JPA-specifikationen inte tillåter oss att göra.

Person person = new Person();person.setName("John");session.save(person);session.evict(person);person.setName("Mary");session.update(person);

att försöka ringa uppdatering på en övergående instans resulterar i ett undantag. Följande fungerar inte:

Person person = new Person();person.setName("John");session.update(person); // PersistenceException!

3.5. SaveOrUpdate

den här metoden visas bara i Hibernate API och har inte sin standardiserade motsvarighet. I likhet med uppdatering kan den också användas för att återansluta instanser.

faktiskt är den interna defaultupdateeventlistener-klassen som behandlar uppdateringsmetoden en underklass av DefaultSaveOrUpdateListener, som bara åsidosätter viss funktionalitet. Huvudskillnaden för saveorupdate-metoden är att den inte kastar undantag när den tillämpas på en övergående instans; istället gör den denna övergående instans beständig. Följande kod kommer att kvarstå en nyskapad instans av Person:

Person person = new Person();person.setName("John");session.saveOrUpdate(person);

du kanske tänker på den här metoden som ett universellt verktyg för att göra ett objekt beständigt oavsett dess tillstånd om det är övergående eller fristående.

Vad ska man använda?

om du inte har några speciella krav, bör du som tumregel hålla fast vid persist and merge-metoderna, eftersom de är standardiserade och garanterade att överensstämma med JPA-specifikationen.

de är också bärbara om du bestämmer dig för att byta till en annan persistensleverantör, men de kan ibland inte vara så användbara som de ”ursprungliga” Hibernate-metoderna, spara, uppdatera och saveOrUpdate.

slutsats

vi har diskuterat syftet med olika Hibernate-Sessionsmetoder i förhållande till hantering av ihållande enheter under körning. Vi har lärt oss hur dessa metoder transist entitetsinstanser genom sina livscykler och varför några av dessa metoder har duplicerat funktionalitet.



+