JPA și Hibernate oferă metode diferite pentru a persista noi și pentru a actualiza entitățile existente. Puteți alege între metodele de salvare și actualizare ale JPA persist și merge și Hibernate.
se pare că există 2 perechi de 2 metode care fac același lucru. Puteți utiliza metodele persist și save pentru a stoca o entitate nouă și metodele fuzionează și actualizează pentru a stoca modificările unei entități detașate în baza de date. De aceea, mulți dezvoltatori se întreabă Care dintre aceste metode ar trebui să folosească. Să aruncăm o privire mai atentă la detaliile și diferențele mici ale acestor metode.
mulțumiri speciale pentru Steve Ebersole (dezvoltator de plumb – Hibernate ORM), care a furnizat feedback-ul său și perspective mari pe unele dintre detaliile de implementare ascunse Hibernate lui!
- Entity state Transitions
- persistând o entitate nouă folosind persist sau save
- Specificații vs. API proprietar
- tipuri de returnare și executarea instrucțiunilor SQL
- not generated
- generat cu strategia de identitate
- generat cu strategia de secvență
- generat cu strategia tabelului
- pe care să-l alegi?
- actualizarea unei entități detașate
- metoda de îmbinare a JPA
- metoda de actualizare Hibernate
- pe care să-l alegi?
- actualizarea unei entități gestionate
Entity state Transitions
înainte de a intra în detaliile acestor 4 metode, trebuie să vă ofer o introducere rapidă în stările ciclului de viață al entității JPA.
dacă o entitate este atașată contextului actual de persistență, aceasta are starea ciclului de viață gestionată. Asta înseamnă că este mapat la o înregistrare a bazei de date. Furnizorul dvs. de persistență generează instrucțiunile SQL Insert și UPDATE necesare pentru a propaga toate modificările. O entitate gestionată este, de asemenea, stocată în memoria cache de nivel 1.
când creați o entitate nouă, aceasta se află în starea tranzitorie. Rămâne în această stare până când o atașați la contextul actual de persistență. Vă voi arăta cum puteți face acest lucru cu metoda de salvare a JPA persist și Hibernate, în secțiunea următoare. Atâta timp cât o entitate se află în starea tranzitorie, nu este mapată la o înregistrare a bazei de date și nu este gestionată de niciun context de persistență.
entitățile din starea detașată a ciclului de viață nu mai sunt gestionate de contextul persistenței. Acest lucru se poate întâmpla deoarece ați închis contextul persistenței sau ați detașat în mod explicit entitatea de contextul actual. Voi intra în mai multe detalii despre modul în care puteți reatașa aceste entități cu metodele de actualizare ale JPA merge și Hibernate într-o parte ulterioară a acestui post.
și ultima stare a ciclului de viață este eliminată. Aceste entități au fost anterior în stare gestionată, înainte de a le-ați programat pentru eliminare. Eliminarea entităților este în afara domeniului de aplicare al acestui post, așa că nu voi intra în prea multe detalii despre el. Puteți programa o entitate pentru eliminare apelând metoda eliminare pe interfața EntityManager.
persistând o entitate nouă folosind persist sau save
când creați un obiect entitate nouă, acesta se află în starea ciclului de viață tranzitoriu. Nu mapează nicio înregistrare a bazei de date.
Author a = new Author();a.setFirstName("Thorben");a.setLastName("Janssen");
trebuie să atașați entitatea la un context de persistență, astfel încât să devină gestionată și să persiste în baza de date. Puteți utiliza fie persistă JPA sau Hibernate salva metoda de a face acest lucru. Ambele metode par să facă același lucru, dar există câteva diferențe.
Specificații vs. API proprietar
diferența cea mai evidentă este că specificația JPA definește metoda persist. Puteți să-l utilizați cu toate implementările JPA. Metoda de salvare, pe de altă parte, este specifică hibernării. Prin urmare, nu este disponibil în alte implementări JPA.
dar acest lucru este relevant numai dacă doriți să puteți înlocui Hibernate cu o altă implementare JPA, cum ar fi Eclipse Link sau OpenJPA.
tipuri de returnare și executarea instrucțiunilor SQL
o altă diferență evidentă între aceste 2 metode este tipul lor de returnare. Metoda persistă JPA returnează void și Hibernate save method returnează cheia primară a entității.
asta ar putea părea o diferență uriașă, mai ales când aruncați o privire mai atentă asupra Javadoc-ului Hibernate și a specificației JPA:
- Javadoc de Hibernate save metoda afirmă că generează prima valoare cheie primară:
persistă instanța tranzitorie dată, atribuind mai întâi un identificator generat.
Sesiune Javadoc.salvare (entitate) - nu găsiți nicio informație despre acest lucru în specificația JPA. Nu definește când trebuie atribuită valoarea cheii primare. Deci, furnizorul de persistență poate face acest lucru în orice moment între apelul metodei persist și spălarea contextului persistenței.
în majoritatea cazurilor, nu contează dacă apelați metoda salvare sau persistență. Hibernate utilizează numele clasei entității și valoarea cheii primare pentru a stoca entitatea în memoria cache a primului nivel. Prin urmare, are nevoie de o valoare cheie primară atunci când execută metoda persist.
în aproape toate situațiile, Hibernate generează imediat valoarea cheii primare și declanșează o instrucțiune SQL dacă este necesar, atunci când apelați metoda persist sau save.
dar nu este cazul dacă utilizați strategia de identitate și încercați să persistați o entitate fără o tranzacție activă sau cu FlushMode.MANUAL. Dacă apelați metoda persist într-una din aceste situații, Hibernate întârzie executarea instrucțiunii SQL INSERT și creează o valoare temporară a cheii primare. Dar dacă apelați metoda de salvare, Hibernate efectuează instrucțiunea SQL INSERT imediat și preia valoarea cheii primare din Baza de date.
îl puteți prelua apoi ca valoare returnată a metodei de salvare.
Author a = new Author();a.setFirstName("Thorben");a.setLastName("Janssen");Long id = (Long) em.unwrap(Session.class).save(a);
sau puteți apela metoda getter a atributului cheie primară a entității gestionate dacă utilizați metoda persistă JPA.
Author a = new Author();a.setFirstName("Torben");a.setLastName("Janssen");em.persist(a);Long id = a.getId();
Hibernate execută aceleași instrucțiuni SQL atunci când apelați persist sau metoda de salvare. Care și când face acest lucru depinde de strategia dvs. de generare a cheilor primare:
not generated
Dacă setați valoarea cheie primară programatic, de exemplu, la un identificator natural, Hibernate efectuează numai o instrucțiune SQL INSERT atunci când se spală contextul persistența.
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 (?, ?, ?, ?)
generat cu strategia de identitate
dacă utilizați strategia de identitate pentru a genera valoarea cheii primare, Hibernate trebuie să execute instrucțiunea Inserare atunci când apelați metoda salvare sau persistență pentru a prelua valoarea cheii primare din Baza de date.
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
generat cu strategia de secvență
și dacă utilizați secvența, Hibernate efectuează o instrucțiune SQL SELECT pentru a prelua următoarea valoare din secvența bazei de date. Hibernate apoi întârzie instrucțiunea INSERT până când se spală contextul persistența. În acest exemplu, culoarea se întâmplă atunci când tranzacția este comisă.
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 (?, ?, ?, ?)
generat cu strategia tabelului
nu ar trebui să utilizați strategia tabelului, deoarece necesită încuietori la nivel de rând pe tabelul cheii primare și nu se scalează bine. Dacă utilizați această strategie oricum, Hibernate efectuează o instrucțiune SQL SELECT pentru a prelua următoarea valoare cheie primară din Baza de date și scrie noua valoare în tabelul bazei de date. Întârzie executarea instrucțiunii SQL INSERT pentru noua entitate până când elimină contextul persistenței.
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 (?, ?, ?, ?)
pe care să-l alegi?
vă puteți aștepta ca metoda salvare și persistență să se comporte diferit, deoarece există câteva diferențe între specificația JPA și Javadoc a metodelor proprietare Hibernate.
dar aproape toate aceste diferențe dispar atunci când aruncați o privire asupra implementării interne. Singurele care rămân sunt 2 cazuri de colț în care Hibernate ar putea întârzia recuperarea cheii primare, tipul de returnare al metodei și suportul de către alte implementări JPA.
pentru majoritatea aplicațiilor, nu contează dacă obțineți valoarea cheii primare generate ca tip de returnare a metodei de salvare a Hibernate sau din metoda getter a atributului cheie primară. Atâta timp cât nu utilizați un context de persistență extins și efectuați toate operațiunile bazei de date cu o tranzacție activă, vă recomand să utilizați metoda persistă a JPA.
actualizarea unei entități detașate
când închideți contextul de persistență curent sau eliminați în mod explicit o entitate din acesta apelând metodele clear sau detach pe interfața EntityManager, entitatea devine detașată. Asta înseamnă că nu mai este stocat în memoria cache de nivel 1 și că Hibernate nu va reproduce niciuna dintre modificările aplicate bazei de date.
puteți utiliza actualizarea Hibernate sau metoda de îmbinare JPA pentru a asocia o entitate detașată cu un context de persistență. După ce ați făcut acest lucru, Hibernate va actualiza baza de date pe baza valorilor atributului entității.
efectul metodei de actualizare și îmbinare pare să fie același, dar după cum veți vedea în secțiunile următoare, există o diferență importantă.
metoda de îmbinare a JPA
metoda de îmbinare a JPA copiază starea unei entități detașate într-o instanță gestionată a aceleiași entități. Hibernate, prin urmare, execută o instrucțiune SQL SELECT pentru a prelua o entitate gestionată din Baza de date. Dacă contextul persistenței conținea deja o instanță gestionată a entității, Hibernate o folosește pe cea existentă. Apoi copiază toate valorile atributelor entității gestionate și le returnează apelantului.
Author managedAuthor = em.merge(a);
După activarea jurnalizării instrucțiunilor SQL, puteți vedea instrucțiunile de selectare și actualizare executate în ieșirea jurnalului.
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=?
când Hibernate spală contextul persistenței pentru data viitoare, mecanismul său de verificare murdară verifică toate entitățile gestionate. Dacă detectează că operația de îmbinare a modificat orice valoare a atributului entității, declanșează instrucțiunea SQL UPDATE necesară.
există un detaliu important pe care trebuie să-l cunoașteți atunci când utilizați metoda de îmbinare a JPA. Hibernate copiază valorile atributelor entității detașate la entitatea gestionată. Aceasta suprascrie orice modificări pe care le-ați efectuat asupra acestei entități în cadrul sesiunii curente.
metoda de actualizare Hibernate
metoda de actualizare Hibernate nu declanșează o instrucțiune SQL SELECT. Doar atașează entitatea la contextul actual de persistență. Spre deosebire de metoda de îmbinare a JPA, nu puteți pierde nicio modificare apelând metoda de actualizare. Dacă contextul persistență conține deja o instanță gestionată a entității pe care doriți să o actualizați, aceasta aruncă o excepție.
em.unwrap(Session.class).update(a);
când Hibernate efectuează următoarea culoare, nu efectuează verificări murdare. Acest lucru nu este posibil deoarece Hibernate nu a citit cea mai recentă versiune a entității din Baza de date. Doar execută o instrucțiune SQL UPDATE pentru entitatea reatașată.
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=?
verificarea murdară lipsă provoacă o instrucțiune de actualizare SQL inutilă atunci când entitatea și înregistrarea corespunzătoare a bazei de date conțin aceleași valori. Aceasta ar putea fi o problemă dacă DBA a înregistrat un declanșator de actualizare pentru tabelul bazei de date. În aceste situații, puteți adnota entitatea dvs. cu @ SelectBeforeUpdate.
@Entity@SelectBeforeUpdatepublic class Author { ... }
care spune Hibernate să selecteze entitatea și să efectueze o verificare murdară înainte de a genera instrucțiunea SQL UPDATE. După cum puteți vedea în ieșirea jurnalului, comportamentul metodei de actualizare este acum similar cu metoda de îmbinare a JPA.
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=?
dar există o diferență semnificativă între cele 2 metode. Când apelați metoda de actualizare, Hibernate va selecta numai entitatea pe care ați furnizat-o ca parametru metodă. Dar când apelați metoda de îmbinare a JPA, Hibernate va selecta, de asemenea, toate asociațiile cu CascadeType.Fuzionează. Prin urmare, ar trebui să preferați metoda de îmbinare a JPA dacă reatașați un grafic imens de entități.
pe care să-l alegi?
nu există un răspuns general la aceste întrebări. După cum ați văzut, ambele metode au avantajele și dezavantajele lor. Trebuie să decideți pentru cazul dvs. specific de utilizare dacă Hibernate trebuie să selecteze entitatea înainte de a declanșa instrucțiunea SQL UPDATE. Și dacă acesta este cazul, trebuie să luați în considerare și profunzimea graficului entității dvs. și implicațiile de performanță ale comportamentului de preluare furnizat.
actualizarea unei entități gestionate
JPA și Hibernare facilitează actualizarea unei entități gestionate. Dacă entitatea dvs. se află în starea ciclului de viață gestionat, de ex.pentru că ați preluat-o cu o interogare JPQL sau cu metoda găsire a EntityManager, trebuie doar să modificați valorile atributelor entității.
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();
când Hibernate decide să spele contextul persistenței, mecanismul de verificare murdar va detecta modificarea și va efectua instrucțiunea SQL UPDATE necesară.
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=?
nu este necesar și nu trebuie să apelați metoda de salvare a Hibernate după ce ați actualizat o entitate. Acest lucru declanșează un eveniment suplimentar SaveOrUpdate fără a oferi beneficii. Când Hibernate decide să spele contextul persistenței, va efectua oricum verificarea murdară pentru a detecta toate modificările înainte de a executa instrucțiunile de actualizare SQL necesare.