Qual è la differenza tra persist, save, merge e update? Quale si dovrebbe usare?

JPA e Hibernate forniscono metodi diversi per mantenere nuove e aggiornare le entità esistenti. Puoi scegliere tra i metodi di salvataggio e aggiornamento di JPA persist e merge e Hibernate.

Sembra che ci siano 2 coppie di 2 metodi che fanno lo stesso. È possibile utilizzare i metodi persist e save per memorizzare una nuova entità e i metodi merge e update per memorizzare le modifiche di un’entità distaccata nel database. Ecco perché molti sviluppatori si stanno chiedendo quale di questi metodi dovrebbero usare. Diamo un’occhiata più da vicino ai dettagli e alle piccole differenze di questi metodi.

Un ringraziamento speciale a Steve Ebersole (Lead Developer – Hibernate ORM) che ha fornito il suo feedback e grandi intuizioni su alcuni dei dettagli di implementazione nascosti di Hibernate!

Transizioni di stato dell’entità

Prima di entrare nei dettagli di questi 4 metodi, ho bisogno di darti una rapida introduzione agli stati del ciclo di vita dell’entità di JPA.

Se un’entità è collegata al contesto di persistenza corrente, viene gestito lo stato del ciclo di vita. Ciò significa che è mappato a un record di database. Il provider di persistenza genera le istruzioni SQL INSERT e UPDATE necessarie per propagare tutte le modifiche. Un’entità gestita viene anche memorizzata nella cache di 1 ° livello.

Quando si crea una nuova entità, si trova nello stato transitorio. Rimane in questo stato fino a quando non lo si collega al contesto di persistenza corrente. Ti mostrerò come puoi farlo con il metodo di salvataggio di JPA persist e Hibernate, nella sezione seguente. Finché un’entità si trova nello stato transitorio, non viene mappata a un record di database e non viene gestita da alcun contesto di persistenza.

Le entità nello stato del ciclo di vita separato non sono più gestite dal contesto di persistenza. Questo può essere il caso perché hai chiuso il contesto di persistenza o hai esplicitamente staccato l’entità dal contesto corrente. Entrerò in maggiori dettagli su come è possibile ricollegare queste entità con i metodi di aggiornamento di JPA Merge e Hibernate in una parte successiva di questo post.

E lo stato dell’ultimo ciclo di vita viene rimosso. Queste entità erano precedentemente nello stato gestito, prima di pianificarle per la rimozione. La rimozione delle entità è al di fuori dello scopo di questo post, quindi non entrerò in troppi dettagli a riguardo. È possibile pianificare un’entità per la rimozione chiamando il metodo remove sull’interfaccia EntityManager.

Persistenza di una nuova entità Utilizzando persist o save

Quando si crea un nuovo oggetto entità, si trova nello stato del ciclo di vita transitorio. Non mappa alcun record di database.

Author a = new Author();a.setFirstName("Thorben");a.setLastName("Janssen");

È necessario collegare l’entità a un contesto di persistenza in modo che venga gestita e mantenuta nel database. È possibile utilizzare il metodo di salvataggio di JPA persist o Hibernate per farlo. Entrambi i metodi sembrano fare lo stesso, ma ci sono alcune differenze.

Specifica vs. API proprietaria

La differenza più ovvia è che la specifica JPA definisce il metodo persist. Puoi usarlo con tutte le implementazioni JPA. Il metodo di salvataggio, d’altra parte, è specifico per Hibernate. Pertanto, non è disponibile in altre implementazioni JPA.

Ma questo è rilevante solo se si desidera essere in grado di sostituire Hibernate con un’altra implementazione JPA, come Eclipse Link o OpenJPA.

Tipi di ritorno ed esecuzione di istruzioni SQL

Un’altra ovvia differenza tra questi 2 metodi è il loro tipo di ritorno. Il metodo persist di JPA restituisce void e il metodo save di Hibernate restituisce la chiave primaria dell’entità.

Potrebbe sembrare un’enorme differenza, specialmente quando si dà un’occhiata più da vicino a Javadoc di Hibernate e alle specifiche JPA:

  • Il Javadoc del metodo di salvataggio di Hibernate afferma che genera prima il valore della chiave primaria:

    Persistere l’istanza transitoria data, assegnando prima un identificatore generato.
    Sessione Javadoc.salva (entità)

  • Non trovi alcuna informazione a riguardo nella specifica JPA. Non definisce quando deve essere assegnato il valore della chiave primaria. Quindi, il provider di persistenza può farlo in qualsiasi momento tra la chiamata del metodo persist e il flush del contesto di persistenza.

Nella maggior parte dei casi, non fa alcuna differenza se si chiama il metodo save o persist. Hibernate utilizza il nome della classe di entità e il valore della chiave primaria per memorizzare l’entità nella cache di primo livello. Pertanto, ha bisogno di un valore della chiave primaria quando esegue il metodo persist.

In quasi tutte le situazioni, Hibernate genera immediatamente il valore della chiave primaria e attiva un’istruzione SQL, se necessario, quando si chiama il metodo persist o save.

Ma non è così se si utilizza la strategia di IDENTITÀ e si tenta di mantenere un’entità senza una transazione attiva o con FlushMode.MANUALE. Se si chiama il metodo persist in una di queste situazioni, Hibernate ritarda l’esecuzione dell’istruzione SQL INSERT e crea un valore della chiave primaria temporanea. Ma se si chiama il metodo save, Hibernate esegue immediatamente l’istruzione SQL INSERT e recupera il valore della chiave primaria dal database.

È quindi possibile recuperarlo come valore restituito del metodo save.

Author a = new Author();a.setFirstName("Thorben");a.setLastName("Janssen");Long id = (Long) em.unwrap(Session.class).save(a);

Oppure è possibile chiamare il metodo getter dell’attributo chiave primaria dell’entità gestita se si utilizza il metodo persist di JPA.

Author a = new Author();a.setFirstName("Torben");a.setLastName("Janssen");em.persist(a);Long id = a.getId();

Hibernate esegue le stesse istruzioni SQL quando si chiama il persist o il metodo save. Quale e quando lo fa dipende dalla tua strategia di generazione della chiave primaria:

Non generato

Se si imposta il valore della chiave primaria a livello di codice, ad esempio su un identificatore naturale, Hibernate esegue solo un’istruzione SQL INSERT quando svuota il contesto di persistenza.

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 (?, ?, ?, ?)
YouTube video

Generato con la strategia di IDENTITÀ

Se si utilizza la strategia di IDENTITÀ per generare il valore della chiave primaria, di Sospensione deve eseguire l’istruzione INSERT quando si chiama il salvataggio o il persistere di metodo per recuperare il valore della chiave primaria del database.

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
Video di YouTube

Generato con la strategia di SEQUENZA

E se si utilizza la SEQUENZA, Hibernate esegue un’istruzione SQL SELECT per recuperare il valore successivo dalla sequenza del database. Hibernate ritarda quindi l’istruzione INSERT finché non svuota il contesto di persistenza. In questo esempio, il flush si verifica quando la transazione viene eseguita.

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 (?, ?, ?, ?)

Generato con la strategia della TABELLA

Non si dovrebbe usare la strategia della TABELLA perché richiede blocchi a livello di riga sulla tabella delle chiavi primarie e non scala bene. Se si utilizza comunque questa strategia, Hibernate esegue un’istruzione SQL SELECT per recuperare il valore della chiave primaria successiva dal database e scrive il nuovo valore nella tabella del database. Ritarda l’esecuzione dell’istruzione SQL INSERT per la nuova entità finché non svuota il contesto di persistenza.

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 (?, ?, ?, ?)

Quale scegliere?

Ci si potrebbe aspettare che il metodo save e persist si comportino in modo diverso perché ci sono alcune differenze tra le specifiche JPA e Javadoc dei metodi proprietari di Hibernate.

Ma quasi tutte queste differenze scompaiono quando si dà un’occhiata all’implementazione interna. Gli unici che rimangono sono 2 casi d’angolo in cui Hibernate potrebbe ritardare il recupero della chiave primaria, il tipo di ritorno del metodo e il supporto da altre implementazioni JPA.

Per la maggior parte delle applicazioni, non fa alcuna differenza se si ottiene il valore della chiave primaria generato come tipo di ritorno del metodo di salvataggio di Hibernate o dal metodo getter dell’attributo della chiave primaria. Finché non si utilizza un contesto di persistenza esteso e si eseguono tutte le operazioni di database con una transazione attiva, si consiglia di utilizzare il metodo persist di JPA.

Aggiornamento di un’entità distaccata

Quando si chiude il contesto di persistenza corrente o si rimuove esplicitamente un’entità da esso chiamando i metodi clear o detach sull’interfaccia EntityManager, l’entità viene distaccata. Ciò significa che non è più memorizzato nella cache di 1 ° livello e che Hibernate non replicherà nessuna delle modifiche applicate al database.

È possibile utilizzare l’aggiornamento di Hibernate o il metodo di unione di JPA per associare un’entità distaccata a un contesto di persistenza. Dopo averlo fatto, Hibernate aggiornerà il database in base ai valori degli attributi dell’entità.

L’effetto del metodo update e merge sembra essere lo stesso, ma come vedrai nelle sezioni seguenti, c’è una differenza importante.

Il metodo di unione di JPA

Il metodo di unione di JPA copia lo stato di un’entità distaccata in un’istanza gestita della stessa entità. Hibernate, pertanto, esegue un’istruzione SQL SELECT per recuperare un’entità gestita dal database. Se il contesto di persistenza conteneva già un’istanza gestita dell’entità, Hibernate utilizza invece quella esistente. Quindi copia tutti i valori degli attributi nell’entità gestita e lo restituisce al chiamante.

Author managedAuthor = em.merge(a);

Dopo aver attivato la registrazione delle istruzioni SQL, è possibile visualizzare le istruzioni SELECT e UPDATE eseguite nell’output del log.

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=?

Quando Hibernate svuota il contesto di persistenza per la prossima volta, il suo meccanismo di controllo sporco controlla tutte le entità gestite. Se rileva che l’operazione di unione ha modificato un valore di attributo di entità, attiva l’istruzione SQL UPDATE richiesta.

C’è un dettaglio importante che devi sapere quando usi il metodo di unione di JPA. Hibernate copia i valori degli attributi dell’entità distaccata nell’entità gestita. Questo sovrascrive tutte le modifiche eseguite su questa entità all’interno della sessione corrente.

Il metodo di aggiornamento di Hibernate

Il metodo di aggiornamento di Hibernate non attiva un’istruzione SQL SELECT. Si collega semplicemente l’entità al contesto di persistenza corrente. In contrasto con il metodo di unione di JPA, non è possibile perdere alcuna modifica chiamando il metodo di aggiornamento. Se il contesto di persistenza contiene già un’istanza gestita dell’entità che si desidera aggiornare, genera un’eccezione.

em.unwrap(Session.class).update(a);

Quando Hibernate esegue il flush successivo, non esegue alcun controllo sporco. Ciò non è possibile perché Hibernate non ha letto l’ultima versione dell’entità dal database. Esegue solo un’istruzione SQL UPDATE per l’entità riattaccata.

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=?

Il controllo sporco mancante causa un’istruzione SQL UPDATE non necessaria quando l’entità e il record del database corrispondente contengono gli stessi valori. Questo potrebbe essere un problema se il tuo DBA ha registrato un trigger di aggiornamento per la tabella del database. In queste situazioni, è possibile annotare la propria entità con @SelectBeforeUpdate.

@Entity@SelectBeforeUpdatepublic class Author { ... }

Che indica a Hibernate di selezionare l’entità ed eseguire un controllo sporco prima che generi l’istruzione SQL UPDATE. Come puoi vedere nell’output del log, il comportamento del metodo di aggiornamento è ora simile al metodo di unione di 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=?

Ma c’è una differenza significativa tra i 2 metodi. Quando si chiama il metodo di aggiornamento, Hibernate selezionerà solo l’entità fornita come parametro del metodo. Ma quando si chiama il metodo di unione di JPA, Hibernate selezionerà anche tutte le associazioni con CascadeType.UNIRE. Dovresti, quindi, preferire il metodo di unione di JPA se riattacchi un enorme grafico di entità.

Quale scegliere?

Non esiste una risposta generale a queste domande. Come hai visto, entrambi i metodi hanno i loro vantaggi e svantaggi. Devi decidere per il tuo caso d’uso specifico se Hibernate deve selezionare l’entità prima che attivi l’istruzione SQL UPDATE. E se questo è il caso, devi anche considerare la profondità del tuo grafico di entità e le implicazioni sulle prestazioni del comportamento di recupero fornito.

Aggiornamento di un’entità gestita

JPA e Hibernate rendono molto semplice l’aggiornamento di un’entità gestita. Se la tua entità si trova nello stato del ciclo di vita gestito, ad esempio perché l’hai recuperata con una query JPQL o il metodo find di EntityManager, devi solo modificare i valori degli attributi dell’entità.

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();

Quando Hibernate decide di svuotare il contesto di persistenza, il meccanismo di controllo sporco rileverà la modifica ed eseguirà l’istruzione SQL UPDATE richiesta.

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=?

Non è necessario e non è necessario chiamare il metodo di salvataggio di Hibernate dopo aver aggiornato un’entità. Ciò attiva un evento SaveOrUpdate aggiuntivo senza fornire alcun vantaggio. Quando Hibernate decide di svuotare il contesto di persistenza, eseguirà comunque il controllo sporco per rilevare tutte le modifiche prima di eseguire le istruzioni SQL UPDATE richieste.

You might also like

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.