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

199

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 qué 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()
Henrik Paul
fuente

Respuestas:

117

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

saveOrUpdate Las llamadas se guardan o actualizan según algunas comprobaciones. Por ejemplo, si no existe un identificador, se llama guardar. De lo contrario, se llama a la 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.

actualizar 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 del objeto, eche un vistazo aquí . Con guardar y actualizar, se trata de objetos persistentes. Están vinculados a una sesión para que Hibernate sepa lo que ha cambiado. Pero cuando tiene un objeto transitorio, no hay sesión involucrada. En estos casos, debe utilizar 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.

Lee Theobald
fuente
22
Me gustaría aceptar esto como la respuesta, pero una cosa aún no está clara: dado que save () recurre a update (), si ese elemento existe, ¿en qué se diferencia de saveOrUpdate () en la práctica?
Henrik Paul
¿Dónde se especifica que guardar funcionaría en instancias separadas?
jrudolph
2
Si su descripción de fusionar / persistir solo es importante en objetos transitorios, entonces esto tiene mucho sentido y se ajusta a cómo usamos hibernate. También tenga en cuenta que una fusión a menudo tiene limitaciones de rendimiento en comparación con una actualización, ya que parece hacer una recuperación adicional para las comprobaciones de integridad de algún tipo.
Martin Dale Lyness
1
La respuesta de jrudolph a continuación es más precisa.
azerole
2
Dado que hibernate probablemente sabe en qué estado se encuentra un objeto, ¿por qué tenemos que hacer esto manualmente al escribir el programa? Debería haber solo un método de guardar.
masterxilo
116
╔══════════════╦═══════════════════════════════╦════════════════════════════════╗
    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
fuente
updateun objeto transitorio está bien, no obtuve una excepción.
GMsoF
Lo que sé, no podemos persistir en un transitorio de ninguna manera. Creo que la diferencia podría ser entre independiente y persistente. Por favor corrigeme.
Ram
hay muchos errores aquí ... por ejemplo, 1) ´save () ´ no devuelve un "objeto adjunto", devuelve el "id"; 2) ´persist () ´ no garantiza que establezca el ´id´, ni "persiste el objeto en DB"; ...
Eugen Labun
67
  • 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. Como guardar devuelve el identificador, la instrucción INSERT tiene que ejecutarse instantáneamente independientemente del estado de la transacción (que generalmente es algo malo). Persist no ejecutará ninguna declaración fuera de la transacción actualmente en ejecución solo para asignar el identificador. Save / Persist funciona en instancias transitorias , es decir, instancias que todavía no tienen un identificador asignado y, como tales, no se guardan en la base de datos.

  • Actualizar y combinar funcionan en instancias separadas , es decir, instancias que tienen una entrada correspondiente en la base de datos, pero que actualmente no están conectadas (o gestionadas por) una sesión. La diferencia entre ellos es lo que le sucede a la instancia que se pasa a la función. La actualización intenta volver a conectar la instancia, lo que significa que no debe haber otra instancia de la entidad persistente conectada a la sesión en este momento, de lo contrario, se genera una excepción. sin embargo, merge 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 modifica. Así que fusionar es más general que actualizar, pero puede usar más recursos.

jrudolph
fuente
la declaración de inserción todavía no sucede si tiene su propio generador de Id
kommradHomer
Entonces, en el caso de un objeto separado, la fusión activará una selección, mientras que la actualización no.
Gab
1
save() - If an INSERT has to be executed to get the identifier, then this INSERT happens immediately, no matter if you are inside or outside of a transaction. This is problematic in a long-running conversation with an extended Session/persistence context.¿Puede decirme cómo puede ocurrir un inserto fuera de una sesión y por qué es malo?
Erran Morad
Descargo de responsabilidad: no he usado hibernar durante mucho tiempo. En mi opinión, el problema es este: la firma y el contrato de save () requieren que save devuelva un identificador para el nuevo objeto. Dependiendo de la estrategia de generación de id que elija, el DB genera el identificador cuando se edita un valor INSERT. En consecuencia, en esos casos no puede devolver un identificador en este momento sin tenerlo generado y para generarlo debe ejecutarlo INSERT ahora . Dado que una transacción de larga duración no se ejecuta en este momento, sino solo en commit, la única forma de ejecutar el INSERTahora es ejecutarla fuera del tx.
jrudolph
12

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 poca frecuencia como para que cuando los volvamos a ver, sepamos que hemos resuelto esto, pero no podemos recordar cómo.

La excepción NonUniqueObjectException lanzada cuando se usa Session.saveOrUpdate () en Hibernate es una de las mías. Agregaré nueva funcionalidad a una aplicación compleja. Todas mis pruebas unitarias funcionan bien. Luego, al probar la interfaz de usuario, al intentar guardar un objeto, empiezo a recibir una excepción con el mensaje "ya se ha asociado un objeto diferente con el mismo valor de identificador a la sesión". Aquí hay un código de ejemplo de Java Persistence con 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 comprender los objetos separados y lo que sucede cuando llama a saveOrUpdate () (o simplemente actualiza ()) en un objeto separado.

Cuando cerramos una sesión de Hibernate 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 del seguimiento de los cambios en los objetos.

Si luego modificamos nuestro objeto separado y queremos actualizarlo, tenemos que volver a colocar el objeto. Durante ese proceso de reinstalación, Hibernate verificará si hay otras copias del mismo objeto. Si encuentra alguno, tiene que decirnos que ya no sabe cuál es la copia "real". Quizás se hicieron otros cambios a esas otras copias que esperamos que se guarden, pero Hibernate no las conoce porque no 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é vamos a 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 combinación devuelve una referencia a la versión recién actualizada de la instancia. No está volviendo a asociar el elemento a la sesión. Si prueba por ejemplo la igualdad (item == item3), encontrará que devuelve falso en este caso. Probablemente desee trabajar con el elemento 3 a partir de ahora.

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

En general, descubrí que cuando utilizo Hibernate, saveOrUpdate () suele ser suficiente para mis necesidades. Por lo general, solo necesito usar merge 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 según sea necesario para casos específicos?

HakunaMatata
fuente
Enlace al artículo sobre webarchive, ya que el original no está disponible: web.archive.org/web/20160521091122/http://www.stevideter.com:80/…
Eugen Labun
5

En realidad, la diferencia entre hibernar save()y persist()métodos depende de la clase de generador que estamos usando.

Si se asigna nuestra clase generador, entonces no hay diferencia entre save()e persist() métodos. Debido a que el generador 'asignado' significa, como programador, necesitamos dar el valor de la clave primaria para guardarlo en la base de datos correcta [Espero que conozca este concepto de generador] En caso de que no sea una clase de generador asignada, suponga que si nuestro nombre de clase de generador es Incremento significa hibernate sí mismo asignará el valor de ID de la clave primaria en la base de datos derecha [aparte del generador asignado, hibernate solo se usa para cuidar el valor de ID de la clave primaria recordar], por lo que en este caso si llamamos save()o persist()método, insertará el registro en la base de datos normalmente Pero escuche, el save()método puede devolver el valor de identificación de la clave primaria que se genera por hibernación y podemos verlo por

long s = session.save(k);

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

Hari Krishna
fuente
5

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, de acuerdo con el enlace anterior:

salvar()

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

persistir()

  • Es similar al uso de save () en la transacción, por lo que es seguro y se encarga de los objetos 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 mapeadas no se guardarán en;

  • 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.

actualizar()

  • La actualización de Hibernate debe usarse donde 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 la actualización, si establecemos valores en la entidad, se actualizarán cuando se confirme la transacción.

unir()

  • La combinación de hibernación se puede utilizar para actualizar los valores existentes, sin embargo, este método crea una copia del objeto de entidad pasado y lo devuelve. El objeto devuelto es parte del contexto persistente y se rastrea por cualquier cambio, el objeto pasado no se rastrea. 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, muestra ejemplos de todos estos métodos diferentes.

Fuera de quicio
fuente
3

Como expliqué en este artículo , debe preferir los métodos JPA la mayor parte del tiempo y las updatetareas de procesamiento por lotes.

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

  • Transitoria (nueva)
  • Gestionado (persistente)
  • Separado
  • Eliminado (eliminado)

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

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

ingrese la descripción de la imagen aquí

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

ingrese la descripción de la imagen aquí

Persistir

Para cambiar el estado de una entidad de Transitoria (Nueva) a Administrada (Persistente), podemos usar el persistmétodo ofrecido por el JPA EntityManagerque también es heredado por el Hibernate Session.

El persistmétodo activa un PersistEventque es manejado por el DefaultPersistEventListeneroyente de eventos 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 instrucciones 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 administradas 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 cual el JPA EntityManagery el Hibernate Sessionse conocen como el caché de primer nivel.

Al llamar persist, la entidad solo se adjunta al contexto de persistencia actualmente en ejecución, y el INSERT puede posponerse hasta que flushse llame.

La única excepción es el generador de IDENTIDAD que activa el INSERTAR de inmediato, ya que esa es la única forma en que puede obtener el identificador de la entidad. Por esta razón, Hibernate no puede insertar por lotes para entidades que usan el generador IDENTITY. Para obtener más detalles sobre este tema, consulte este artículo .

Salvar

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

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

Para ver cómo funciona el savemé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 instrucciones 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 persist, el savemétodo devuelve el identificador de entidad.

Para más detalles, mira este artículo .

Actualizar

El updatemétodo específico de Hibernate está destinado a evitar el mecanismo de verificación sucio y forzar una actualización de la entidad en el momento del vaciado.

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

Para ver cómo funciona el updatemé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 instrucciones 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.

Utilizando @SelectBeforeUpdatepara evitar actualizaciones innecesarias

Ahora, la ACTUALIZACIÓN siempre se ejecutará, incluso si la entidad no se cambió mientras estaba en el estado desconectado. Para evitar esto, puede usar la @SelectBeforeUpdateanotación Hibernate que activará una SELECTdeclaración que se obtiene y loaded stateque luego utiliza el mecanismo de verificación sucio.

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 instrucciones 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

Tenga en cuenta que, esta vez, no se UPDATEejecuta desde que el mecanismo de comprobación sucia de Hibernate ha detectado que la entidad no se modificó.

SaveOrUpdate

El saveOrUpdatemétodo específico de Hibernate es solo un alias para savey update.

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

Ahora, puede usar saveOrUpdatecuando desea persistir una entidad o forzar una 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 el NonUniqueObjectException

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 va a lanzar un NonUniqueObjectExceptionporque el segundoEntityManager ya contiene una Bookentidad con el mismo identificador al 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 usar elmerge método ofrecido por JPA EntityManagery heredado por Hibernate Sessiontambién.

Como se explica en este artículo , elmerge obtiene una nueva instantánea de 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 que se pasó al mergemétodo.

El mergemétodo desencadena unMergeEventque es manejado por el DefaultMergeEventListeneroyente de eventos Hibernate.

Para ver cómo funciona el mergemétodo, considere el siguiente ejemplo que persiste a una Bookentidad en una transacción, luego la modifica mientras la entidad está en el estado separado y pasa la entidad separada amerge en un contexto de persistencia posterior.

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

Al ejecutar el caso de prueba anterior, Hibernate ejecutó las siguientes instrucciones SQL:

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

Observe que la referencia de entidad devuelta por merge es diferente de la separada que pasamos al mergemétodo.

Ahora, aunque debería preferir usar JPA merge al copiar el estado de la entidad separada, el extra SELECTpuede ser problemático al ejecutar una tarea de procesamiento por lotes.

Por esta razón, debería preferir usar update cuando esté seguro de que no hay una referencia de entidad ya asociada al Contexto de persistencia actualmente en ejecución y que la entidad separada se ha modificado.

Para obtener más detalles sobre este tema, consulte este artículo. .

Conclusión

Para persistir una entidad, debe usar el persistmétodo JPA . Para copiar el estado de la entidad separada, se mergedebe preferir. El updatemétodo es útil solo para tareas de procesamiento por lotes. Los savey saveOrUpdateson solo alias updatey probablemente no debería usarlos en absoluto.

Algunos desarrolladores llaman save incluso cuando la entidad ya está administrada, pero esto es un error y desencadena un evento redundante ya que, para las entidades administradas, la ACTUALIZACIÓN se maneja automáticamente en el momento de descarga del contexto de Persistencia.

Para más detalles, mira este artículo .

Vlad Mihalcea
fuente
2

Tenga en cuenta que si llama a una actualización en un objeto separado, siempre se realizará una actualización 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.

Debe llamar a la actualización solo si el objeto se cambió fuera del alcance de su sesión actual (cuando está en modo separado).

bernardn
fuente
1

Ninguna de las siguientes respuestas son correctas. Todos estos métodos parecen ser iguales, pero en la práctica hacen cosas absolutamente diferentes. Es difícil hacer comentarios cortos. Es mejor dar un enlace a la documentación completa sobre estos métodos: http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/objectstate.html

Anton Popovich
fuente
11
Díganos por qué las siguientes respuestas no son correctas.
Erran Morad
0

Ninguna de las respuestas anteriores está completa. Aunque la respuesta de Leo Theobald parece la respuesta más cercana.

El punto básico es cómo hibernate trata con estados de entidades y cómo los maneja cuando hay un cambio de estado. Todo se debe ver con respecto a los rubores y los compromisos, que todo el mundo parece haber ignorado por completo.

NUNCA USE EL MÉTODO DE AHORRO de HIBERNATE. ¡OLVIDEN QUE INCLUSO EXISTE EN HIBERNATE!

Persistir

Como todos explicaron, Persist básicamente hace una transición de una entidad del estado "Transitorio" al "Administrado". En este punto, un granizado o una confirmación pueden crear una declaración de inserción. Pero la entidad aún permanecerá en estado "Administrado". Eso no cambia con el rubor.

En este punto, si "persiste" nuevamente no habrá cambios. Y no habrá más salvaciones si tratamos de persistir en una entidad persistente.

La diversión comienza cuando tratamos de desalojar a la entidad.

Un desalojo es una función especial de Hibernate que hará la transición de la entidad de "Administrado" a "Independiente". No podemos llamar a persistir en una entidad separada. Si hacemos eso, entonces Hibernate genera una excepción y toda la transacción se revierte en commit.

Combinar vs Actualizar

Estas son 2 funciones interesantes que hacen cosas diferentes cuando se tratan de diferentes maneras. Ambos intentan hacer la transición de la entidad del estado "Separado" al estado "Administrado". Pero hacerlo de manera diferente.

Comprenda el hecho de que Separado significa una especie de estado "fuera de línea". y administrado significa estado "en línea".

Observe el siguiente código:

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

Cuando haces esto? ¿Qué piensas tú que sucederá? Si dijo que esto generará una excepción, entonces está en lo correcto. Esto generará una excepción porque la fusión ha funcionado en el objeto de entidad, que es el estado separado. Pero no altera el estado del objeto.

Detrás de escena, la fusión generará una consulta de selección y básicamente devolverá una copia de la entidad que está en estado adjunto. Observe el siguiente código:

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

El ejemplo anterior funciona porque la fusión ha traído una nueva entidad al contexto que está en estado persistente.

Cuando se aplica con Actualización, lo mismo funciona bien porque la actualización en realidad no trae una copia de la entidad como fusión.

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

Al mismo tiempo, en el seguimiento de depuración podemos ver que la actualización no ha generado una consulta SQL de seleccionar como combinar.

Eliminar

En el ejemplo anterior, utilicé eliminar sin hablar de eliminar. Eliminar básicamente hará que la entidad pase del estado administrado al estado "eliminado". Y cuando se sonroja o se compromete, emitirá un comando de eliminación para almacenar.

Sin embargo, es posible devolver la entidad al estado "administrado" desde el estado "eliminado" utilizando el método de persistencia.

Espero que la explicación anterior aclare cualquier duda.

Cerveza de jengibre
fuente