EntityManager JPA: Perché usare persist () over merge ()?


Risposte:


1615

In entrambi i casi si aggiungerà un'entità a un PersistenceContext, la differenza è in ciò che si fa con l'entità in seguito.

Persist accetta un'istanza di entità, la aggiunge al contesto e rende tale istanza gestita (ovvero verranno tracciati i futuri aggiornamenti dell'entità).

Unisci restituisce l'istanza gestita in cui è stato unito lo stato. Restituisce qualcosa che esiste in PersistenceContext o crea una nuova istanza della tua entità. In ogni caso, copierà lo stato dall'entità fornita e restituirà la copia gestita. L'istanza trasmessa non verrà gestita (eventuali modifiche apportate non faranno parte della transazione, a meno che non si chiami nuovamente unione). È possibile utilizzare l'istanza restituita (gestita).

Forse un esempio di codice sarà di aiuto.

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)

Gli scenari 1 e 3 sono approssimativamente equivalenti, ma ci sono alcune situazioni in cui si desidera utilizzare lo scenario 2.


3
@dma_k: sembra che tu stia usando Hibernate. Ho meno familiarità con Hibernate di JPA - ma in JPA se chiami EntityManager.persist () e passi in un'entità distaccata otterrai: a) immediatamente un EntityExistsException o b) un'altra PersistenceException al momento del flush / commit. Forse ho frainteso la domanda qui?
Mike,

49
Questa risposta potrebbe essere migliorata se riguardasse anche i casi in cui l'entità da unire / persistere esiste già nel contesto persistente (o almeno chiarire che descrive il comportamento solo quando l'entità persistente / unita non esiste già)
Henry,

2
Un metodo è più performante? Forse mergela copia completa di un oggetto prima di gestirlo ha un impatto sulle prestazioni?
Kevin Meredith,

2
E gli ID? Se ho un @GeneratedIdposso ottenerlo nello scenario 2?
Rascio,

7
Mike: "Unisci crea una nuova istanza ...": questo non è sempre vero. Se EntityManager trova un'entità già gestita nel suo contesto, restituisce questa istanza (dopo aver aggiornato i campi). Modifica la tua risposta, quindi voterò per questo.
Heri,

181

Persistenza e unione hanno due scopi diversi (non sono affatto alternative).

(modificato per espandere le informazioni sulle differenze)

persistere:

  • Inserisci un nuovo registro nel database
  • Collegare l'oggetto al gestore entità.

merge:

  • Trova un oggetto allegato con lo stesso ID e aggiornalo.
  • Se esiste, aggiornare e restituire l'oggetto già collegato.
  • Se non esiste, inserire il nuovo registro nel database.

efficienza persist ():

  • Potrebbe essere più efficiente per l'inserimento di un nuovo registro in un database di merge ().
  • Non duplica l'oggetto originale.

semantica persist ():

  • Si assicura che si stia inserendo e non si aggiorni per errore.

Esempio:

{
    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);
}

In questo modo esiste solo 1 oggetto allegato per qualsiasi registro nel gestore entità.

merge () per un'entità con un ID è simile a:

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;
}

Sebbene se connesso a MySQL merge () potrebbe essere efficiente come persist () utilizzando una chiamata a INSERT con l'opzione ON DUPLICATE KEY UPDATE, JPA è una programmazione di livello molto alto e non si può presumere che sarà così ovunque.


Può fare il nome di un caso in cui non è valida per sostituire em.persist(x)con x = em.merge(x)?
Aaron Digulla,

20
persist () può generare un EntityExistsException. Se vuoi essere sicuro che il tuo codice stia eseguendo un inserimento e non un aggiornamento dei dati, devi usare persist.
Josep Panadero,

1
merge()può anche lanciare unEntityExistsException
Sean il

1
@Non è possibile perché è un RuntimeException, ma non è menzionato nel Javadoc.
Martin,

154

Se si utilizza il generatore assegnato, l' utilizzo di merge invece di persist può causare un'istruzione SQL ridondante , influendo quindi sulle prestazioni.

Inoltre, chiamare l'unione per entità gestite è anche un errore poiché le entità gestite sono gestite automaticamente da Hibernate e il loro stato è sincronizzato con il record del database dal meccanismo di controllo sporco al momento dello svuotamento del contesto di persistenza .

Per capire come funziona tutto ciò, dovresti prima sapere che Hibernate sposta la mentalità degli sviluppatori dalle istruzioni SQL alle transizioni di stato delle entità .

Una volta che un'entità è attivamente gestita da Hibernate, tutte le modifiche verranno automaticamente propagate al database.

Hibernate monitora le entità attualmente collegate. Ma affinché un'entità venga gestita, deve trovarsi nello stato entità corretto.

Per comprendere meglio le transizioni di stato JPA, è possibile visualizzare il diagramma seguente:

Transizioni di stato dell'entità JPA

O se usi l'API specifica di Hibernate:

Sospensione delle transizioni di stato delle entità

Come illustrato dai diagrammi precedenti, un'entità può trovarsi in uno dei seguenti quattro stati:

  • Nuovo (Transitorio)

    Un oggetto appena creato che non è mai stato associato a un Hibernate Session(akaPersistence Context ) e non è mappato su nessuna riga della tabella del database è considerato nello stato Nuovo (Transitorio).

    Per perseverare, è necessario chiamare esplicitamente il EntityManager#persistmetodo o utilizzare il meccanismo di persistenza transitiva.

  • Persistente (gestito)

    Un'entità persistente è stata associata a una riga della tabella del database ed è gestita dal contesto di persistenza attualmente in esecuzione. Qualsiasi modifica apportata a tale entità verrà rilevata e propagata al database (durante il flush-time della sessione). Con Hibernate, non è più necessario eseguire le istruzioni INSERT / UPDATE / DELETE. Hibernate utilizza uno stile di lavoro transazionale write-behind e le modifiche sono sincronizzate all'ultimo momento responsabile, durante l'attuale Sessionflush-time.

  • Distaccato

    Una volta chiuso il contesto di persistenza attualmente in esecuzione, tutte le entità gestite in precedenza vengono staccate. Le modifiche successive non verranno più monitorate e non verrà eseguita alcuna sincronizzazione automatica del database.

    Per associare un'entità staccata a una Sessione di ibernazione attiva, è possibile scegliere una delle seguenti opzioni:

    • ricollegamento

      Hibernate (ma non JPA 2.1) supporta il ricollegamento tramite il metodo di aggiornamento Session #. Una sessione di ibernazione può associare solo un oggetto Entity per una determinata riga del database. Questo perché il contesto di persistenza funge da cache in memoria (cache di primo livello) e solo un valore (entità) è associato a una determinata chiave (tipo di entità e identificativo del database). Un'entità può essere ricollegata solo se non esiste nessun altro oggetto JVM (corrispondente alla stessa riga del database) già associato alla sessione di ibernazione corrente.

    • Fusione

    L'unione sta per copiare lo stato di entità staccata (origine) in un'istanza di entità gestita (destinazione). Se l'entità risultante dalla fusione non ha equivalenti nella sessione corrente, ne verrà recuperata una dal database. L'istanza dell'oggetto disconnesso continuerà a rimanere staccata anche dopo l'operazione di unione.

  • Rimosso

    Sebbene l'APP richieda che solo le entità gestite possano essere rimosse, Hibernate può anche eliminare entità distaccate (ma solo tramite una chiamata al metodo di eliminazione Session #). Un'entità rimossa viene pianificata solo per l'eliminazione e l'istruzione DELETE effettiva del database verrà eseguita durante il flush-time della sessione.



@gstackoverflow La risposta che hai ricevuto è quella giusta. Per maggiori dettagli, consulta questo articolo o il mio libro, Persistenza ad alte prestazioni Java .
Vlad Mihalcea,

Pertanto non è possibile modificare l'ordine delle operazioni per orphanremoval = true?
gstackoverflow,

Il tuo articolo sull'ordine di funzionamento nel solito caso. La mia domanda specifica per orphanRemoval
gstackoverflow

1
Il fatto è che è impossibile spiegare il letargo con un diagramma del genere. Perché non riesci a scaricare la sessione dopo il distacco? Cosa succede quando si tenta di salvare un'entità già persistente? Perché quel comportamento di flush è diverso quando si tratta di salvare e persistere? Ci sono 1000 domande del genere, a cui nessuno ha una chiara logica.
GingerBeer,

37

Ho notato che quando l'ho usato em.merge, ho ricevuto una SELECTdichiarazione per tutti INSERT, anche quando non c'era campo che JPA stava generando per me - il campo chiave principale era un UUID che mi ero impostato. Sono passato a em.persist(myEntityObject)e ho ottenuto solo INSERTdichiarazioni allora.


3
Ha senso dal momento che si assegnano gli ID e il contenitore JPA non ha idea da dove sia stato ottenuto. Esiste una (piccola) possibilità che l'oggetto esista già nel database, ad esempio in uno scenario in cui diverse applicazioni scrivono nello stesso database.
Aaron Digulla,

Ho affrontato un problema simile con merge(). Avevo il database PostgreSQL con una vista complicata : la vista aggregava i dati da più tabelle (le tabelle avevano una struttura identica ma nomi diversi). Quindi JPA ha provato a farlo merge(), ma in realtà JPA è stato inizialmente creato SELECT(il database a causa delle impostazioni di visualizzazione potrebbe restituire diversi record con la stessa chiave primaria da tabelle diverse!), Quindi JPA (Hibernate era un implementatiion) fallito: ci sono diversi record con la stessa chiave ( org.hibernate.HibernateException: More than one row with the given identifier was found). Nel mio caso persist()mi ha aiutato.
flaz14,

29

La specifica JPA dice quanto segue persist().

Se X è un oggetto distaccato, EntityExistsExceptionpuò essere lanciato quando viene invocata l'operazione persist o l'uno EntityExistsExceptiono l'altro PersistenceExceptionpuò essere lanciato al momento del flush o del commit.

Quindi l'uso persist()sarebbe adatto quando l'oggetto non dovrebbe essere un oggetto distaccato. Potresti preferire che il codice PersistenceExceptionvenga lanciato in modo che fallisca rapidamente.

Sebbene le specifiche non siano chiare , è persist()possibile impostare l' @GeneratedValue @Idoggetto per un oggetto. merge()tuttavia deve avere un oggetto con il @Idgià generato.


5
+1 per " merge()tuttavia deve avere un oggetto con il @Id già generato . ". Ogni volta che EntityManager non trova un valore per il campo ID oggetto, viene persistito (inserito) nel DB.
Omar,

Non l'ho capito prima perché non ero chiaro sugli stati. Spero che questo aiuti qualcuno come ha fatto per me. docs.jboss.org/hibernate/core/3.6/reference/en-US/html/…
RBz,

1
@GeneratedValue non ha implicazioni diverse per merge () e persist ()
SandeepGodara

17

Alcuni ulteriori dettagli sull'unione che ti aiuteranno a utilizzare l'unione su persist:

La restituzione di un'istanza gestita diversa dall'entità originale è una parte fondamentale del processo di unione. Se un'istanza di entità con lo stesso identificatore esiste già nel contesto di persistenza, il provider sovrascriverà il suo stato con lo stato dell'entità che viene unita, ma la versione gestita che esisteva già deve essere restituita al client in modo che possa essere Usato. Se il provider non ha aggiornato l'istanza Employee nel contesto di persistenza, eventuali riferimenti a tale istanza diventeranno incoerenti con il nuovo stato che viene unito.

Quando merge () viene invocato su una nuova entità, si comporta in modo simile all'operazione persist (). Aggiunge l'entità al contesto di persistenza, ma invece di aggiungere l'istanza dell'entità originale, crea una nuova copia e gestisce invece quell'istanza. La copia creata dall'operazione merge () viene mantenuta come se su di essa fosse stato invocato il metodo persist ().

In presenza di relazioni, l'operazione merge () tenterà di aggiornare l'entità gestita in modo che punti alle versioni gestite delle entità a cui fa riferimento l'entità staccata. Se l'entità ha una relazione con un oggetto che non ha identità persistente, il risultato dell'operazione di unione non è definito. Alcuni provider potrebbero consentire alla copia gestita di puntare all'oggetto non persistente, mentre altri potrebbero generare immediatamente un'eccezione. L'operazione merge () può essere facoltativamente messa in cascata in questi casi per evitare che si verifichi un'eccezione. Tratteremo in cascata dell'operazione merge () più avanti in questa sezione. Se un'entità che viene unita punta a un'entità rimossa, verrà generata un'eccezione IllegalArgumentException.

Le relazioni di caricamento lento sono un caso speciale nell'operazione di unione. Se una relazione a caricamento lento non è stata attivata su un'entità prima che si staccasse, tale relazione verrà ignorata quando l'entità viene unita. Se la relazione è stata attivata mentre gestita e quindi impostata su null mentre l'entità era staccata, anche la versione gestita dell'entità avrà la relazione cancellata durante l'unione. "

Tutte le informazioni di cui sopra sono state tratte da "Pro JPA 2 Mastering the Java ™ Persistence API" di Mike Keith e Merrick Schnicariol. Capitolo 6. Separazione e fusione della sezione. Questo libro è in realtà un secondo libro dedicato all'APP dagli autori. Questo nuovo libro contiene molte nuove informazioni rispetto al precedente. Ho davvero raccomandato di leggere questo libro per coloro che saranno seriamente coinvolti con l'APP. Mi dispiace per aver pubblicato anonimamente la mia prima risposta.


17

Ci sono alcune differenze tra mergee persist(enumererò di nuovo quelle già postate qui):

D1. mergenon gestisce l'entità passata, ma restituisce piuttosto un'altra istanza gestita. persistdall'altro lato renderà gestita l'entità passata:

//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. Se rimuovi un'entità e poi decidi di mantenere l'entità indietro, puoi farlo solo con persist (), perché mergegenererà unIllegalArgumentException .

D3. Se hai deciso di occuparti manualmente dei tuoi ID (ad es. Utilizzando UUID), merge un'operazione attiverà le SELECTquery successive al fine di cercare entità esistenti con quell'ID, mentrepersist potrebbero non essere necessarie quelle query.

D4. Ci sono casi in cui semplicemente non ti fidi del codice che chiama il tuo codice e per assicurarti che nessun dato sia aggiornato, ma piuttosto inserito, devi usare persist.


8

Stavo diventando pigroCaricando eccezioni sulla mia entità perché stavo provando ad accedere a una raccolta caricata pigra che era in sessione.

Quello che avrei fatto sarebbe stato in una richiesta separata, recuperare l'entità dalla sessione e quindi provare ad accedere a una raccolta nella mia pagina jsp che era problematica.

Per ovviare a questo, ho aggiornato la stessa entità nel mio controller e l'ho passata al mio jsp, anche se immagino che quando avrò salvato di nuovo in sessione che sarà accessibile anche se SessionScopee non lancio a LazyLoadingException, una modifica dell'esempio 2:

Per me ha funzionato:

// 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!!

7

Ho trovato questa spiegazione dai documenti di Hibernate illuminante, perché contengono un caso d'uso:

L'uso e la semantica di merge () sembra essere fonte di confusione per i nuovi utenti. Innanzitutto, finché non si tenta di utilizzare lo stato oggetto caricato in un gestore entità in un altro nuovo gestore entità, non è necessario utilizzare merge () . Alcune intere applicazioni non useranno mai questo metodo.

Di solito merge () viene utilizzato nel seguente scenario:

  • L'applicazione carica un oggetto nel primo gestore entità
  • l'oggetto viene passato al livello di presentazione
  • vengono apportate alcune modifiche all'oggetto
  • l'oggetto viene restituito al livello della logica aziendale
  • l'applicazione persiste queste modifiche chiamando merge () in un secondo gestore entità

Ecco l'esatta semantica di merge ():

  • se esiste un'istanza gestita con lo stesso identificatore attualmente associato al contesto di persistenza, copiare lo stato dell'oggetto specificato sull'istanza gestita
  • se non esiste un'istanza gestita attualmente associata al contesto di persistenza, provare a caricarla dal database o creare una nuova istanza gestita
  • viene restituita l'istanza gestita
  • l'istanza data non viene associata al contesto di persistenza, rimane distaccata e di solito viene scartata

Da: http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html


6

Scorrendo le risposte mancano alcuni dettagli su `Cascade 'e la generazione dell'id. Vedi la domanda

Inoltre, vale la pena ricordare che è possibile avere Cascadeannotazioni separate per l'unione e la persistenza: Cascade.MERGEeCascade.PERSIST che verranno trattate secondo il metodo utilizzato.

La specifica è tua amica;)


6

JPA è indiscutibilmente una grande semplificazione nel dominio delle applicazioni aziendali costruite sulla piattaforma Java. Come sviluppatore che ha dovuto far fronte alle complessità dei vecchi bean di entità in J2EE, vedo l'inclusione di JPA tra le specifiche Java EE come un grande passo avanti. Tuttavia, mentre approfondisco i dettagli dell'APP, trovo cose che non sono così facili. In questo articolo mi occupo del confronto tra i metodi di unione e persistenza di EntityManager il cui comportamento sovrapposto può causare confusione non solo a un principiante. Inoltre, propongo una generalizzazione che vede entrambi i metodi come casi speciali di un metodo più generale combinato.

Entità persistenti

A differenza del metodo di unione, il metodo persist è piuttosto semplice e intuitivo. Lo scenario più comune dell'uso del metodo persist può essere riassunto come segue:

"Un'istanza appena creata della classe di entità viene passata al metodo persist. Dopo la restituzione di questo metodo, l'entità viene gestita e pianificata per l'inserimento nel database. Può accadere durante o prima del commit della transazione o quando viene chiamato il metodo flush. Se l'entità fa riferimento a un'altra entità attraverso una relazione contrassegnata con la strategia a cascata PERSIST, anche questa procedura viene applicata ad essa. "

inserisci qui la descrizione dell'immagine

La specifica va più nei dettagli, tuttavia, ricordarli non è cruciale in quanto questi dettagli coprono solo situazioni più o meno esotiche.

Unione di entità

In confronto a persistere, la descrizione del comportamento della fusione non è così semplice. Non esiste uno scenario principale, come nel caso di persist, e un programmatore deve ricordare tutti gli scenari per poter scrivere un codice corretto. Mi sembra che i progettisti dell'APP volessero avere un metodo la cui preoccupazione principale sarebbe la gestione di entità distaccate (come l'opposto del metodo persist che si occupa principalmente di entità appena create). Il compito principale del metodo di unione è trasferire lo stato da un entità non gestita (passata come argomento) alla sua controparte gestita nel contesto di persistenza. Questo compito, tuttavia, si divide ulteriormente in diversi scenari che peggiorano l'intelligibilità del comportamento complessivo del metodo.

Invece di ripetere i paragrafi dalla specifica JPA, ho preparato un diagramma di flusso che illustra schematicamente il comportamento del metodo di unione:

inserisci qui la descrizione dell'immagine

Quindi, quando dovrei usare persist e quando unire?

persistere

  • Vuoi che il metodo crei sempre una nuova entità e non aggiorni mai un'entità. In caso contrario, il metodo genera un'eccezione come conseguenza della violazione dell'unicità della chiave primaria.
  • Processi batch, gestione entità in modo stateful (vedi Pattern Gateway).
  • Ottimizzazione delle prestazioni

merge

  • Si desidera che il metodo inserisca o aggiorni un'entità nel database.
  • Vuoi gestire le entità in modo apolide (oggetti di trasferimento dati nei servizi)
  • Si desidera inserire una nuova entità che può avere un riferimento a un'altra entità che può ma non può essere ancora creata (la relazione deve essere contrassegnata come MERGE). Ad esempio, inserendo una nuova foto con un riferimento a un album nuovo o preesistente.

Qual è la differenza tra E è gestito e PC contiene una versione gestita di E?
GingerBeer,

5

Scenario X:

Tabella: Spitter (uno), Tabella: Spittles (molti) (Spittles è il proprietario della relazione con un FK: spitter_id)

Questo scenario comporta un risparmio: The Spitter ed entrambi Spittles come se fossero di proprietà di Same 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!!

Scenario Y:

Questo salverà lo Spitter, salverà i 2 Spittles Ma non faranno riferimento allo stesso 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!!

1
Lo spitter è un oggetto tratto dal libro "Spring in Action" terza edizione di Graig Walls. Gli Spitters sono persone che dicono qualcosa e il loro Spittle è ciò che stanno effettivamente dicendo. Quindi uno Spitter ha molti spittles significa che ha un elenco di stringhe.
George Papatheodorou,

1
Avresti potuto usare un esempio un po 'più leggibile senza leggere Spring in Action ...
wonderb0lt

1
In realtà non è necessario sapere cosa sia uno sputo o uno spitter poiché in cima è scritto che Spitter è un tavolo, lo spitter è un altro tavolo che possiede .. questo e quello ...
George Papatheodorou

3

Un'altra osservazione:

merge()si preoccuperà solo di un ID generato automaticamente (testato su IDENTITYe SEQUENCE) quando nella tabella esiste già un record con tale ID. In tal caso merge()proverà ad aggiornare il record. Se, tuttavia, un ID è assente o non corrisponde a nessun record esistente, merge()lo ignorerà completamente e chiederà a un db di allocarne uno nuovo. Questo a volte è fonte di molti bug. Non utilizzare merge()per forzare un ID per un nuovo record.

persist()d'altra parte non ti permetterà nemmeno di passarci un ID. Fallirà immediatamente. Nel mio caso, è:

Causato da: org.hibernate.PersistentObjectException: entità distaccata passata a persistere

hibernate-jpa javadoc ha un suggerimento:

Produce : javax.persistence.EntityExistsException - se l'entità esiste già. (Se l'entità esiste già, l'EntityExistsException può essere generata quando viene invocata l'operazione persist, oppure l'EntityExistsException o un'altra PersistenceException può essere lanciata al momento del flush o del commit.)


2
Se non stai utilizzando ID generati automaticamente, dovresti assegnare manualmente un nuovo ID alla tua Entità. persist()non si lamenterà che ha un ID, si lamenta solo quando nel database è già presente qualcosa con lo stesso ID.
ore

1

Potresti essere venuto qui per un consiglio su quando usare persist e quando usare merge . Penso che dipenda dalla situazione: quanto è probabile che sia necessario creare un nuovo record e quanto sia difficile recuperare i dati persistenti.

Supponiamo che tu possa usare una chiave / identificatore naturale.

  • I dati devono essere persistenti, ma di tanto in tanto esiste un record e viene richiesto un aggiornamento. In questo caso puoi provare a persistere e se genera un EntityExistsException, lo cerchi e combini i dati:

    provare {entityManager.persist (entity)}

    catch (eccezione EntityExistsException) {/ * recupera e unisci * /}

  • I dati persistenti devono essere aggiornati, ma di tanto in tanto non esiste ancora alcun record per i dati. In questo caso lo cerchi e fai una persistenza se l'entità manca:

    entity = entityManager.find (chiave);

    if (entity == null) {entityManager.persist (entity); }

    altrimenti {/ * merge * /}

Se non hai la chiave / identificatore naturale, avrai più difficoltà a capire se l'entità esiste o no, o come cercarla.

Le fusioni possono essere gestite anche in due modi:

  1. Se le modifiche sono in genere piccole, applicarle all'entità gestita.
  2. Se le modifiche sono comuni, copiare l'ID dall'entità persistente, nonché i dati inalterati. Quindi chiamare EntityManager :: merge () per sostituire il vecchio contenuto.

0

persist (entità) dovrebbe essere usato con entità totalmente nuove, per aggiungerle al DB (se l'entità esiste già nel DB ci sarà il lancio di EntityExistsException).

merge (entity) dovrebbe essere usato, per riportare l'entità in un contesto di persistenza se l'entità è stata staccata ed è stata cambiata.

Probabilmente persistere sta generando l'istruzione sql INSERT e unendo l'istruzione sql UPDATE (ma non ne sono sicuro).


Questo non è corretto Se chiamate merge (e) su una nuova e, è necessario che persista.
Pedro Lamarão,


Dalla specifica 2.1 della versione JPA, sezione 3.2.7.1, secondo punto elenco: "Se X è una nuova istanza di entità, viene creata una nuova istanza di entità gestita X 'e lo stato di X viene copiato nella nuova istanza di entità gestita X'."
Pedro Lamarão,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.