Hibernate: save, persist, update, merge, saveOrUpdate

Introducere

în acest articol vom discuta diferențele dintre mai multe metode ale interfeței sesiunii: save, persist, update, merge, saveOrUpdate.

aceasta nu este o introducere în hibernare și ar trebui să cunoașteți deja elementele de bază ale configurației, maparea obiect-relațională și lucrul cu instanțele entității. Pentru un articol introductiv Pentru hibernare, vizitați tutorialul nostru despre hibernare 4 cu primăvară.

lecturi suplimentare:

ștergerea obiectelor cu Hibernate

ghid rapid pentru ștergerea unei entități în Hibernate.
Citeste mai mult →

proceduri stocate cu Hibernate

acest articol discută în scurt timp cum să apelați procedurile de stocare din Hibernate.
Citeste mai mult →

o prezentare generală a identificatorilor din Hibernate / JPA

Aflați cum să mapați identificatorii entității cu Hibernate.
Citeste mai mult →

sesiunea ca implementare de context persistență

interfața sesiunii are mai multe metode care duc în cele din urmă la salvarea datelor în baza de date: persist, save, update, merge, saveOrUpdate. Pentru a înțelege diferența dintre aceste metode, trebuie să discutăm mai întâi scopul sesiunii ca context de persistență și diferența dintre stările instanțelor entității în raport cu sesiunea.

ar trebui să înțelegem, de asemenea, istoria dezvoltării Hibernate care a dus la unele metode API parțial duplicate.

2.1. Gestionarea instanțelor entității

în afară de maparea obiect-relațională în sine, una dintre problemele pe care Hibernate a fost destinată să le rezolve este problema gestionării entităților în timpul rulării. Noțiunea de” context de persistență ” este soluția Hibernate la această problemă. Persistența context poate fi gândit ca un container sau un cache de prim nivel pentru toate obiectele pe care le încărcate sau salvate într-o bază de date în timpul unei sesiuni.

sesiunea este o tranzacție logică, ale cărei limite sunt definite de logica de afaceri a aplicației dvs. Când lucrați cu baza de date printr-un context de persistență și toate instanțele entității dvs. sunt atașate la acest context, ar trebui să aveți întotdeauna o singură instanță de entitate pentru fiecare înregistrare a bazei de date cu care ați interacționat în timpul sesiunii.

în hibernare, contextul persistenței este reprezentat de org.hibernează.Instanță de sesiune. Pentru JPA, este javax.persistență.Managerul EntityManager. Când folosim Hibernate ca furnizor JPA și operăm prin interfața EntityManager, implementarea acestei interfețe înfășoară practic obiectul sesiunii de bază. Cu toate acestea, Hibernate Session oferă o interfață mai bogată, cu mai multe posibilități, astfel încât uneori este util să lucrați direct cu sesiunea.

2.2. Stări ale instanțelor entității

orice instanță a entității din aplicația dvs. apare într-una din cele trei stări principale în raport cu contextul persistenței sesiunii:

  • transient — această instanță nu este și nu a fost niciodată atașată unei sesiuni; această instanță nu are rânduri corespunzătoare în baza de date; de obicei este doar un obiect nou pe care l-ați creat pentru a-l salva în baza de date;
  • persistent — această instanță este asociată cu un obiect unic de sesiune; la spălarea sesiunii în baza de date, această entitate este garantată să aibă o înregistrare consistentă corespunzătoare în baza de date;
  • detașat — această instanță a fost atașată odată la o sesiune (într-o stare persistentă), dar acum nu este; o instanță intră în această stare dacă o evacuați din context, ștergeți sau închideți sesiunea sau puneți instanța prin procesul de serializare/deserializare.

Iată o diagramă de stare simplificată cu comentarii despre metodele de sesiune care fac tranzițiile de stare să se întâmple.

2016-07-11_13-38-11

când instanța entității este în starea persistentă, toate modificările pe care le faceți câmpurilor mapate ale acestei instanțe vor fi aplicate înregistrărilor și câmpurilor bazei de date corespunzătoare la spălarea sesiunii. Instanța persistentă poate fi considerată „online”, în timp ce instanța detașată a trecut „offline” și nu este monitorizată pentru modificări.

aceasta înseamnă că atunci când modificați câmpurile unui obiect persistent, nu trebuie să apelați salvare, actualizare sau oricare dintre aceste metode pentru a obține aceste modificări în baza de date: tot ce aveți nevoie este să comiteți tranzacția sau să spălați sau să închideți sesiunea, când ați terminat cu ea.

2.3. Conformitatea cu specificația JPA

Hibernate a fost cea mai reușită implementare Java ORM. Nu e de mirare că specificația pentru Java persistence API (JPA) a fost puternic influențată de API-ul Hibernate. Din păcate, au existat și multe diferențe: unele majore, altele mai subtile.

pentru a acționa ca o implementare a standardului JPA, API-urile Hibernate au trebuit revizuite. Mai multe metode au fost adăugate la interfața sesiune pentru a se potrivi cu interfața EntityManager. Aceste metode servesc aceluiași scop ca și metodele „originale”, dar sunt conforme cu specificațiile și au astfel unele diferențe.

diferențele dintre operațiile

este important să înțelegem de la început că toate metodele (persist, save, update, merge, saveOrUpdate) nu rezultă imediat în instrucțiunile SQL UPDATE sau INSERT corespunzătoare. Salvarea efectivă a datelor în baza de date are loc la comiterea tranzacției sau la spălarea sesiunii.

metodele menționate gestionează practic starea instanțelor entității prin tranziția acestora între diferite stări de-a lungul ciclului de viață.

ca o entitate exemplu, vom folosi un simplu persoană entitate mapată-adnotare:

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

3.1. Persist

metoda persist este destinată adăugării unei noi instanțe de entitate în contextul persistenței, adică trecerea unei instanțe de la starea tranzitorie la starea persistentă.

îl numim de obicei atunci când dorim să adăugăm o înregistrare la baza de date (persistă o instanță entitate):

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

ce se întâmplă după ce se numește metoda persist? Obiectul persoanei a trecut de la starea tranzitorie la cea persistentă. Obiectul se află în contextul persistenței acum, dar nu a fost încă salvat în baza de date. Generarea declarațiilor de inserare va avea loc numai la comiterea tranzacției, spălarea sau închiderea sesiunii.

observați că metoda persist are tipul de returnare nulă. Funcționează pe obiectul trecut” în loc”, schimbându-și starea. Variabila persoană face referire la obiectul real persistat.

această metodă este o adăugare ulterioară la interfața sesiunii. Principala caracteristică diferențiatoare a acestei metode este că este conformă cu specificația JSR-220 (persistența EJB). Semantica acestei metode este strict definită în specificație, care afirmă practic că:

  • o instanță tranzitorie devine persistentă (și operația se încadrează în toate relațiile sale cu cascade=PERSIST sau cascade=ALL),
  • dacă o instanță este deja persistentă, atunci acest apel nu are niciun efect pentru această instanță particulară (dar încă se încadrează în relațiile sale cu cascade=PERSIST sau cascade=ALL),
  • dacă o instanță este detașată, trebuie să vă așteptați la o excepție, fie la apelarea acestei metode, fie la comiterea sau spălarea sesiunii.

observați că nu există nimic aici care să se refere la identificatorul unei instanțe. Spec nu precizează că id-ul va fi generat imediat, indiferent de strategia de generare id. Specificația pentru metoda persist permite implementării să emită instrucțiuni pentru generarea id-ului pe commit sau flush, iar id-ul nu este garantat să fie non-nul după apelarea acestei metode, deci nu ar trebui să vă bazați pe ea.

puteți apela această metodă pe o instanță deja persistentă și nu se întâmplă nimic. Dar dacă încercați să persiste o instanță detașată, punerea în aplicare este obligat să arunce o excepție. În exemplul următor, persistăm entitatea, o evacuăm din context, astfel încât să se detașeze și apoi încercăm să persistăm din nou. Al doilea apel la sesiune.persist () provoacă o excepție, astfel încât următorul cod nu va funcționa:

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

3.2. Salvare

metoda de salvare este o metodă de hibernare „originală” care nu este conformă cu specificația JPA.

scopul său este în esență același cu persist, dar are detalii diferite de implementare. Documentația pentru această metodă afirmă strict că persistă instanța, „atribuind mai întâi un identificator generat”. Metoda este garantată pentru a returna valoarea Serializabilă a acestui identificator.

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

efectul salvării unei instanțe deja persistente este același ca și cu persist. Diferența vine atunci când încercați să salvați o instanță detașată:

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

variabila id2 va diferi de id1. Apelul de salvare pe o instanță detașată creează o nouă instanță persistentă și îi atribuie un nou identificator, ceea ce duce la o înregistrare duplicată într-o bază de date la comiterea sau spălarea.

3.3. Merge

intenția principală a metodei merge este de a actualiza o instanță entitate persistentă cu noi valori de câmp dintr-o instanță entitate detașată.

de exemplu, să presupunem că aveți o interfață RESTful cu o metodă pentru preluarea unui obiect serializat JSON prin id-ul său către apelant și o metodă care primește o versiune actualizată a acestui obiect de la apelant. O entitate care a trecut printr-o astfel de serializare/deserializare va apărea într-o stare detașată.

după deserializarea acestei instanțe de entitate, trebuie să obțineți o instanță de entitate persistentă dintr-un context de persistență și să actualizați câmpurile sale cu valori noi din această instanță detașată. Deci, metoda de îmbinare face exact asta:

  • găsește o instanță entitate de id-ul luate de la obiectul trecut (fie o instanță entitate existentă din contextul persistența este preluat, sau o nouă instanță încărcate din Baza de date);
  • copiază câmpurile din obiectul trecut la această instanță;
  • returnează instanță nou actualizat.

în exemplul următor evacuăm (detașăm) entitatea salvată din context, schimbăm câmpul Nume și apoi fuzionăm entitatea detașată.

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

rețineți că metoda de îmbinare returnează un obiect — obiectul mergedPerson a fost încărcat în contextul persistenței și actualizat, Nu obiectul persoană pe care l-ați transmis ca argument. Acestea sunt două obiecte diferite, iar obiectul persoanei trebuie de obicei aruncat (oricum, nu conta pe faptul că este atașat contextului de persistență).

ca și în cazul metodei persist, metoda de îmbinare este specificată de JSR-220 pentru a avea anumite semantici pe care vă puteți baza:

  • dacă entitatea este detașată, aceasta este copiată pe o entitate persistentă existentă;
  • dacă entitatea este tranzitorie, este copiată pe o entitate persistentă nou creată;
  • Această operațiune este în cascadă pentru toate relațiile cu cascade=MERGE sau cascade=all mapping;
  • dacă entitatea este persistentă, atunci acest apel de metodă nu are efect asupra acesteia (dar Cascada are loc în continuare).

3.4. Actualizare

ca și în cazul persist și save, metoda de actualizare este o metodă de hibernare „originală” care a fost prezentă cu mult înainte de adăugarea metodei de îmbinare. Semantica sa diferă în mai multe puncte cheie:

  • acționează asupra obiectului trecut (tipul său de returnare este nul); metoda de actualizare trece obiectul trecut de la detașat la starea persistentă;
  • această metodă aruncă o excepție dacă îl treci o entitate tranzitorie.

în exemplul următor salvăm obiectul, apoi îl evacuăm (îl detașăm) din context, apoi îi schimbăm numele și apelăm la actualizare. Observați că nu punem rezultatul operației de actualizare într-o variabilă separată, deoarece actualizarea are loc pe obiectul persoană în sine. Practic, reatașăm instanța entității existente la contextul persistenței-ceva ce specificația JPA nu ne permite să facem.

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

încercarea de a apela actualizare pe o instanță tranzitorie va duce la o excepție. Următoarele nu vor funcționa:

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

3.5. SaveOrUpdate

această metodă apare numai în API-ul Hibernate și nu are omologul său standardizat. Similar cu actualizare, de asemenea, poate fi folosit pentru reatașarea instanțe.

de fapt, clasa DefaultUpdateEventListener internă care procesează metoda de actualizare este o subclasă de DefaultSaveOrUpdateListener, doar suprascrie unele funcționalități. Principala diferență a metodei saveOrUpdate este că nu aruncă excepție atunci când este aplicată unei instanțe tranzitorii; în schimb, face ca această instanță tranzitorie să fie persistentă. Următorul cod va persista o instanță nou creată de persoană:

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

vă puteți gândi la această metodă ca la un instrument universal pentru a face un obiect persistent, indiferent de starea sa, indiferent dacă este tranzitoriu sau detașat.

ce să folosiți?

dacă nu aveți cerințe speciale, de regulă, ar trebui să respectați metodele persist și merge, deoarece acestea sunt standardizate și garantate pentru a se conforma specificației JPA.

de asemenea, sunt portabile în cazul în care decideți să treceți la un alt furnizor de persistență, dar uneori pot părea nu atât de utile ca metodele de hibernare „originale”, salvați, actualizați și salvațiorupdate.

concluzie

am discutat despre scopul diferitelor metode de sesiune de hibernare în legătură cu gestionarea entităților persistente în timpul rulării. Am învățat cum aceste metode tranzitează instanțele entității prin ciclurile lor de viață și de ce unele dintre aceste metode au duplicat funcționalitatea.



+