Pregunta

Hibernate tiene varios métodos que, de una forma u otra, toman su objeto y lo colocan en la base de datos. ¿Cuáles son las diferencias entre ellos, cuándo usar cuál y por qué no hay un solo método inteligente que sepa cuándo usar qué?

Los métodos que he identificado hasta ahora son:

  • save()
  • update()
  • saveOrUpdate()
  • saveOrUpdateCopy()
  • merge()
  • persist()
¿Fue útil?

Solución

Aquí tengo mi comprensión de los métodos. Principalmente, estos se basan en la API , aunque como lo hago No utilice todos estos en la práctica.

saveOrUpdate Las llamadas guardan o actualizan dependiendo de algunas verificaciones. P.ej. Si no existe un identificador, se llama guardar. De lo contrario se llama actualización.

guardar Persiste una entidad. Asignará un identificador si no existe uno. Si uno lo hace, esencialmente está haciendo una actualización. Devuelve el ID generado de la entidad.

actualización Intenta persistir la entidad utilizando un identificador existente. Si no existe un identificador, creo que se produce una excepción.

saveOrUpdateCopy Esto está en desuso y ya no debe usarse. En cambio hay ...

fusionar Ahora aquí es donde mi conocimiento comienza a flaquear. Lo importante aquí es la diferencia entre entidades transitorias, separadas y persistentes. Para obtener más información sobre los estados de los objetos, eche un vistazo aquí . Con guardar & amp; Actualización, estás tratando con objetos persistentes. Están vinculados a una sesión para que Hibernate sepa lo que ha cambiado. Pero cuando tienes un objeto transitorio, no hay sesión involucrada. En estos casos, debe usar la combinación para actualizaciones y persistir para guardar.

persistir Como se mencionó anteriormente, esto se usa en objetos transitorios. No devuelve la ID generada.

Otros consejos

╔══════════════╦═══════════════════════════════╦════════════════════════════════╗
║    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()         ║
║              ║                               ║                                ║
╚══════════════╩═══════════════════════════════╩════════════════════════════════╝
  • Consulte Hibernate Forum para Una explicación de las diferencias sutiles entre persistir y guardar. Parece que la diferencia es la hora en que se ejecuta la instrucción INSERT. Como guardar devuelve el identificador, la instrucción INSERT debe ejecutarse instantáneamente independientemente del estado de la transacción (lo que generalmente es algo malo). Persistir no ejecutará ninguna declaración fuera de la transacción actualmente en ejecución solo para asignar el identificador. Guardar / Persistir funciona tanto en instancias transitorias , es decir, instancias que todavía no tienen un identificador asignado y, como tal, no se guardan en la base de datos.

  • Actualizar y Combinar ambos funcionan en instancias desconectadas , es decir, instancias que tienen una entrada correspondiente en el DB pero que actualmente están no adjunto a (o gestionado por) una sesión. La diferencia entre ellos es lo que sucede con la instancia que se pasa a la función. actualizar intenta volver a vincular la instancia, lo que significa que puede que no haya otra instancia de la entidad persistente adjunta a la sesión en este momento, de lo contrario se lanzará una excepción. fusionar , sin embargo, solo copia todos los valores a una instancia persistente en la sesión (que se cargará si no está cargada actualmente). El objeto de entrada no se cambia. Entonces, fusionar es más general que actualizar , pero puede usar más recursos.

Este enlace explica de buena manera:

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

Todos tenemos esos problemas con los que nos encontramos con la misma frecuencia que cuando los volvemos a ver, sabemos que hemos resuelto esto, pero no podemos recordar cómo.

La excepción NonUniqueObjectException que se produce al usar Session.saveOrUpdate () en Hibernate es una de las mías. Estaré agregando nuevas funcionalidades a una aplicación compleja. Todas mis pruebas unitarias funcionan bien. Luego, al probar la interfaz de usuario, al intentar guardar un objeto, comienzo a recibir una excepción con el mensaje & # 8220; un objeto diferente con el mismo valor de identificador ya estaba asociado con la sesión. & # 8221; Aquí & # 8217; s un código de ejemplo de 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();

Para comprender la causa de esta excepción, es importante entender los objetos separados y lo que sucede cuando llama a saveOrUpdate () (o simplemente actualiza ()) en un objeto separado.

Cuando cerramos una sesión de hibernación individual, los objetos persistentes con los que estamos trabajando se separan. Esto significa que los datos todavía están en la memoria de la aplicación, pero Hibernate ya no es responsable de rastrear los cambios en los objetos.

Si luego modificamos nuestro objeto separado y queremos actualizarlo, tenemos que volver a adjuntar el objeto. Durante ese proceso de reinstalación, Hibernate verificará si hay otras copias del mismo objeto. Si encuentra alguno, tiene que decirnos que no sabe qué es lo real # 8220; La copia es más. Quizás se hicieron otros cambios a esas otras copias que esperamos que se guarden, pero Hibernate no las conoce, porque no se las estaba administrando en ese momento.

En lugar de guardar datos posiblemente malos, Hibernate nos informa sobre el problema a través de NonUniqueObjectException.

Entonces, ¿qué debemos hacer? En Hibernate 3, tenemos merge () (en Hibernate 2, use saveOrUpdateCopy ()). Este método obligará a Hibernate a copiar cualquier cambio de otras instancias separadas en la instancia que desea guardar y, por lo tanto, fusiona todos los cambios en la memoria antes de guardar.

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

Es importante tener en cuenta que la fusión devuelve una referencia a la versión recién actualizada de la instancia. No se trata de volver a colocar el elemento en la sesión. Si prueba, por ejemplo, la igualdad (item == item3), encontrará que en este caso devuelve falso. Probablemente querrá trabajar con item3 a partir de este punto.

También es importante tener en cuenta que la API de persistencia de Java (JPA) no tiene un concepto de objetos separados y reenganchados, y utiliza EntityManager.persist () y EntityManager.merge ().

He encontrado en general que cuando uso Hibernate, saveOrUpdate () suele ser suficiente para mis necesidades. Por lo general, solo necesito usar la combinación cuando tengo objetos que pueden tener referencias a objetos del mismo tipo. Más recientemente, la causa de la excepción estaba en el código que validaba que la referencia no era recursiva. Estaba cargando el mismo objeto en mi sesión como parte de la validación, causando el error.

¿Dónde has encontrado este problema? ¿La fusión funcionó para usted o necesitaba otra solución? ¿Prefiere usar siempre la combinación o prefiere usarla solo cuando sea necesario para casos específicos?

En realidad, la diferencia entre los métodos save () y persist () de hibernación depende de la clase de generador que estemos usando.

Si nuestra clase de generador está asignada, entonces no hay diferencia entre los métodos save () y persist (). Debido a que el generador & # 8216; asignó & # 8217; significa que, como programador, necesitamos dar el valor clave principal para guardar en la base de datos correctamente [Espero que conozcas este concepto de generadores] En el caso de una clase de generador diferente a la asignada, suponga que si nuestro nombre de clase de generador es Incremento significa que hibernar por sí mismo asignará el valor de ID de clave primaria en el derecho de la base de datos [distinto del generador asignado, hibernación solo se utiliza para cuidar el valor de ID de clave primaria recordado ], por lo tanto, en este caso, si llamamos al método save () o persist () , entonces insertará el registro en la base de datos normalmente. Pero lo importante es que el método save () puede devolver ese valor de ID de clave principal generado por hibernación y podemos verlo por

long s = session.save(k);

En este mismo caso, persist () nunca devolverá ningún valor al cliente.

Encontré un buen ejemplo que muestra las diferencias entre todos los métodos de guardado de hibernación:

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

En resumen, según el enlace anterior:

save()

  • Podemos invocar este método fuera de una transacción. Si usamos esto sin transacciones y tenemos una conexión en cascada entre entidades, entonces solo se guarda la entidad primaria a menos que eliminemos la sesión.
  • Entonces, si hay otros objetos asignados desde el objeto principal, se guardan al momento de confirmar la transacción o cuando eliminamos la sesión.

persist()

  • Es similar a usar save () en la transacción, por lo que es seguro y se ocupa de cualquier objeto en cascada.

saveOrUpdate()

  • Se puede usar con o sin la transacción, y al igual que save (), si se usa sin la transacción, las entidades asignadas no se guardarán una vez que eliminemos la sesión.

  • Resultados en consultas de inserción o actualización basadas en los datos proporcionados. Si los datos están presentes en la base de datos, se ejecuta la consulta de actualización.

update()

  • La actualización de Hibernate debe usarse cuando sabemos que solo estamos actualizando la información de la entidad. Esta operación agrega el objeto de entidad al contexto persistente y los cambios adicionales se rastrean y guardan cuando se confirma la transacción.
  • Por lo tanto, incluso después de llamar a update, si establecemos algún valor en la entidad, se actualizarán cuando se confirme la transacción.

merge()

  • La combinación de hibernación se puede usar para actualizar los valores existentes, sin embargo, este método crea una copia del objeto de entidad pasado y lo devuelve. El objeto devuelto forma parte del contexto persistente y se realiza un seguimiento de los cambios, no se realiza un seguimiento del objeto pasado. Esta es la principal diferencia con merge () de todos los demás métodos.

También para ejemplos prácticos de todo esto, consulte el enlace que mencioné anteriormente, que muestra ejemplos de todos estos métodos diferentes.

Tenga en cuenta que si llama a una actualización en un objeto separado, siempre habrá una actualización realizada en la base de datos, ya sea que haya cambiado el objeto o no. Si no es lo que desea, debe usar Session.lock () con LockMode.None.

Deberías llamar a actualizar solo si el objeto se cambió fuera del alcance de tu sesión actual (cuando está en modo separado).

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top