¿Cuáles son las diferencias entre los distintos métodos de guardado en Hibernate?

Resuelto Henrik Paul asked hace 16 años • 10 respuestas

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 existe 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()
Henrik Paul avatar Oct 02 '08 14:10 Henrik Paul
Aceptado

Aquí está mi comprensión de los métodos. Sin embargo, principalmente se basan en la API , ya que no los uso todos en la práctica.

saveOrUpdate Llama a guardar o actualizar dependiendo de algunas comprobaciones. Por ejemplo, si no existe ningún identificador, se llama a guardar. De lo contrario, se llama a la actualización.

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

update Intenta conservar la entidad utilizando un identificador existente. Si no existe ningún identificador, creo que se lanza una excepción.

saveOrUpdateCopy Esto está en desuso y ya no debería usarse. En cambio hay...

fusionar Ahora aquí es donde mi conocimiento comienza a fallar. Lo importante aquí es la diferencia entre entidades transitorias, separadas y persistentes. Para obtener más información sobre los estados de los objetos, consulte aquí . Con guardar y actualizar, se trata de objetos persistentes. Están vinculados a una sesión para que Hibernate sepa qué ha cambiado. Pero cuando tienes un objeto transitorio, no hay sesión involucrada. En estos casos, debe utilizar la combinación para realizar actualizaciones y persistir para guardar.

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

Lee Theobald avatar Oct 02 '2008 08:10 Lee Theobald
╔══════════════╦═══════════════════════════════╦════════════════════════════════╗
║    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()         ║
║              ║                               ║                                ║
╚══════════════╩═══════════════════════════════╩════════════════════════════════╝
Sergii Shevchyk avatar Sep 03 '2013 20:09 Sergii Shevchyk
  • Consulte el Foro de Hibernate para obtener una explicación de las sutiles diferencias entre persistir y guardar. Parece que la diferencia es el momento en que finalmente se ejecuta la instrucción INSERT. Dado que save devuelve el identificador, la instrucción INSERT debe ejecutarse instantáneamente independientemente del estado de la transacción (lo cual generalmente es malo). Persist no ejecutará ninguna declaración fuera de la transacción actualmente en ejecución solo para asignar el identificador. Guardar/Persistir funcionan en instancias transitorias , es decir, instancias que aún no tienen un identificador asignado y, como tales, no se guardan en la base de datos.

  • Tanto Update como Merge funcionan en instancias separadas , es decir, instancias que tienen una entrada correspondiente en la base de datos pero que actualmente no están adjuntas (ni administradas por) una sesión. La diferencia entre ellos es lo que sucede con la instancia que se pasa a la función. La actualización intenta volver a adjuntar la instancia, eso significa que no debe haber ninguna otra instancia de la entidad persistente adjunta a la sesión en este momento; de lo contrario, se genera una excepción. merge , sin embargo, simplemente copia todos los valores en una instancia persistente en la sesión (que se cargará si no está cargada actualmente). El objeto de entrada no se modifica. Por lo tanto , fusionar es más general que actualizar , pero puede utilizar más recursos.

jrudolph avatar Oct 02 '2008 09:10 jrudolph

Debería preferir los métodos JPA la mayor parte del tiempo y los métodos updatepara tareas de procesamiento por lotes.

Una entidad JPA o Hibernate puede estar en uno de los siguientes cuatro estados:

  • Transitorio (nuevo)
  • Gestionado (persistente)
  • Separado
  • Eliminado (eliminado)

La transición de un estado a otro se realiza mediante los métodos EntityManager o Session.

Por ejemplo, la JPA EntityManagerproporciona los siguientes métodos de transición del estado de la entidad.

Transiciones de estado de entidad JPA

Hibernate Sessionimplementa todos los EntityManagermétodos JPA y proporciona algunos métodos adicionales de transición de estado de entidad savecomo saveOrUpdatey update.

Hibernar transiciones de estado de entidad

Persistir

Para cambiar el estado de una entidad de Transitorio (Nuevo) a Administrado (Persistente), podemos usar el persistmétodo que ofrece JPA EntityManagerque también es heredado por Hibernate Session.

El persistmétodo activa un PersistEventque es manejado por el DefaultPersistEventListenerdetector de eventos de Hibernate.

Por lo tanto, al ejecutar el siguiente caso de prueba:

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 genera las siguientes declaraciones SQL:

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
)

Observe que idse asigna antes de adjuntar la Bookentidad al contexto de persistencia actual. Esto es necesario porque las entidades gestionadas se almacenan en una Mapestructura donde la clave está formada por el tipo de entidad y su identificador y el valor es la referencia de la entidad. Esta es la razón por la que JPA EntityManagere Hibernate Sessionse conocen como caché de primer nivel.

Al llamar persist, la entidad solo se adjunta al contexto de persistencia que se está ejecutando actualmente, y el INSERT se puede posponer hasta que se flushllame.

La única excepción es la IDENTITYque activa INSERT de inmediato, ya que esa es la única forma en que puede obtener el identificador de entidad. Por este motivo, Hibernate no puede realizar inserciones por lotes para entidades que utilicen el IDENTITYgenerador.

Ahorrar

El método específico de Hibernate savees anterior a JPA y ha estado disponible desde el comienzo del proyecto Hibernate.

El savemétodo activa un SaveOrUpdateEventque es manejado por el DefaultSaveOrUpdateEventListenerdetector de eventos de Hibernate. Por tanto, el savemétodo es equivalente a los métodos updatey saveOrUpdate.

Para ver cómo savefunciona el método, considere el siguiente caso de prueba:

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

Al ejecutar el caso de prueba anterior, Hibernate genera las siguientes declaraciones SQL:

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
)

Como puede ver, el resultado es idéntico a la persistllamada al método. Sin embargo, a diferencia de persist, el savemétodo devuelve el identificador de la entidad.

Actualizar

El método específico de Hibernate updateestá destinado a omitir el mecanismo de verificación sucia y forzar una actualización de la entidad en el momento del vaciado.

El updatemétodo activa un SaveOrUpdateEventque es manejado por el DefaultSaveOrUpdateEventListenerdetector de eventos de Hibernate. Por tanto, el updatemétodo es equivalente a los métodos savey saveOrUpdate.

Para ver cómo updatefunciona el método, considere el siguiente ejemplo que persiste una Bookentidad en una transacción, luego la modifica mientras la entidad está en el estado desconectado y fuerza la ACTUALIZACIÓN de SQL utilizando la updatellamada al método.

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

Al ejecutar el caso de prueba anterior, Hibernate genera las siguientes declaraciones SQL:

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

Observe que UPDATEse ejecuta durante el vaciado del contexto de persistencia, justo antes de la confirmación, y es por eso que el Updating the Book entitymensaje se registra primero.

Usar @SelectBeforeUpdatepara evitar actualizaciones innecesarias

Ahora, la ACTUALIZACIÓN siempre se ejecutará incluso si la entidad no se modificó mientras estaba en el estado desconectado. Para evitar esto, puede usar la @SelectBeforeUpdateanotación Hibernate, que activará una SELECTdeclaración que se obtuvo loaded statey luego será utilizada por el mecanismo de verificación sucia.

Entonces, si anotamos la Bookentidad con la @SelectBeforeUpdateanotación:

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

    //Code omitted for brevity
}

Y ejecute el siguiente caso de prueba:

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 ejecuta las siguientes declaraciones SQL:

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

Observe que, esta vez, no se UPDATEejecuta ya que el mecanismo de verificación sucia de Hibernate ha detectado que la entidad no fue modificada.

Guardar o actualizar

El método específico de Hibernate saveOrUpdatees solo un alias para savey update.

El saveOrUpdatemétodo activa un SaveOrUpdateEventque es manejado por el DefaultSaveOrUpdateEventListenerdetector de eventos de Hibernate. Por tanto, el updatemétodo es equivalente a los métodos savey saveOrUpdate.

Ahora, puede utilizarlo saveOrUpdatecuando desee conservar una entidad o forzarla, UPDATEcomo se ilustra en el siguiente ejemplo.

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

Cuidado con elNonUniqueObjectException

Un problema que puede ocurrir con save, updatey saveOrUpdatees si el contexto de persistencia ya contiene una referencia de entidad con la misma identificación y del mismo tipo que en el siguiente ejemplo:

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

Ahora, al ejecutar el caso de prueba anterior, Hibernate arrojará un NonUniqueObjectExceptionporque el segundo EntityManagerya contiene una Bookentidad con el mismo identificador que el que pasamos update, y el contexto de persistencia no puede contener dos representaciones de la misma entidad.

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)

Unir

Para evitarlo NonUniqueObjectException, debe utilizar el mergemétodo ofrecido por JPA y heredado también EntityManagerpor Hibernate .Session

Obtiene mergeuna nueva instantánea de la entidad de la base de datos si no se encuentra ninguna referencia de entidad en el contexto de persistencia y copia el estado de la entidad separada pasada al mergemétodo.

El mergemétodo activa un MergeEventque es manejado por el DefaultMergeEventListenerdetector de eventos de Hibernate.

Para ver cómo mergefunciona el método, considere el siguiente ejemplo que persiste una Bookentidad en una transacción, luego la modifica mientras la entidad está en el estado desconectado y pasa la entidad separada a mergeuna subsecuencia de contexto de persistencia.

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.

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.

Vlad Mihalcea avatar Feb 27 '2019 13:02 Vlad Mihalcea