Domanda

EntityManager.merge() possibile inserire nuovi oggetti e aggiornamento di quelle esistenti.

Perché si dovrebbe desidera utilizzare persist() (che può solo creare nuovi oggetti)?

È stato utile?

Soluzione

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

Persistere prende un'istanza di entità, aggiunge al contesto e rende tale istanza gestito (vale a dire gli aggiornamenti futuri per l'entità verranno monitorati).

Merge crea una nuova istanza del soggetto, copia lo stato dall'entità in dotazione, e rende la nuova copia gestita. L'istanza si passa non saranno gestiti (tutte le modifiche apportate non saranno parte della transazione - a meno che non si chiami unire di nuovo).

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)

Scenario 1 e 3 sono più o meno equivalenti, ma ci sono alcune situazioni in cui ci si desidera utilizzare Scenario 2.

Altri suggerimenti

persistono e merge sono per due scopi diversi (non sono alternative a tutti).

(a cura di espandere le informazioni differenze)

persistere:

  • Inserire un nuovo registro per il database
  • Collegare l'oggetto al gestore di entità.

fusione:

  • Trova un oggetto attaccato con lo stesso ID e aggiornarla.
  • Se esiste aggiornamento e restituire l'oggetto già allegato.
  • Se non esiste il montaggio del nuovo registro al database.

persistono () efficienza:

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

persistono () la semantica:

  • Si fa in modo che si sta inserendo e non aggiornare per sbaglio.

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 allegata qualsiasi registro nel gestore entità.

merge () per un'entità con un id è qualcosa come:

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

Anche se collegato a MySQL merge () potrebbe essere il più efficiente persistono () utilizzando una chiamata a INSERIRE opzione KEY AGGIORNAMENTO SULLA duplicati con, APP è una programmazione di altissimo livello e non si può assumere questo sta per essere il caso ovunque.

Se si utilizza il generatore assegnato, utilizzando unire invece di persistere può causare uno SQL ridondante dichiarazione , prestazioni quindi che colpisce.

Inoltre, chiamando fondono per entità gestite è anche un errore in quanto entità gestite sono automaticamente gestito da Sospensione e il loro stato è sincronizzato con il record del database dal meccanismo di controllo sporco su lavaggio del contesto di persistenza .

Per capire come tutto questo funziona, si deve prima sapere che Hibernate sposta la mentalità sviluppatore da istruzioni SQL per transizioni di stato entità .

Una volta che un soggetto è gestito attivamente da Hibernate, tutte le modifiche stanno per essere propagato automaticamente al database.

Sospensione monitor attualmente collegati entità. Ma per un'entità a diventare gestito, deve essere in stato di entità destra.

In primo luogo, dobbiamo definire tutti gli stati di entità:

  • Nuovo (Transient)

    Un oggetto appena creato che non è mai stato associato con un Hibernate Session (a.k.a Persistence Context) e non è mappato ad una riga della tabella di database è considerato nello stato Nuovo (Transient).

    Per diventare persistito dobbiamo esplicitamente chiamare il metodo EntityManager#persist o usufruire del meccanismo di persistenza transitiva.

  • Persistente (gestito)

    Un'entità persistente è stata associata con una riga della tabella del database ed è gestito dalla corrente in esecuzione contesto di persistenza. Qualsiasi modifica apportata a tale entità sta per essere rilevato e propagato al database (durante la sessione flush-tempo). Con Hibernate, non abbiamo più ad eseguire INSERT / UPDATE / DELETE. Hibernate si avvale di un write-behind stile di lavoro transazionale e le modifiche sono sincronizzato all'ultimo momento responsabile, durante il <=> filo-tempo corrente.

  • indipendente

    Una volta che la corrente che contesto di persistenza è chiuso tutte le entità in precedenza gestite si staccano. modifiche successive non verranno più monitorati e non la sincronizzazione automatica del database sta per accadere.

    Per associare un'entità indipendente a un Hibernate sessione attiva, è possibile scegliere una delle seguenti opzioni:

    • riattaccare

      Hibernate (ma non JPA 2.1) supporta riattaccare attraverso il metodo di aggiornamento Session #. Un Hibernate Session può associare un solo oggetto entità per una data riga di database. Questo perché il contesto di persistenza agisce come una cache in memoria (cache di primo livello) e solo valore (entità) è associato ad una determinata chiave (tipo di entità e l'identificatore del database). Un'entità può essere riattaccata solo se non v'è altro oggetto JVM (corrispondente alla stessa riga database) già associato alla corrente Hibernate Session.

    • La fusione

    La fusione sta per copiare lo stato di un'entità indipendente (sorgente) a un'istanza entità gestite (destinazione). Se l'entità fusione non ha equivalenti nella corrente sessione, uno sarà prelevato dalla banca dati. L'istanza di oggetto distaccato continuerà a rimanere indipendente anche dopo l'operazione di unione.

  • Rimosso

    Anche se JPA richieste che sono riuscite entità Sono consentite solo per essere rimosso, Hibernate può anche eliminare le entità indipendenti (ma solo attraverso una sessione # cancella chiamata al metodo). Un'entità rimosso è prevista solo per l'eliminazione e il database effettivo DELETE verrà eseguired durante la sessione filo-tempo.

Per capire le transizioni di stato JPA meglio, è possibile visualizzare il seguente schema:

entrare descrizione dell'immagine qui

Se si utilizza l'API specifica Hibernate:

entrare descrizione dell'immagine qui

Ho notato che quando ho usato em.merge, ho ottenuto un SELECT dichiarazione per ogni INSERT, anche quando non c'era campo che APP stava generando per me - il campo chiave primaria è stato un UUID che ho impostato me stessa. Sono passato a em.persist(myEntityObject) ed ho ottenuto solo <=> dichiarazioni poi.

La specifica JPA dice quanto segue riguardo persist().

  

Se X è un oggetto indipendente, il EntityExistsException può essere generata quando il persistono   operazione viene invocato, o la PersistenceException o in un altro @GeneratedValue può essere gettato a filo o commit.

Quindi, utilizzando @Id sarebbe adatto quando l'oggetto dovrebbe non di essere un oggetto indipendente. Si potrebbe preferire di avere il codice di gettare il merge() così non riesce veloce.

Anche se la specifica non è chiaro , <=> potrebbe impostare il <=> <=> per un oggetto. <=> tuttavia deve avere un oggetto con il <=> già generato.

Ci sono alcune altre differenze tra merge e persist (io enumerare ancora una volta quelli già postato qui):

D1. IllegalArgumentException non rende l'entità passato è riuscito, ma restituisce un'altra istanza che viene gestito. SELECT dall'altra parte farà l'entità passato è riuscito:

//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 si rimuove un soggetto e poi decidere a persistere l'entità indietro, si può fare che solo con persistono (), perché <=> genera un <=>.

D3. Se avete deciso di prendersi cura manualmente gli ID (per esempio utilizzando UUID), poi un <=> operazione innescherà successive interrogazioni <=> al fine di cercare per le entità esistenti con quel ID, mentre <=> non essere necessario le query.

D4. Ci sono casi in cui semplicemente non si fidano del codice che chiama il codice, e al fine di assicurarsi che nessun dato è aggiornato, ma piuttosto è inserito, è necessario utilizzare <=>.

Alcuni ulteriori dettagli su merge che vi aiuterà ad utilizzare fondersi nel corso persistono:

  

Tornando un'istanza gestito diverso dall'ente originale è una parte critica della fusione   processi. Se un'istanza entità con lo stesso identificatore è già presente nel contesto di persistenza, la   fornitore sovrascriverà il suo stato con lo stato del soggetto che viene fusa, ma il gestito   versione che esisteva già deve essere restituito al cliente in modo che possa essere utilizzato. Se il provider non ha   aggiornare l'istanza Employee nel contesto di persistenza, alcun riferimento a tale istanza diventerà   in contrasto con il nuovo stato di essere fusi in.

     

Quando merge () viene richiamato in una nuova entità, si comporta in modo simile al funzionamento persistere (). aggiunge   l'entità al contesto di persistenza, ma invece di aggiungere l'istanza dell'entità originale, crea un nuovo   copiare e gestisce tale istanza, invece. La copia che viene creato l'operazione di unione () viene mantenuto   come se il persistono () il metodo è stato invocato su di esso.

     

In presenza di rapporti, l'operazione di unione () tenterà di aggiornare l'entità gestita   per puntare a versioni gestite delle entità a cui fa riferimento l'entità indipendente. Se l'entità ha un   relazione ad un oggetto che ha identità persistente, il risultato dell'operazione di unione è   non definito. Alcuni provider potrebbero permettere la copia è riuscito a puntare verso l'oggetto non persistente,   mentre altri potrebbero un'eccezione immediatamente. L'operazione di unione () può essere opzionalmente   cascata in questi casi per evitare un'eccezione si verifichi. Tratteremo cascata del merge ()   funzionamento avanti in questa sezione. Se un'entità essere punti unite ad un'entità rimosso, un   verrà generata IllegalArgumentException eccezione.

     

rapporti Lazy-caricamento sono un caso speciale in l'operazione di unione. Se un pigro-caricamento   relazione non è stata innescata su un'entità prima di diventare indipendente, che il rapporto sarà   ignorato quando l'entità viene unita. Se il rapporto è stato attivato mentre gestito e quindi impostare a nulla, mentre l'entità è stata staccata, la versione gestita della entità sarà allo stesso modo avere il rapporto eliminato durante l'unione ".

Tutte le informazioni di cui sopra è stata presa da "Pro JPA 2 Mastering API Java Persistence ™" da Mike Keith e Merrick Schnicariol. Capitolo 6. Sezione di distacco e la fusione. Questo libro è in realtà un secondo libro dedicato al JPA dagli autori. Questo nuovo libro ha molte nuove informazioni, allora precedente. Ho davvero recommed di leggere questo libro per quelli che saranno seriamente coinvolti con JPA. Mi dispiace per la pubblicazione anonimamente mia prima risposta.

Mi è stato sempre eccezioni lazyLoading sul mio soggetto, perché stavo cercando di accedere a una raccolta caricato pigro che era in sessione.

Quello che vorrei fare è stato in una richiesta separata, recuperare l'entità dalla sessione e quindi si cerca di accedere a una raccolta nel mio pagina JSP, che è stato problematico.

Per ovviare a questo, ho aggiornato la stessa entità in mio controller e lo passò al mio jsp, anche se immagino che quando ho ri-salvato in sessione che sarà accessibile anche se SessionScope e non gettare un LazyLoadingException, una modifica dell'esempio 2:

Di seguito ha funzionato per me:

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

Passando attraverso le risposte ci sono alcuni dettagli mancanti per quanto riguarda ` 'e la generazione id Cascade. Vedi domanda

Inoltre, vale la pena ricordare che si può avere separati Cascade annotazioni per la fusione e persistente. Cascade.MERGE e Cascade.PERSIST che saranno trattati secondo il metodo utilizzato

La specifica è tuo amico;)

Ho trovato questa spiegazione dalla Sospensione docs illuminante, perché contengono un caso d'uso:

L'utilizzo e la semantica di tipo merge() sembra essere fonte di confusione per i nuovi utenti.In primo luogo, finché non si tenta di utilizzare l'oggetto stato caricato in un gestore di entità in un altro nuovo ente gestore, si dovrebbe non è necessario utilizzare merge() a tutti.Alcune intere applicazioni non potrà mai utilizzare questo metodo.

Di solito merge() è utilizzato nel seguente scenario:

  • L'applicazione carica un oggetto in primo gestore di entità
  • l'oggetto è passato per il livello di presentazione
  • alcune modifiche apportate all'oggetto
  • l'oggetto è passato di nuovo giù per la business logic layer
  • l'applicazione persiste queste modifiche chiamando merge() in un secondo gestore di entità

Qui è l'esatto semantica di tipo merge():

  • se c'è un riuscito esempio con lo stesso identificatore attualmente associato con il contesto di persistenza, copiare lo stato dell'oggetto in questione su istanza gestita
  • se non c'è riuscito esempio, attualmente associato con il contesto di persistenza, provare a caricare dal database, o creare una nuova istanza gestita
  • gestito istanza viene restituito
  • il dato istanza non diventare associati con il contesto di persistenza, rimane distaccato e di solito è scartata

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

  

JPA è indiscutibilmente un grande semplificazione nel dominio di impresa   le applicazioni basate sulla piattaforma Java. Come sviluppatore che ha dovuto   far fronte con la complessità dei vecchi bean di entità in J2EE vedo il   inclusione di JPA Tra le specifiche Java EE come un grande salto   inoltrare. Tuttavia, mentre scavando più in profondità i dettagli JPA trovo   cose che non sono così facili. In questo articolo mi occupo di confronto   unione del EntityManager e persistono metodi quali la sovrapposizione   comportamento può causare confusione non solo per un principiante. Inoltre ho   proporre una generalizzazione che vede entrambi i metodi come casi particolari di un   altro metodo generale combinare.

entità persistenti

A differenza del metodo di unione del persistono metodo è molto semplice ed intuitivo. Lo scenario più comune di utilizzo del metodo di persistere può essere riassunta come segue:

"Una nuova istanza della classe entità è passato al metodo persistere. Dopo, questo metodo, l'entità è gestito e pianificato per l'inserimento nel database. Può accadere al momento o prima del commit della transazione o se il metodo flush è chiamato. Se l'entità fa riferimento a un'altra entità attraverso un rapporto segnato con la strategia a cascata PERSIST questa procedura viene applicata ad essa anche ".

 entrare descrizione dell'immagine qui

La specifica va più nei dettagli, tuttavia, ricordare loro non è cruciale come questi dettagli riguardano situazioni più o meno esotiche solo.

Unione di entità

In confronto a persistere, la descrizione del comportamento della fusione non è così semplice. Non v'è alcun scenario principale, come è nel caso di persistere, e un programmatore deve ricordare tutti gli scenari al fine di scrivere un codice corretto. Mi sembra che i progettisti JPA voluto avere qualche metodo la cui prima preoccupazione sarebbe la manipolazione entità indipendenti (come l'opposto del metodo che si occupa di nuove entità create principalmente persistere.) Compito principale del metodo di fusione è quello di trasferire lo stato da un entità non gestito (passato come argomento) per la sua controparte gestito all'interno del contesto di persistenza. Questo compito, tuttavia, suddivide ulteriormente in diversi scenari che peggiorano l'intelligibilità del comportamento del metodo generale.

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

 entrare descrizione dell'immagine qui

Così, quando dovrei usare persistere e quando unione?

persistere

  • Si desidera che il metodo crea sempre una nuova entità e non aggiorna mai un'entità. In caso contrario, il metodo genera un'eccezione in conseguenza della violazione primaria unicità chiave.
  • processi
  • Batch, la manipolazione entità in modo stateful (vedi modello Gateway).
  • Ottimizzazione delle prestazioni

merge

  • Si desidera il metodo sia inserti o aggiorna un'entità nel database.
  • Si desidera gestire le entità in modo stateless (trasferimento dati oggetti nei servizi)
  • Si vuole inserire un nuovo soggetto che può avere un riferimento ad un altro soggetto che può, ma non può essere ancora creata (rapporto deve essere contrassegnata MERGE). Ad esempio, l'inserimento di una nuova foto con un riferimento a un nuovo o un album preesistente.

Scenario X:

Tabella: Spitter (One), Tabella: sputi (Many) (sputi è proprietario del rapporto con un FK: spitter_id)

Questo risultato scenario nel risparmio: Lo Spitter ed entrambi sputi come se 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 sputi Ma non farà riferimento la stessa 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!!

Un'altra osservazione:

merge() si preoccupano solo un id generato automaticamente (testato su IDENTITY e SEQUENCE) quando un record con una tale ID esiste già nella tabella. In tal caso persist() cercherà di aggiornare il record. Se, tuttavia, un id è assente o non corrisponde alcun record esistenti, <=> sarà completamente ignorarlo e chiedere un db di allocare una nuova. Questo a volte è fonte di un sacco di bug. Non utilizzare <=> per forzare un ID per un nuovo record.

<=> d'altra parte non ti lascerò mai nemmeno passa un id ad esso. Fallirà immediatamente. Nel mio caso, è:

  

Causato da: org.hibernate.PersistentObjectException: separato da un'entità   passò a persistere

javadoc hibernate-APP ha un suggerimento:

  

Genera : javax.persistence.EntityExistsException - se l'entità   esiste già. (Se l'entità esiste già, il   EntityExistsException può essere generata quando l'operazione è persistono   invocata, o EntityExistsException o altro PersistenceException   può essere gettato in fase di filo o commettere.)

È possibile venuti qui per un consiglio su quando utilizzare persistere e quando utilizzare Unisci . Penso che dipende la situazione:. Quanto è probabile che è necessario creare un nuovo record e quanto è difficile per recuperare dati permanenti

Presumiamo è possibile utilizzare una chiave / identificatore naturale.

  • I dati ha bisogno di essere persistente, ma di tanto in tanto esiste un record e un aggiornamento è richiesto. In questo caso si potrebbe provare un persistono e se si getta un EntityExistsException, si guarda in su e combinare i dati:

    try {entityManager.persist (entità)}

    catch (Exception EntityExistsException) {/ * recuperare e unire * /}

  • persistito dati deve essere aggiornato, ma una volta ogni tanto non v'è alcuna traccia per i dati ancora. In questo caso si guarda in su, e fare un persistere se l'entità non è presente:

    entità = entityManager.find (chiave);

    if (entità == null) {entityManager.persist (entità); }

    else {/ * merge * /}

Se non si dispone di chiave naturale / identificativo, avrete un tempo più difficile da capire se l'entità esiste o non, o il modo di guardare in su.

Le unioni possono essere affrontati in due modi, troppo:

  1. Se le modifiche sono in genere piccole, li applica al soggetto gestito.
  2. Se le modifiche sono comuni, copiare l'ID dall'entità persistente, così come i dati inalterati. Quindi chiamare EntityManager :: merge () per sostituire il vecchio contenuto.

persistono (entità) deve essere usato con tutto nuove entità, per aggiungerli a DB (se entità esiste già nel DB ci saranno EntityExistsException tiro).

merge (entità) è consentito, per mettere di nuovo a un'entità contesto di persistenza se l'entità è stata staccata ed è stato cambiato.

Probabilmente persistono sta generando INSERT SQL e merge SQL UPDATE (ma non sono sicuro).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top