Domanda

Hibernate ha una manciata di metodi che, in un modo o nell'altro, prendono l'oggetto e lo inseriscono nel database. Quali sono le differenze tra loro, quando usare quale e perché non esiste un solo metodo intelligente che sappia quando usare cosa?

I metodi che ho identificato finora sono:

  • save ()
  • update ()
  • saveOrUpdate ()
  • saveOrUpdateCopy ()
  • merge ()
  • persistono ()
È stato utile?

Soluzione

Ecco la mia comprensione dei metodi. Principalmente si basano sulla API sebbene io utilizzarli tutti in pratica.

saveOrUpdate Le chiamate vengono salvate o aggiornate in base ad alcuni controlli. Per esempio. se non esiste un identificatore, viene chiamato il salvataggio. Altrimenti viene chiamato l'aggiornamento.

Salva Persiste un'entità. Assegnerà un identificatore se non esiste. Se lo fa, sta essenzialmente facendo un aggiornamento. Restituisce l'ID generato dell'entità.

Aggiorna Tenta di persistere nell'entità utilizzando un identificatore esistente. Se non esiste un identificatore, credo che venga generata un'eccezione.

saveOrUpdateCopy Questo è obsoleto e non dovrebbe più essere utilizzato. Invece c'è ...

merge Ora è qui che la mia conoscenza inizia a vacillare. La cosa importante qui è la differenza tra entità transitorie, distaccate e persistenti. Per maggiori informazioni sugli stati degli oggetti, dai un'occhiata qui . Con save & amp; aggiornamento, hai a che fare con oggetti persistenti. Sono collegati a una sessione, quindi Hibernate sa cosa è cambiato. Ma quando hai un oggetto transitorio, non c'è nessuna sessione coinvolta. In questi casi è necessario utilizzare l'unione per gli aggiornamenti e persistere per il salvataggio.

persistere Come accennato in precedenza, questo viene utilizzato su oggetti transitori. Non restituisce l'ID generato.

Altri suggerimenti

╔══════════════╦═══════════════════════════════╦════════════════════════════════╗
║    METHOD    ║            TRANSIENT          ║            DETACHED            ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║              ║       sets id if doesn't      ║   sets new id even if object   ║
║    save()    ║     exist, persists to db,    ║    already has it, persists    ║
║              ║    returns attached object    ║ to DB, returns attached object ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║              ║       sets id on object       ║             throws             ║
║   persist()  ║     persists object to DB     ║       PersistenceException     ║
║              ║                               ║                                ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║              ║                               ║                                ║
║   update()   ║           Exception           ║     persists and reattaches    ║
║              ║                               ║                                ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║              ║  copy the state of object in  ║    copy the state of obj in    ║
║    merge()   ║     DB, doesn't attach it,    ║      DB, doesn't attach it,    ║
║              ║    returns attached object    ║     returns attached object    ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║              ║                               ║                                ║
║saveOrUpdate()║           as save()           ║            as update()         ║
║              ║                               ║                                ║
╚══════════════╩═══════════════════════════════╩════════════════════════════════╝
  • Consulta il Hibernate Forum per una spiegazione delle sottili differenze tra persistere e salvare. Sembra che la differenza sia il momento in cui l'istruzione INSERT viene eseguita alla fine. Poiché salva restituisce l'identificatore, l'istruzione INSERT deve essere eseguita immediatamente indipendentemente dallo stato della transazione (che in genere è una cosa negativa). Persistere non eseguirà dichiarazioni al di fuori della transazione attualmente in esecuzione solo per assegnare l'identificatore. Save / Persist funzionano entrambi su istanze transitorie , vale a dire istanze a cui non è stato ancora assegnato un identificatore e come tali non vengono salvate nel DB.

  • Aggiorna e Unisci funzionano entrambi su istanze distaccate , ovvero istanze che hanno una voce corrispondente nel DB ma che sono attualmente non collegato a (o gestito da) una sessione. La differenza tra loro è ciò che accade all'istanza che viene passata alla funzione. aggiorna tenta di ricollegare l'istanza, ciò significa che potrebbe non esserci altra istanza dell'entità persistente collegata alla Sessione in questo momento, altrimenti viene generata un'eccezione. Unisci , tuttavia, copia solo tutti i valori in un'istanza persistente nella Sessione (che verrà caricata se non è attualmente caricata). L'oggetto di input non è cambiato. Quindi unisci è più generale di aggiorna , ma può utilizzare più risorse.

Questo link spiega in modo corretto:

http://www.stevideter.com/2008/12// 07 / saveOrUpdate-versus-merge-in-hibernate /

Tutti abbiamo quei problemi che incontriamo abbastanza raramente che quando li vediamo di nuovo, sappiamo che abbiamo risolto questo problema, ma non possiamo ricordare come.

NonUniqueObjectException generata quando si utilizza Session.saveOrUpdate () in Hibernate è una delle mie. Aggiungerò nuove funzionalità a un'applicazione complessa. Tutti i test delle mie unità funzionano bene. Quindi, testando l'interfaccia utente, cercando di salvare un oggetto, inizio a ottenere un'eccezione con il messaggio & # 8220; un oggetto diverso con lo stesso valore identificativo era già associato alla sessione. & # 8221; Ecco un esempio di codice da Java Persistence with Hibernate.

            Session session = sessionFactory1.openSession();
            Transaction tx = session.beginTransaction();
            Item item = (Item) session.get(Item.class, new Long(1234));
            tx.commit();
            session.close(); // end of first session, item is detached

            item.getId(); // The database identity is "1234"
            item.setDescription("my new description");
            Session session2 = sessionFactory.openSession();
            Transaction tx2 = session2.beginTransaction();
            Item item2 = (Item) session2.get(Item.class, new Long(1234));
            session2.update(item); // Throws NonUniqueObjectException
            tx2.commit();
            session2.close();

Per comprendere la causa di questa eccezione, è importante comprendere gli oggetti distaccati e cosa succede quando si chiama saveOrUpdate () (o semplicemente update ()) su un oggetto distaccato.

Quando chiudiamo una singola sessione di ibernazione, gli oggetti persistenti con cui stiamo lavorando vengono staccati. Ciò significa che i dati sono ancora nella memoria dell'applicazione, ma Hibernate non è più responsabile del rilevamento delle modifiche agli oggetti.

Se poi modifichiamo il nostro oggetto distaccato e vogliamo aggiornarlo, dobbiamo ricollegare l'oggetto. Durante quel processo di riattacco, Hibernate verificherà se ci sono altre copie dello stesso oggetto. Se ne trova qualcuno, deve dirci che non sa cosa sia reale & reale # 8221; la copia è più. Forse sono state apportate altre modifiche a quelle altre copie che prevediamo di essere salvate, ma Hibernate non le conosce, perché al momento non le stava gestendo.

Invece di salvare dati potenzialmente errati, Hibernate ci informa del problema tramite NonUniqueObjectException.

Quindi cosa dobbiamo fare? In Hibernate 3, abbiamo merge () (in Hibernate 2, usa saveOrUpdateCopy ()). Questo metodo costringerà Hibernate a copiare eventuali modifiche da altre istanze distaccate sull'istanza che si desidera salvare e quindi unisce tutte le modifiche in memoria prima del salvataggio.

        Session session = sessionFactory1.openSession();
        Transaction tx = session.beginTransaction();
        Item item = (Item) session.get(Item.class, new Long(1234));
        tx.commit();
        session.close(); // end of first session, item is detached

        item.getId(); // The database identity is "1234"
        item.setDescription("my new description");
        Session session2 = sessionFactory.openSession();
        Transaction tx2 = session2.beginTransaction();
        Item item2 = (Item) session2.get(Item.class, new Long(1234));
        Item item3 = session2.merge(item); // Success!
        tx2.commit();
        session2.close();

È importante notare che l'unione restituisce un riferimento alla versione appena aggiornata dell'istanza. Non è ricollegare l'elemento alla Sessione. Se provi ad esempio l'uguaglianza (item == item3), troverai che in questo caso restituisce false. Probabilmente vorrai lavorare con item3 da questo punto in avanti.

È anche importante notare che l'API Java Persistence (JPA) non ha un concetto di oggetti staccati e ricollegati e utilizza EntityManager.persist () e EntityManager.merge ().

Ho riscontrato in generale che quando si utilizza Hibernate, saveOrUpdate () è in genere sufficiente per le mie esigenze. Di solito ho solo bisogno di usare l'unione quando ho oggetti che possono avere riferimenti a oggetti dello stesso tipo. Più recentemente, la causa dell'eccezione era nel codice che confermava che il riferimento non era ricorsivo. Stavo caricando lo stesso oggetto nella mia sessione come parte della convalida, causando l'errore.

Dove hai riscontrato questo problema? L'unione ha funzionato per te o hai bisogno di un'altra soluzione? Preferisci usare sempre l'unione o preferisci usarlo solo se necessario per casi specifici

In realtà la differenza tra i metodi hibernate save () e persist () dipende dalla classe del generatore che stiamo usando.

Se viene assegnata la nostra classe di generatori, non vi è alcuna differenza tra i metodi save () e persist (). Perché il generatore "assegnato" significa che come programmatore dobbiamo fornire il valore della chiave primaria per salvare nel database giusto [Spero che tu conosca questo concetto di generatori] Nel caso di una classe di generatore diversa da quella assegnata, supponiamo che se il nome della nostra classe di generatore è Incremento significa ibernazione, esso stesso assegnerà il valore dell'ID chiave primaria nel diritto del database [diverso dal generatore assegnato, ibernazione utilizzato solo per occuparsi del valore dell'ID chiave primaria ricordare ], quindi in questo caso se chiamiamo il metodo save () o persist () , inserirà normalmente il record nel database Ma ascolta la cosa, il metodo save () può restituire quel valore ID chiave primaria che viene generato da ibernazione e possiamo vederlo da

long s = session.save(k);

In questo stesso caso, persist () non restituirà mai alcun valore al client.

Ho trovato un buon esempio che mostra le differenze tra tutti i metodi di salvataggio in letargo:

http: // www. journaldev.com/3481/hibernate-session-merge-vs-update-save-saveorupdate-persist-example

In breve, secondo il link sopra:

save ()

  • Possiamo invocare questo metodo al di fuori di una transazione. Se lo utilizziamo senza transazione e abbiamo un collegamento in cascata tra entità, viene salvata solo l'entità primaria a meno che non scarichiamo la sessione.
  • Quindi, se ci sono altri oggetti mappati dall'oggetto primario, vengono salvati al momento del commit della transazione o quando scarichiamo la sessione.

persistono ()

  • È simile all'utilizzo di save () nelle transazioni, quindi è sicuro e si prende cura di tutti gli oggetti a cascata.

saveOrUpdate ()

  • Può essere usato con o senza la transazione, e proprio come save (), se usato senza la transazione, le entità mappate non verranno salvate in un momento, quindi cancelliamo la sessione.

  • Risultati in query di inserimento o aggiornamento basate sui dati forniti. Se i dati sono presenti nel database, viene eseguita la query di aggiornamento.

update ()

  • L'aggiornamento di ibernazione deve essere utilizzato laddove sappiamo che stiamo aggiornando solo le informazioni sull'entità. Questa operazione aggiunge l'oggetto entità al contesto persistente e ulteriori modifiche vengono tracciate e salvate quando viene eseguita la transazione.
  • Quindi, anche dopo aver chiamato update, se impostiamo dei valori nell'entità, questi verranno aggiornati al momento del commit della transazione.

merge ()

  • L'unione in ibernazione può essere utilizzata per aggiornare i valori esistenti, tuttavia questo metodo crea una copia dall'oggetto entità passato e lo restituisce. L'oggetto restituito fa parte del contesto persistente e viene tracciato per eventuali modifiche, l'oggetto passato non viene tracciato. Questa è la principale differenza con merge () rispetto a tutti gli altri metodi.

Anche per esempi pratici di tutti questi, si prega di fare riferimento al link che ho menzionato sopra, mostra esempi per tutti questi diversi metodi.

Tenere presente che se si chiama un aggiornamento su un oggetto distaccato, nel database verrà sempre eseguito un aggiornamento, indipendentemente dal fatto che sia stato modificato l'oggetto. Se non è quello che vuoi, dovresti usare Session.lock () con LockMode.None.

È necessario chiamare l'aggiornamento solo se l'oggetto è stato modificato al di fuori dell'ambito della sessione corrente (in modalità disconnessa).

As I explained in this article, you should prefer the JPA methods most of the time, and the update for batch processing tasks.

A JPA or Hibernate entity can be in one of the following four states:

  • Transient (New)
  • Managed (Persistent)
  • Detached
  • Removed (Deleted)

The transition from one state to the other is done via the EntityManager or Session methods.

For instance, the JPA EntityManager provides the following entity state transition methods.

enter image description here

The Hibernate Session implements all the JPA EntityManager methods and provides some additional entity state transition methods like save, saveOrUpdate and update.

enter image description here

Persist

To change the state of an entity from Transient (New) to Managed (Persisted), we can use the persist method offered by the JPA EntityManager which is also inherited by the Hibernate Session.

The persist method triggers a PersistEvent which is handled by the DefaultPersistEventListener Hibernate event listener.

Therefore, when executing the following test case:

doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    LOGGER.info(
        "Persisting the Book entity with the id: {}", 
        book.getId()
    );
});

Hibernate generates the following SQL statements:

CALL NEXT VALUE FOR hibernate_sequence

-- Persisting the Book entity with the id: 1

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

Notice that the id is assigned prior to attaching the Book entity to the current Persistence Context. This is needed because the managed entities are stored in a Map structure where the key is formed by the entity type and its identifier and the value is the entity reference. This is the reason why the JPA EntityManager and the Hibernate Session are known as the First-Level Cache.

When calling persist, the entity is only attached to the currently running Persistence Context, and the INSERT can be postponed until the flush is called.

The only exception is the IDENTITY generator which triggers the INSERT right away since that's the only way it can get the entity identifier. For this reason, Hibernate cannot batch inserts for entities using the IDENTITY generator. For more details about this topic, check out this article.

Save

The Hibernate-specific save method predates JPA and it's been available since the beginning of the Hibernate project.

The save method triggers a SaveOrUpdateEvent which is handled by the DefaultSaveOrUpdateEventListener Hibernate event listener. Therefore, the save method is equivalent to the update and saveOrUpdate methods.

To see how the save method works, consider the following test case:

doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    Session session = entityManager.unwrap(Session.class);

    Long id = (Long) session.save(book);

    LOGGER.info(
        "Saving the Book entity with the id: {}", 
        id
    );
});

When running the test case above, Hibernate generates the following SQL statements:

CALL NEXT VALUE FOR hibernate_sequence

-- Saving the Book entity with the id: 1

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

As you can see, the outcome is identical to the persist method call. However, unlike persist, the save method returns the entity identifier.

For more details, check out this article.

Update

The Hibernate-specific update method is meant to bypass the dirty checking mechanism and force an entity update at the flush time.

The update method triggers a SaveOrUpdateEvent which is handled by the DefaultSaveOrUpdateEventListener Hibernate event listener. Therefore, the update method is equivalent to the save and saveOrUpdate methods.

To see how the update method works consider the following example which persists a Book entity in one transaction, then it modifies it while the entity is in the detached state, and it forces the SQL UPDATE using the update method call.

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

LOGGER.info("Modifying the Book entity");

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

doInJPA(entityManager -> {
    Session session = entityManager.unwrap(Session.class);

    session.update(_book);

    LOGGER.info("Updating the Book entity");
});

When executing the test case above, Hibernate generates the following SQL statements:

CALL NEXT VALUE FOR hibernate_sequence

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

-- Modifying the Book entity
-- Updating the Book entity

UPDATE 
    book 
SET 
    author = 'Vlad Mihalcea', 
    isbn = '978-9730228236', 
    title = 'High-Performance Java Persistence, 2nd edition'
WHERE 
    id = 1

Notice that the UPDATE is executed during the Persistence Context flush, right before commit, and that's why the Updating the Book entity message is logged first.

Using @SelectBeforeUpdate to avoid unnecessary updates

Now, the UPDATE is always going to be executed even if the entity was not changed while in the detached state. To prevent this, you can use the @SelectBeforeUpdate Hibernate annotation which will trigger a SELECT statement that fetched loaded state which is then used by the dirty checking mechanism.

So, if we annotate the Book entity with the @SelectBeforeUpdate annotation:

@Entity(name = "Book")
@Table(name = "book")
@SelectBeforeUpdate
public class Book {

    //Code omitted for brevity
}

And execute the following test case:

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

doInJPA(entityManager -> {
    Session session = entityManager.unwrap(Session.class);

    session.update(_book);
});

Hibernate executes the following SQL statements:

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

SELECT 
    b.id,
    b.author AS author2_0_,
    b.isbn AS isbn3_0_,
    b.title AS title4_0_
FROM 
    book b
WHERE 
    b.id = 1

Notice that, this time, there is no UPDATE executed since the Hibernate dirty checking mechanism has detected that the entity was not modified.

SaveOrUpdate

The Hibernate-specific saveOrUpdate method is just an alias for save and update.

The saveOrUpdate method triggers a SaveOrUpdateEvent which is handled by the DefaultSaveOrUpdateEventListener Hibernate event listener. Therefore, the update method is equivalent to the save and saveOrUpdate methods.

Now, you can use saveOrUpdate when you want to persist an entity or to force an UPDATE as illustrated by the following example.

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    Session session = entityManager.unwrap(Session.class);
    session.saveOrUpdate(book);

    return book;
});

_book.setTitle("High-Performance Java Persistence, 2nd edition");

doInJPA(entityManager -> {
    Session session = entityManager.unwrap(Session.class);
    session.saveOrUpdate(_book);
});

Beware of the NonUniqueObjectException

One problem that can occur with save, update, and saveOrUpdate is if the Persistence Context already contains an entity reference with the same id and of the same type as in the following example:

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    Session session = entityManager.unwrap(Session.class);
    session.saveOrUpdate(book);

    return book;
});

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

try {
    doInJPA(entityManager -> {
        Book book = entityManager.find(
            Book.class, 
            _book.getId()
        );

        Session session = entityManager.unwrap(Session.class);
        session.saveOrUpdate(_book);
    });
} catch (NonUniqueObjectException e) {
    LOGGER.error(
        "The Persistence Context cannot hold " +
        "two representations of the same entity", 
        e
    );
}

Now, when executing the test case above, Hibernate is going to throw a NonUniqueObjectException because the second EntityManager already contains a Book entity with the same identifier as the one we pass to update, and the Persistence Context cannot hold two representations of the same entity.

org.hibernate.NonUniqueObjectException: 
    A different object with the same identifier value was already associated with the session : [com.vladmihalcea.book.hpjp.hibernate.pc.Book#1]
    at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:651)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:284)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:227)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:92)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)
    at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:682)
    at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:674)

Merge

To avoid the NonUniqueObjectException, you need to use the merge method offered by the JPA EntityManager and inherited by the Hibernate Session as well.

As explained in this article, the merge fetches a new entity snapshot from the database if there is no entity reference found in the Persistence Context, and it copies the state of the detached entity passed to the merge method.

The merge method triggers a MergeEvent which is handled by the DefaultMergeEventListener Hibernate event listener.

To see how the merge method works consider the following example which persists a Book entity in one transaction, then it modifies it while the entity is in the detached state, and pass the detached entity to merge in a subsequence Persistence Context.

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

LOGGER.info("Modifying the Book entity");

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

doInJPA(entityManager -> {
    Book book = entityManager.merge(_book);

    LOGGER.info("Merging the Book entity");

    assertFalse(book == _book);
});

When running the test case above, Hibernate executed the following SQL statements:

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

-- Modifying the Book entity

SELECT 
    b.id,
    b.author AS author2_0_,
    b.isbn AS isbn3_0_,
    b.title AS title4_0_
FROM 
    book b
WHERE 
    b.id = 1

-- Merging the Book entity

UPDATE 
    book 
SET 
    author = 'Vlad Mihalcea', 
    isbn = '978-9730228236', 
    title = 'High-Performance Java Persistence, 2nd edition'
WHERE 
    id = 1

Notice that the entity reference returned by merge is different than the detached one we passed to the merge method.

Now, although you should prefer using JPA merge when copying the detached entity state, the extra SELECT can be problematic when executing a batch processing task.

For this reason, you should prefer using update when you are sure that there is no entity reference already attached to the currently running Persistence Context and that the detached entity has been modified.

For more details about this topic, check out this article.

Conclusion

To persist an entity, you should use the JPA persist method. To copy the detached entity state, merge should be preferred. The update method is useful for batch processing tasks only. The save and saveOrUpdate are just aliases to update and you should not probably use them at all.

Some developers call save even when the entity is already managed, but this is a mistake and triggers a redundant event since, for managed entities, the UPDATE is automatically handled at the Persistence context flush time.

For more details, check out this article.

None of the following answers are right. All these methods just seem to be alike, but in practice do absolutely different things. It is hard to give short comments. Better to give a link to full documentation about these methods: http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/objectstate.html

None of the answers above are complete. Although Leo Theobald answer looks nearest answer.

The basic point is how hibernate is dealing with states of entities and how it handles them when there is a state change. Everything must be seen with respect to flushes and commits as well, which everyone seems to have ignored completely.

NEVER USE THE SAVE METHOD of HIBERNATE. FORGET THAT IT EVEN EXISTS IN HIBERNATE!

Persist

As everyone explained, Persist basically transitions an entity from "Transient" state to "Managed" State. At this point, a slush or a commit can create an insert statement. But the entity will still remains in "Managed" state. That doesn't change with flush.

At this point, if you "Persist" again there will be no change. And there wont be any more saves if we try to persist a persisted entity.

The fun begins when we try to evict the entity.

An evict is a special function of Hibernate which will transition the entity from "Managed" to "Detached". We cannot call a persist on a detached entity. If we do that, then Hibernate raises an exception and entire transaction gets rolled back on commit.

Merge vs Update

These are 2 interesting functions doing different stuff when dealt in different ways. Both of them are trying to transition the entity from "Detached" state to "Managed" state. But doing it differently.

Understand a fact that Detached means kind of an "offline" state. and managed means "Online" state.

Observe the code below:

Session ses1 = sessionFactory.openSession();

    Transaction tx1 = ses1.beginTransaction();

    HibEntity entity = getHibEntity();

    ses1.persist(entity);
    ses1.evict(entity);

    ses1.merge(entity);

    ses1.delete(entity);

    tx1.commit();

When you do this? What do you think will happen? If you said this will raise exception, then you are correct. This will raise exception because, merge has worked on entity object, which is detached state. But it doesn't alter the state of object.

Behind the scene, merge will raise a select query and basically returns a copy of entity which is in attached state. Observe the code below:

Session ses1 = sessionFactory.openSession();

    Transaction tx1 = ses1.beginTransaction();
    HibEntity entity = getHibEntity();

    ses1.persist(entity);
    ses1.evict(entity);

    HibEntity copied = (HibEntity)ses1.merge(entity);
    ses1.delete(copied);

    tx1.commit();

The above sample works because merge has brought a new entity into the context which is in persisted state.

When applied with Update the same works fine because update doesn't actually bring a copy of entity like merge.

Session ses1 = sessionFactory.openSession();

    Transaction tx1 = ses1.beginTransaction();

    HibEntity entity = getHibEntity();

    ses1.persist(entity);
    ses1.evict(entity);

    ses1.update(entity);

    ses1.delete(entity);

    tx1.commit();

At the same time in debug trace we can see that Update hasn't raised SQL query of select like merge.

delete

In the above example I used delete without talking about delete. Delete will basically transition the entity from managed state to "removed" state. And when flushed or commited will issue a delete command to store.

However it is possible to bring the entity back to "managed" state from "removed" state using the persist method.

Hope the above explanation clarified any doubts.

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