Dvaletilstand: gem, vedhold, opdater, flet, gemOrUpdate

1. Introduktion

I denne artikel vil vi diskutere forskellene mellem flere metoder til Session grænseflade: Gemme, vedvarer, opdatering, fusionere, gem eller opdater.

Dette er ikke en introduktion til dvale, og du skal allerede kende det grundlæggende i konfiguration, objektrelationskortlægning og arbejde med enhedsforekomster. For en introduktionsartikel til dvale, besøg vores vejledning om dvale 4 med foråret.

2. Session som en Persistence Context Implementation

Det Session interface har flere metoder, der til sidst resulterer i lagring af data i databasen: vedvarer, Gemme, opdatering, fusionere, gem eller opdater. For at forstå forskellen mellem disse metoder skal vi først diskutere formålet med Session som en vedholdenhedskontekst og forskellen mellem tilstandene for enhedens forekomster i forhold til Session.

Vi bør også forstå historien om dvaleudvikling, der førte til nogle delvist duplikerede API-metoder.

2.1. Håndtering af enhedsforekomster

Bortset fra selve objektrelationskortlægningen er et af de problemer, som dvale var beregnet på at løse, problemet med at styre enheder under løbetiden. Begrebet "vedholdenhedskontekst" er dvaleens løsning på dette problem. Persistens-kontekst kan betragtes som en container eller en cache på første niveau for alle de objekter, du har indlæst eller gemt i en database under en session.

Sessionen er en logisk transaktion, hvilke grænser defineres af din applikations forretningslogik. Når du arbejder med databasen gennem en persistens-kontekst, og alle dine enhedsforekomster er knyttet til denne kontekst, skal du altid have en enkelt forekomst af enhed for hver database-post, som du har interageret under sessionen med.

I dvale er vedholdenhedskonteksten repræsenteret af org. dvale. session eksempel. For JPA er det javax.persistence.EntityManager. Når vi bruger dvale som en JPA-udbyder og fungerer via EntityManager interface, implementeringen af ​​denne grænseflade omslutter dybest set det underliggende Session objekt. Men dvale Session giver en rigere grænseflade med flere muligheder, så nogle gange er det nyttigt at arbejde med Session direkte.

2.2. Enheder i enheder

Enhver enhedsforekomst i din applikation vises i en af ​​de tre hovedtilstande i forhold til Session vedholdenhedskontekst:

  • forbigående - denne instans er ikke og var aldrig knyttet til en Session; denne forekomst har ingen tilsvarende rækker i databasen; det er normalt bare et nyt objekt, som du har oprettet for at gemme i databasen;
  • vedholdende - denne forekomst er forbundet med en unik Session objekt; efter skylning af Session til databasen er denne enhed garanteret at have en tilsvarende konsistent post i databasen;
  • løsrevet - denne forekomst var engang knyttet til en Session (i en vedholdende tilstand), men nu er det ikke; en instans går ind i denne tilstand, hvis du kaster den ud af konteksten, rydder eller lukker sessionen eller sætter forekomsten gennem serie- / deserialiseringsproces.

Her er et forenklet tilstandsdiagram med kommentarer til Session metoder, der får statens overgange til at ske.

Når enhedens forekomst er i vedholdende tilstand, alle ændringer, du foretager i de kortlagte felter i denne forekomst, vil blive anvendt på de tilsvarende databaseposter og felter, når du skyller Session. Det vedholdende eksempel kan betragtes som "online", mens løsrevet forekomst er gået "offline" og overvåges ikke for ændringer.

Dette betyder, at når du skifter felt i a vedholdende objekt, du behøver ikke at ringe Gemme, opdatering eller en af ​​disse metoder til at få disse ændringer til databasen: alt hvad du behøver er at begå transaktionen eller skylle eller lukke sessionen, når du er færdig med den.

2.3. Overensstemmelse med JPA-specifikation

Dvaletilstand var den mest succesrige Java ORM-implementering. Ikke underligt, at specifikationen for Java persistens API (JPA) var stærkt påvirket af Hibernate API. Desværre var der også mange forskelle: nogle store, nogle mere subtile.

For at fungere som en implementering af JPA-standarden måtte Hibernate API'er revideres. Flere metoder blev føjet til Session-interface for at matche EntityManager-interface. Disse metoder tjener det samme formål som de “originale” metoder, men er i overensstemmelse med specifikationen og har således nogle forskelle.

3. Forskelle mellem operationerne

Det er vigtigt at forstå fra starten, at alle metoderne (vedvarer, Gemme, opdatering, fusionere, gem eller opdater) ikke med det samme resultere i den tilsvarende SQL OPDATER eller INDSÆT udsagn. Den faktiske lagring af data i databasen sker ved at foretage transaktionen eller skylle Session.

De nævnte metoder styrer grundlæggende tilstanden af ​​enhedens forekomster ved at overføre dem mellem forskellige tilstande langs livscyklussen.

Som et eksempel på enheden bruger vi en simpel annoteringskortet enhed Person:

@Entity offentlig klasse Person {@Id @GeneratedValue privat Lang id; privat strengnavn; // ... getters og setters}

3.1. Fortsæt

Det vedvarer metoden er beregnet til at tilføje en ny enhedsinstans til persistens-konteksten, dvs. overføre en instans fra transient til vedholdende stat.

Vi kalder det normalt, når vi vil føje en post til databasen (vedvarer en enhedsinstans):

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

Hvad sker der efter vedvarer metode kaldes? Det person objektet er overgået fra forbigående til vedholdende stat. Objektet er i vedholdenhedskonteksten nu, men endnu ikke gemt i databasen. Generationen af INDSÆT udsagn finder kun sted, når transaktionen påbegyndes, gennemskylning eller lukning af sessionen.

Bemærk, at vedvarer metoden har ugyldig returtype. Det fungerer på det passerede objekt "på plads" og ændrer dets tilstand. Det person variabel henviser til det aktuelle vedvarende objekt.

Denne metode er en senere tilføjelse til Session-grænsefladen. Det vigtigste differentierende træk ved denne metode er, at den er i overensstemmelse med JSR-220-specifikationen (EJB-persistens). Semantikken ved denne metode er strengt defineret i specifikationen, som grundlæggende siger, at:

  • -en forbigående eksempel bliver vedholdende (og operationen kaskaderer til alle dens forbindelser med kaskade = PERSIST eller kaskade = ALLE),
  • hvis en forekomst allerede er vedholdende, så har dette kald ingen virkning for denne særlige instans (men det kaskader stadig til dets forhold til kaskade = PERSIST eller kaskade = ALLE),
  • hvis en forekomst er løsrevet, skal du forvente en undtagelse, enten når du kalder denne metode eller ved at begå eller skylle sessionen.

Bemærk, at der ikke er noget her, der vedrører identifikationen af ​​en forekomst. Specifikationen angiver ikke, at id'et genereres med det samme, uanset id-genereringsstrategien. Specifikationen for vedvarer metoden tillader implementeringen at udsende udsagn til generering af id ved commit eller flush, og id'et garanteres ikke at være ikke-nul efter at have kaldt denne metode, så du skal ikke stole på det.

Du kan kalde denne metode på en allerede vedholdende eksempel, og der sker intet. Men hvis du prøver at fortsætte a løsrevet Eksempelvis er implementeringen bundet til en undtagelse. I det følgende eksempel vi vedvarer enheden, kaste ud det fra sammenhængen, så det bliver løsrevet, og prøv derefter at vedvarer igen. Det andet opkald til session.persist () forårsager en undtagelse, så følgende kode fungerer ikke:

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

3.2. Gemme

Det Gemme metode er en "original" dvale-metode, der ikke overholder JPA-specifikationen.

Dens formål er dybest set det samme som vedvarer, men det har forskellige implementeringsdetaljer. Dokumentationen for denne metode angiver strengt, at den fortsætter forekomsten, "tildeler først en genereret identifikator". Metoden er garanteret at returnere Serialiserbar værdi af denne identifikator.

Person person = ny person (); person.setName ("John"); Lang id = (Lang) session.save (person);

Effekten af ​​at gemme en allerede vedvarende forekomst er den samme som med vedvarer. Forskellen kommer, når du prøver at gemme en løsrevet eksempel:

Person person = ny person (); person.setName ("John"); Lang id1 = (Lang) session.save (person); session.evict (person); Lang id2 = (Lang) session.save (person);

Det id2 variabel vil variere fra id1. Opkaldet til at gemme på en løsrevet eksempel opretter en ny vedholdende forekomst og tildeler den en ny identifikator, som resulterer i en duplikatregistrering i en database efter begivenhed eller skylning.

3.3. Fusionere

Hovedintentionen med fusionere metoden er at opdatere en vedholdende enhedsforekomst med nye feltværdier fra en løsrevet enhedsinstans.

Antag for eksempel, at du har en RESTful-grænseflade med en metode til at hente et JSON-serialiseret objekt med dets id til den, der ringer op, og en metode, der modtager en opdateret version af dette objekt fra den, der ringer op. En enhed, der har gennemgået en sådan serialisering / deserialisering, vises i en løsrevet stat.

Efter deserialisering af denne enhedsforekomst skal du få en vedholdende enhedsinstans fra en persistens-kontekst og opdatere dens felter med nye værdier fra denne løsrevet eksempel. Så fusionere metode gør nøjagtigt det:

  • finder en enhedsforekomst efter id taget fra det passerede objekt (enten hentes en eksisterende enhedsforekomst fra persistenskonteksten eller en ny forekomst indlæst fra databasen);
  • kopierer felter fra det passerede objekt til denne forekomst;
  • returnerer nyopdateret forekomst.

I det følgende eksempel vi kaste ud (frigør) den gemte enhed fra kontekst, skift navn felt og derefter fusionere det løsrevet enhed.

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

Bemærk, at fusionere metoden returnerer et objekt - det er fusioneretPerson objekt, der blev indlæst i persistenskontekst og opdateret, ikke person objekt, som du sendte som argument. Disse er to forskellige objekter, og person objekt skal normalt kasseres (alligevel regner du ikke med at det er knyttet til persistens-kontekst).

Som med vedvarer metode, den fusionere metode er specificeret af JSR-220 for at have visse semantik, som du kan stole på:

  • hvis enheden er løsrevet, det kopieres på en eksisterende vedholdende enhed;
  • hvis enheden er forbigående, det kopieres på en nyoprettet vedholdende enhed;
  • denne operation kaskader for alle forbindelser med kaskade = FUSION eller kaskade = ALLE kortlægning;
  • hvis enheden er vedholdende, så har denne metodeopkald ikke indflydelse på det (men kaskadeforløbet finder stadig sted).

3.4. Opdatering

Som med vedvarer og Gemme, det opdatering metoden er en "original" dvale-metode, der var til stede længe før fusionere metoden blev tilføjet. Dets semantik adskiller sig i flere nøglepunkter:

  • det virker på bestået objekt (dets returtype er ugyldig); det opdatering metoden overgår det passerede objekt fra løsrevet til vedholdende stat;
  • denne metode kaster en undtagelse, hvis du videregiver den a forbigående enhed.

I det følgende eksempel vi Gemme genstanden kaste ud (afmonter) det fra sammenhængen, og skift derefter dets navn og ring opdatering. Bemærk, at vi ikke sætter resultatet af opdatering operation i en separat variabel, fordi opdatering finder sted på person objektet selv. Dybest set tilslutter vi den eksisterende enhedsinstans til persistens-konteksten - noget, som JPA-specifikationen ikke tillader os at gøre.

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

Forsøger at ringe opdatering på en forbigående eksempel vil resultere i en undtagelse. Følgende fungerer ikke:

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

3.5. Gem eller opdater

Denne metode vises kun i Hibernate API og har ikke sin standardiserede modstykke. Svarende til opdatering, det kan også bruges til gentilslutning af forekomster.

Faktisk det indre StandardUpdateEventListener klasse, der behandler opdatering metoden er en underklasse af DefaultSaveOrUpdateListener, bare tilsidesætte nogle funktioner. Den største forskel på gem eller opdater metoden er, at den ikke kaster undtagelse, når den anvendes på en forbigående instans; i stedet gør det dette forbigående eksempel vedholdende. Den følgende kode fortsætter med en nyoprettet forekomst af Person:

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

Du kan måske tænke på denne metode som et universelt værktøj til at skabe et objekt vedholdende uanset dens tilstand, uanset om det er forbigående eller løsrevet.

4. Hvad skal jeg bruge?

Hvis du ikke har nogen specielle krav, skal du som en tommelfingerregel holde dig til vedvarer og fusionere metoder, fordi de er standardiserede og garanteret at overholde JPA-specifikationen.

De er også bærbare, hvis du beslutter at skifte til en anden udbyder af udholdenhed, men de kan undertiden virke ikke så nyttige som de "originale" dvale-metoder, Gemme, opdatering og gem eller opdater.

5. Konklusion

Vi har diskuteret formålet med forskellige Hibernate Session-metoder i forhold til styring af vedvarende enheder i løbetid. Vi har lært, hvordan disse metoder transisterer forekomster af enheder gennem deres livscyklus, og hvorfor nogle af disse metoder har dobbelt funktionalitet.

Kildekoden til artiklen er tilgængelig på GitHub.