EntityManager.merge()
pot introduce noi obiecte și actualiza pe cele existente.
De ce ar vrea să folosesc persista()` (care poate crea obiecte noi)?
Oricum va adăuga o entitate la un PersistenceContext, diferența este în ceea ce faci cu entitatea după aceea.
Persista nevoie de o entitate exemplu, adaugă-l la contextul și face ca exemplu reușit (de exemplu actualizări viitoare pentru entitate vor fi urmărite).
Merge creează o nouă instanță de entitate, copii stat din cele furnizate de entitate, și face o nouă copie a reușit. De exemplu, te trece în nu vor fi gestionate (orice modificări pe care le face nu va fi parte a tranzacției - dacă nu spui ce merge din nou).
Poate un exemplu de cod va ajuta.
MyEntity e = new MyEntity();
// scenario 1
// tran starts
em.persist(e);
e.setSomeField(someValue);
// tran ends, and the row for someField is updated in the database
// scenario 2
// tran starts
e = new MyEntity();
em.merge(e);
e.setSomeField(anotherValue);
// tran ends but the row for someField is not updated in the database
// (you made the changes *after* merging)
// scenario 3
// tran starts
e = new MyEntity();
MyEntity e2 = em.merge(e);
e2.setSomeField(anotherValue);
// tran ends and the row for someField is updated
// (the changes were made to e2, not e)
Scenariul 1 și 3 sunt aproximativ echivalente, dar există unele situații în care te'd doriți să utilizați Scenariul 2.
Persistă și îmbinare pentru două scopuri diferite (acestea sunt't alternative la toate).
(editate pentru a extinde diferențe informații)
persista:
merge:
persista() eficiența:
persista() semantica:
Exemplu:
{
AnyEntity newEntity;
AnyEntity nonAttachedEntity;
AnyEntity attachedEntity;
// Create a new entity and persist it
newEntity = new AnyEntity();
em.persist(newEntity);
// Save 1 to the database at next flush
newEntity.setValue(1);
// Create a new entity with the same Id than the persisted one.
AnyEntity nonAttachedEntity = new AnyEntity();
nonAttachedEntity.setId(newEntity.getId());
// Save 2 to the database at next flush instead of 1!!!
nonAttachedEntity.setValue(2);
attachedEntity = em.merge(nonAttachedEntity);
// This condition returns true
// merge has found the already attached object (newEntity) and returns it.
if(attachedEntity==newEntity) {
System.out.print("They are the same object!");
}
// Set 3 to value
attachedEntity.setValue(3);
// Really, now both are the same object. Prints 3
System.out.println(newEntity.getValue());
// Modify the un attached object has no effect to the entity manager
// nor to the other objects
nonAttachedEntity.setValue(42);
}
În acest fel există doar 1 obiect atașat pentru orice înregistra în entitate manager.
merge() pentru o entitate cu o identitate este ceva de genul:
AnyEntity myMerge(AnyEntity entityToSave) {
AnyEntity attached = em.find(AnyEntity.class, entityToSave.getId());
if(attached==null) {
attached = new AnyEntity();
em.persist(attached);
}
BeanUtils.copyProperties(attached, entityToSave);
return attached;
}
Deși dacă este conectat la MySQL merge() ar putea fi la fel de eficient ca persista() folosind un telefon pentru a INTRODUCE cu ON DUPLICATE KEY UPDATE opțiune, APP este un nivel foarte ridicat de programare si ai poate't presupune acest lucru va fi cazul peste tot.
Daca're folosind atribuit generator, folosind îmbinare în loc de a persista poate provoca un redundante declarație SQL, prin urmare, afectează performanța.
De asemenea, asteptare îmbinare pentru gestionate de entitățile este, de asemenea, o greșeală, deoarece nu a reușit entități sunt gestionate automat de Hibernare și de starea lor este sincronizate cu baza de date de înregistrare de murdar mecanism de verificare la spălare Persistența Context.
Pentru a înțelege cum funcționează acest lucru, trebuie să știți mai întâi că Hibernare schimburi producător mentalitatea de la declarațiile SQL pentru a entitate tranziții de stat.
Odată ce o entitate este gestionat în mod activ de Hibernare, toate modificările vor fi propagate automat la baza de date.
Hibernare monitorizează în prezent atașat entități. Dar pentru ca o entitate să devină reușit, acesta trebuie să fie într-o entitate de stat.
În primul rând, trebuie să definească toate entitate membre:
Un nou create obiect care nu a fost niciodată asociat cu o Hibernare "Sesiune" (un.k.o `Persistența Context) și nu este mapat la orice baza de date pe rând de tabel este considerat a fi în Noi (Tranzitorie) de stat.
Pentru a deveni persistat trebuie să fie în mod explicit apel la EntityManager#persist
metoda sau face uz de tranzitiv persistența mecanism.
Un persistentă entitate a fost asociat cu un tabel de bază de date de rând și este gestionat de funcționare curent Persistența Context. Orice modificare făcută la o astfel de persoană este de gând să fie detectat și propagate la baza de date (în timpul Sesiunii culoare-timp). Cu Hibernare, nu mai avem de a executa INSERT/UPDATE/DELETE declarații. Hibernare are o tranzacționale scrie-spate stil de lucru și modificările sunt sincronizate în ultimul responsabil moment, în actuala Sesiune culoare-timp.
Odată curentă Persistența Context este închis anterior reușit entități desprinse. Modificări succesive nu vor mai fi urmărite și nu automate de baze de date de sincronizare este de gând să se întâmple.
Pentru a asocia o entitate de sine stătătoare la un activ Hibernare Sesiune, puteți alege una dintre următoarele opțiuni:
Hibernare (dar nu JPA 2.1) sprijină reconectarea prin Sesiunea#metoda de actualizare. O Hibernare Sesiune poate asocia numai o Entitate obiect pentru un anumit rând al bazei de date. Acest lucru este pentru că Persistența Context acționează ca o memorie cache (primul nivel de cache) și o singură valoare (entitate) este asociată cu o anumită cheie (tip de entitate și date de identificare). O entitate poate fi refacut numai dacă nu există nici alte JVM obiect (de potrivire același rând al bazei de date) deja asociate la curent Hibernare Sesiune.
Îmbinarea este de gând să copiați entitate de sine stătătoare de stat (sursa) a reușit o entitate exemplu (destinație). Dacă fuzionează nu are echivalent în Sesiunea curentă, va fi preluat din baza de date. Detașat de obiect exemplu, va continua să rămână detașat chiar și după operația de îmbinare.
Deși APP cere reușit entități numai ca sunt permise pentru a fi eliminate, Hibernare poate șterge, de asemenea, decomandat entități (dar numai printr-o Sesiune de#a șterge apel de metodă). Un eliminate entitate este programat doar pentru ștergerea și bazei de date reale DELETE va fi executat în timpul Sesiunii de culoare în timp.
Pentru a înțelege APP tranziții de stat mai bine, puteți vizualiza următoarea diagramă:
Sau dacă utilizați Hibernare API specifice:
Am observat că atunci când eu le-am folosit.merge, eu am un "SELECTAȚI" declarație pentru fiecare INSERT
, chiar și atunci când nu a fost nici un domeniu care APP a fost generatoare pentru mine-câmpul cheie primară a fost un UUID că m-am stabilit. Am trecut la ei.persista(myEntityObject) și a luat doar pentru a INTRODUCE declarații de apoi.
APP caietul de sarcini spune următoarele despre persista()`.
Dacă X este o decomandat obiect,
EntityExistsException
poate fi aruncat atunci când persistă operațiunea este invocată, sauEntityExistsException
sau altPersistenceException
poate fi aruncat la culoare sau comite timp.
Deci, folosind persista()
ar fi potrivit atunci când obiectul nu trebuie să fie detașat de obiect. S-ar putea prefera să ai codul arunca `PersistenceException așa că eșuează repede.
Deși caietul de sarcini este neclar, persista ()
- ar putea să setați @GeneratedValue
@Id
pentru un obiect. fuziona()
cu toate acestea trebuie să aibă un obiect cu @Id
deja generate.
Aici sunt unele mai multe diferențe între merge
si persist
(voi enumera din nou cele deja postate aici):
D1. merge
nu face trecut entitate a reușit, dar mai degrabă se întoarce un alt exemplu care este gestionat. persist
pe de altă parte va face trecut entitate reușit:
//MERGE: passedEntity remains unmanaged, but newEntity will be managed
Entity newEntity = em.merge(passedEntity);
//PERSIST: passedEntity will be managed after this
em.persist(passedEntity);
D2. Dacă eliminați o entitate și apoi să decidă de a persista entitatea înapoi, ați putea face asta doar cu persista(), pentru că merge
va arunca o IllegalArgumentException
.
D3. Dacă te-ai decis pentru a avea grijă manual de Id-uri (e.g prin utilizarea Uuid), apoi un merge
operațiunea va declanșa ulterior "SELECTAȚI" interogări în scopul de a căuta existente entități cu acel ID, în timp ce persist
ar putea să nu nevoie de acele interogări.
D4. Există cazuri când pur și simplu nu au încredere în codul care solicită codul, și în scopul de a se asigura că datele nu sunt actualizate, dar mai degrabă este introdus, trebuie să utilizați persist
.
Mai multe detalii despre fuziona care vă va ajuta să utilizați merge peste persista:
Întoarce un reușit exemplu, altele decât originalul entitate este o parte esențială a fuziona proces. Dacă o entitate exemplu cu același identificator există deja în persistența context, furnizorul va suprascrie starea cu starea de entitate care este fuzionat, dar a reușit versiunea care a existat deja trebuie să fie returnat la client, astfel încât acesta poate fi utilizat. Dacă furnizorul nu update Angajatul exemplu în persistența context, orice referire la această instanță va deveni în contradicție cu noul stat fiind fuzionat în.
Când merge() este invocată pe o nouă entitate, se comportă în mod similar la persista() operațiunea. Se adaugă entitatea persistența context, dar în loc de a adăuga original entitate exemplu, se creează o nouă copia și gestionează exemplu, în loc. O copie pe care este creat de îmbinare() operațiunea este persistat ca în cazul în care persistă() metodă au fost invocate pe ea.
În prezența unor relații, unificare() operațiunea va încerca să actualizeze reușit entitate la punctul a reușit versiuni ale entităților de referință de către entitate de sine stătătoare. Dacă entitatea are o relația cu un obiect care nu are nici persistente de identitate, rezultatul de operația de îmbinare este nedefinit. Unii furnizori pot permite reușit copie la punctul de non-persistente obiect, în timp ce alții ar putea arunca o excepție imediat. Unificare() operație poate fi opțional cascadă în aceste cazuri, pentru a preveni o excepție de la care apar. Vom acoperi în cascadă de îmbinare() funcționarea mai târziu în această secțiune. Dacă o entitate fiind fuzionat puncte la un eliminate entitate, un IllegalArgumentException excepție va fi aruncat.
Leneș-încărcare relațiile sunt un caz special în operația de îmbinare. Dacă un leneș-încărcare relația nu a fost determinat, pe de o entitate înainte de a deveni detașat, că relația va fi ignorat atunci când entitatea este fuzionat. Dacă relația a fost declanșată în timp ce a reușit și apoi setat la nul, în timp ce entitatea a fost detașată, a reușit versiune de entitatea va trebui, de asemenea, relația eliminat în timpul fuziona."
Toate informațiile de mai sus a fost luat de la "Pro APP 2 Stăpânirea Java Persistence API" de Mike Keith și Merrick Schnicariol. Capitolul 6. Secțiunea de detașare și fuzionează. Această carte este de fapt o a doua carte dedicată APP de autori. Această nouă carte a multe informații noi apoi a fostul unul. Am foarte recomanda să citiți această carte pentru cei care vor fi implicați serios cu JPA. Îmi pare rău pentru anonimously de a posta primul meu răspuns.
Am fost obtinerea lazyLoading excepții de la entitatea mea pentru că am fost încercarea de a accesa un leneș încărcate de colectare, care era în sesiune.
Ce m-aș face a fost într-o cerere separată, de a prelua entitate din sesiune și apoi încercați să accesați o colecție în pagina jsp care a fost problematică.
Pentru a atenua acest lucru, am actualizat aceeași entitate în controler și a trecut-o să-mi jsp, deși îmi imaginez că atunci când am re-salvate în sesiune, care va fi, de asemenea, accesibile pentru SessionScopeși nu arunca o LazyLoadingException
, o modificare a exemplul 2:
Următoarele a lucrat pentru mine:
// scenario 2 MY WAY
// tran starts
e = new MyEntity();
e = em.merge(e); // re-assign to the same entity "e"
//access e from jsp and it will work dandy!!
APP este, indiscutabil, un mare simplificare în domeniul enterprise aplicații construite pe platforma Java. Ca un dezvoltator care a trebuit să a face față cu complexitatea vechi entitate de fasole în J2EE văd includerea JPA printre Java EE caietul de sarcini ca un salt mare înainte. Cu toate acestea, în timp ce pătrunde mai adânc în APP detalii mă găsiți lucruri care nu sunt atât de ușor. În acest articol am de-a face cu comparație a EntityManager merge si persista metode a căror suprapunere comportament poate provoca confuzie nu numai pentru un newbie. În Plus Am propune o generalizare care vede ambele metode ca și cazuri speciale de mai generală metodă de a combina.
Persistă entități
În contrast cu îmbinare metoda persista metodă este destul de simplă și intuitivă. Cel mai comun scenariu a persista metoda's utilizare poate fi rezumată după cum urmează:
"Un nou create exemplu de clasa entitate este trecut la persista metodă. După această metodă se întoarce, entitatea este administrată și planificate pentru introducerea în baza de date. Se poate întâmpla la sau înainte de tranzacție se angajează sau atunci când metoda flush este numit. Dacă entitatea trimiterile o altă entitate printr-o relație marcată cu PERSISTA cascadă strategia această procedură se aplică, de asemenea,."
Caietul de sarcini se duce mai mult în detalii, cu toate acestea, amintindu-le nu este crucial ca aceste detalii acoperi mai mult sau mai puțin exotice situații.
Fuziunea entităților
În comparație cu persistă, descriere a merge's de comportament nu este atât de simplu. Nu este scenariul de bază, cum este în cazul persistă, și un programator trebuie să ne amintim toate scenariile, în scopul de a scrie un cod corect. Mi se pare că APP designeri vrut să aibă o metodă a cărei preocupare principală s-ar ocupa detașați entități (ca opus la persista metodă care se ocupă cu entităților nou create în primul rând.) Îmbinarea metodei's sarcină importantă este de a transfera stat la o unmanaged entitate (transmis ca argument) a reușit omologul său în persistența context. Această sarcină, cu toate acestea, se împarte în continuare în mai multe scenarii care agrava inteligibilitate a metodei generale's de comportament.
În loc de a repeta punctele de APP caietul de sarcini-am pregătit o diagramă care descrie schematic comportamentul merge metoda:
Deci, atunci când ar trebui să folosesc persistă și atunci când merge?
persistă
merge
Trece prin răspunsurile lipsesc câteva detalii cu privire la Cascade' si id-ul de generație. A se vedea întrebarea
De asemenea, este de remarcat faptul că puteți fi separate "în Cascadă" adnotări pentru care fuzionează și persistente: `Cascadă.MERGE " și " în Cascadă.PERSISTA care vor fi tratate în funcție de metoda utilizată.
Spec este prietenul tău ;)
Am găsit această explicație din Hibernare documente edificatoare, deoarece acestea conțin un caz de utilizare:
utilizarea și semantica de îmbinare() pare a fi confuz pentru utilizatorii noi. În primul rând, atâta timp cât nu sunteți încercarea de a utiliza obiecte încărcate de stat într-o singură entitate manager într-o altă entitate nou manager, ar trebui să nu trebuie să utilizați merge() la toate. Niște aplicații nu va folosi această metodă.
de Obicei merge() este folosită în următorul scenariu:
- sarcinile De aplicare a unui obiect în prima entitate manager
- obiectul este trecut până la stratul de prezentare
- unele modificări sunt făcute la obiect
- obiectul este trecut înapoi la stratul de logica de afaceri
- cererea persistă aceste modificări de asteptare merge() într-o a doua entitate manager
Aici este exact semantic de îmbinare():
- dacă există un reușit exemplu cu același identificator în prezent asociate cu persistența context, copia starea de obiect dat pe reușit exemplu
- dacă nu există nici un reușit exemplu, în prezent, asociate cu persistența context, încercați să-l încarce din baza de date, sau pentru a crea o nouă reușit exemplu
- a reușit exemplu este returnat
- dat exemplu nu deveni asociat cu persistența context, rămâne detașat și este, de obicei aruncate
De la: http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html
Scenariu X:
Masa:Spitter (Unul) ,Masa: Spittles (Multe) (Spittles este Proprietarul relație cu un FK:spitter_id)
Acest scenariu rezultate în salvarea : Scuipătorul și ambele Spittles ca daca deținute de Același Spitter.
Spitter spitter=new Spitter();
Spittle spittle3=new Spittle();
spitter.setUsername("George");
spitter.setPassword("test1234");
spittle3.setSpittle("I love java 2");
spittle3.setSpitter(spitter);
dao.addSpittle(spittle3); // <--persist
Spittle spittle=new Spittle();
spittle.setSpittle("I love java");
spittle.setSpitter(spitter);
dao.saveSpittle(spittle); //<-- merge!!
Scenariu Y:
Acest lucru va salva Spitter, va salva 2 Spittles Dar ei nu vor referință același Spitter!
Spitter spitter=new Spitter();
Spittle spittle3=new Spittle();
spitter.setUsername("George");
spitter.setPassword("test1234");
spittle3.setSpittle("I love java 2");
spittle3.setSpitter(spitter);
dao.save(spittle3); // <--merge!!
Spittle spittle=new Spittle();
spittle.setSpittle("I love java");
spittle.setSpitter(spitter);
dao.saveSpittle(spittle); //<-- merge!!
O altă observație:
fuziona()
va pasa doar de un auto-generat id-ul(testat pe "IDENTITATE" și SECVENȚĂ) atunci când o înregistrare cu o astfel de id-ul există deja în tabel. În acest caz merge()
va încerca pentru a actualiza înregistrarea.
Dacă, totuși, o identitate este absent sau nu este de potrivire orice înregistrările existente, merge()
va ignora complet și cere un db pentru a aloca un nou unul. Acest lucru este uneori o sursă de o mulțime de bug-uri. A nu se folosi merge()
pentru a forța un id pentru un nou record.
persista()
pe de altă parte, nu te va lasa sa trece chiar și un act de identitate la ea. Acesta va eșua imediat. În cazul meu, a's:
Cauzate de: org.hibernare.PersistentObjectException: entitate de sine stătătoare a trecut la persista
hibernare-app javadoc are o sugestie:
Aruncă: javax.persistența.EntityExistsException - dacă entitatea există deja. (Dacă entitatea există deja, EntityExistsException poate fi aruncat atunci când persistă operațiunea este invocate, sau EntityExistsException sau un alt PersistenceException poate fi aruncat la culoare sau comite timp.)
Poate ați venit aici pentru sfaturi cu privire la când să utilizați persistă și când să utilizați merge. Cred că depinde de situație: cât de probabil este că aveți nevoie pentru a crea un nou record și cât de greu este de a prelua datele persistente.
Las's presupun puteți utiliza o cheie naturale/identificare.
încercați { entityManager.persista(entitate) }
a prinde(EntityExistsException excepție) { / de a prelua și îmbina / }
entitate = entityManager.find(key);
dacă (entitate == null) { entityManager.persista(entitate); }
else { / merge / }
Dacă tu nu't trebuie naturale cheie/identificator, ai'll au un timp mai greu să-mi dau seama dacă entitatea exista sau nu, sau cum să-l caute.
Care fuzionează pot fi tratate în două moduri:
persista(entitate) ar trebui să fie utilizat cu totul nouă entități, pentru a le adăuga la DB (dacă entitatea există deja în DB nu va fi EntityExistsException arunca).
merge(entitate) ar trebui să fie utilizate, pentru a pune entitate înapoi la persistența context dacă entitatea a fost detașat și a fost schimbat.
Probabil persistă este generatoare a INTRODUCE declarație sql și merge UPDATE sql (dar nu'm nu sunt sigur).