¿Cuál es la diferencia entre persistir, guardar, fusionar y actualizar? ¿Cuál deberías usar?

JPA e Hibernar proporcionan diferentes métodos para mantener las entidades nuevas y actualizar las existentes. Puede elegir entre los métodos persist and merge de JPA y guardar y actualizar de Hibernate.

Parece que hay 2 pares de 2 métodos que hacen lo mismo. Puede usar los métodos persist y save para almacenar una nueva entidad y los métodos merge y update para almacenar los cambios de una entidad separada en la base de datos. Es por eso que muchos desarrolladores se preguntan cuál de estos métodos deberían usar. Echemos un vistazo más de cerca a los detalles y pequeñas diferencias de estos métodos.

Un agradecimiento especial a Steve Ebersole (Desarrollador principal – Hibernate OR), quien proporcionó sus comentarios y excelentes ideas sobre algunos de los detalles ocultos de la implementación de Hibernate.

Transiciones de Estado de entidad

Antes de entrar en los detalles de estos 4 métodos, necesito darle una introducción rápida a los estados de ciclo de vida de entidad de JPA.

Si una entidad está asociada al contexto de persistencia actual, tiene administrado el estado del ciclo de vida. Esto significa que se asigna a un registro de base de datos. Su proveedor de persistencia genera las instrucciones SQL INSERT y UPDATE necesarias para propagar todos los cambios. Una entidad administrada también se almacena en la caché de 1er nivel.

Cuando crea una entidad nueva, se encuentra en estado transitorio. Permanece en este estado hasta que lo adjunte al contexto de persistencia actual. Te mostraré cómo puedes hacer eso con el método de guardado persist y Hibernate de JPA, en la siguiente sección. Mientras una entidad se encuentre en estado transitorio, no se asignará a un registro de base de datos y no se administrará mediante ningún contexto de persistencia.

Las entidades en el estado de ciclo de vida separado ya no son administradas por el contexto de persistencia. Ese puede ser el caso porque cerró el contexto de persistencia o separó explícitamente la entidad del contexto actual. Entraré en más detalles sobre cómo puedes volver a unir estas entidades con los métodos de actualización de Hibernate y merge de JPA en una parte posterior de este post.

Y se elimina el último estado del ciclo de vida. Estas entidades estaban administradas previamente en el estado, antes de que las programara para su eliminación. Eliminar entidades está fuera del alcance de esta publicación, por lo que no entraré en demasiados detalles al respecto. Puede programar la eliminación de una entidad llamando al método remove en la interfaz EntityManager.

Persistencia de una Nueva Entidad Mediante persist O save

Al crear un nuevo objeto de entidad, se encuentra en el estado de ciclo de vida transitorio. No mapea ningún registro de base de datos.

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

Necesita adjuntar la entidad a un contexto de persistencia para que se administre y se mantenga en la base de datos. Puedes usar el método persist de JPA o el método save de Hibernate para hacer eso. Ambos métodos parecen hacer lo mismo, pero hay algunas diferencias.

Especificación vs API propietaria

La diferencia más obvia es que la especificación JPA define el método persist. Puede usarlo con todas las implementaciones de JPA. El método de guardado, por otro lado, es específico de la hibernación. Por lo tanto, no está disponible en otras implementaciones de JPA.

Pero eso solo es relevante si desea poder reemplazar Hibernate con otra implementación de JPA, como Eclipse Link o OpenJPA.

Tipos De retorno Y Ejecución De Sentencias SQL

Otra diferencia obvia entre estos 2 métodos es su tipo de retorno. El método persist de JPA devuelve void y el método save de Hibernate devuelve la clave principal de la entidad.

Eso puede parecer una gran diferencia, especialmente si echas un vistazo más de cerca al Javadoc de Hibernate y a la especificación JPA:

  • El Javadoc del método save de Hibernate indica que genera primero el valor de la clave primaria:

    Mantenga la instancia transitoria dada, asignando primero un identificador generado.
    Sesión Javadoc.guardar (entidad)

  • No encontrará ninguna información sobre esto en la especificación de JPA. No define cuándo debe asignarse el valor de la clave primaria. Por lo tanto, el proveedor de persistencia puede hacerlo en cualquier momento entre la llamada del método persist y el flujo del contexto de persistencia.

En la mayoría de los casos, no hace ninguna diferencia si llama al método save o persist. Hibernate utiliza el nombre de la clase de entidad y el valor de la clave principal para almacenar la entidad en la caché de primer nivel. Por lo tanto, necesita un valor de clave primaria cuando ejecuta el método persist.

En casi todas las situaciones, Hibernate genera el valor de clave primaria de inmediato y desencadena una instrucción SQL si es necesario, cuando llama al método persist o save.

Pero ese no es el caso si utiliza la estrategia de identidad e intenta mantener una entidad sin una transacción activa o con FlushMode.MANUAL. Si llama al método persist en una de estas situaciones, Hibernate retrasa la ejecución de la instrucción SQL INSERT y crea un valor de clave primaria temporal. Pero si llama al método save, Hibernate ejecuta la instrucción SQL INSERT inmediatamente y recupera el valor de la clave principal de la base de datos.

Luego puede recuperarlo como el valor devuelto del método save.

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

O puede llamar al método getter del atributo de clave primaria de su entidad administrada si utiliza el método persist de JPA.

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

Hibernate ejecuta las mismas instrucciones SQL cuando llama al método persist o save. Qué y cuándo lo hace depende de su estrategia de generación de claves principales:

No generado

Si establece el valor de la clave primaria mediante programación, por ejemplo, en un identificador natural, Hibernate solo ejecuta una instrucción SQL INSERT cuando vacía el contexto de persistencia.

14:08:34,979 INFO TestPersistSaveMerge:237 - Save entity14:08:35,052 INFO TestPersistSaveMerge:240 - Commit transaction14:08:35,123 DEBUG SQL:92 - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)
Vídeo de YouTube

Generado con estrategia de identidad

Si utiliza la estrategia de identidad para generar el valor de clave principal, Hibernate debe ejecutar la instrucción INSERT cuando llame al método save o persist para recuperar el valor de clave principal de la base de datos.

14:09:28,264 INFO TestPersistSaveMerge:237 - Save entity14:09:28,336 DEBUG SQL:92 - insert into Author (firstName, lastName, version) values (?, ?, ?)14:09:28,354 INFO TestPersistSaveMerge:240 - Commit transaction
Vídeo de YouTube

Generado con SEQUENCE strategy

Y si usa la SECUENCIA, Hibernate realiza una instrucción SQL SELECT para recuperar el siguiente valor de la secuencia de la base de datos. Hibernate retrasa la instrucción INSERT hasta que borra el contexto de persistencia. En este ejemplo, el flush ocurre cuando se confirma la transacción.

14:10:27,994 INFO TestPersistSaveMerge:237 - Save entity14:10:28,002 DEBUG SQL:92 - select nextval ('hibernate_sequence')14:10:28,042 INFO TestPersistSaveMerge:240 - Commit transaction14:10:28,096 DEBUG SQL:92 - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)

Generado con la estrategia de tabla

No debe usar la estrategia de tabla porque requiere bloqueos de nivel de fila en la tabla de claves principales y no escala bien. Si utiliza esta estrategia de todos modos, Hibernate realiza una instrucción SQL SELECT para recuperar el siguiente valor de clave primaria de la base de datos y escribe el nuevo valor en la tabla de la base de datos. Retrasa la ejecución de la instrucción SQL INSERT para la nueva entidad hasta que borra el contexto de persistencia.

14:11:17,368 INFO TestPersistSaveMerge:237 - Save entity14:11:17,482 DEBUG SQL:92 - select tbl.next_val from hibernate_sequences tbl where tbl.sequence_name=? for update of tbl14:11:17,531 DEBUG SQL:92 - insert into hibernate_sequences (sequence_name, next_val) values (?,?)14:11:17,534 DEBUG SQL:92 - update hibernate_sequences set next_val=? where next_val=? and sequence_name=?14:11:17,584 INFO TestPersistSaveMerge:240 - Commit transaction14:11:17,655 DEBUG SQL:92 - insert into Author (firstName, lastName, version, id) values (?, ?, ?, ?)

cuál elegir?

Puede esperar que el método save y persist se comporte de manera diferente porque hay algunas diferencias entre la especificación JPA y el Javadoc de los métodos propietarios de Hibernate.

Pero casi todas estas diferencias desaparecen cuando echas un vistazo a la implementación interna. Los únicos que quedan son 2 casos de esquina en los que Hibernar puede retrasar la recuperación de la clave primaria, el tipo de retorno del método y el soporte de otras implementaciones de JPA.

Para la mayoría de las aplicaciones, no hace ninguna diferencia si obtiene el valor de clave primaria generado como el tipo de retorno del método save de Hibernate o desde el método getter de su atributo de clave primaria. Mientras no utilice un contexto de persistencia extendida y realice todas las operaciones de la base de datos con una transacción activa, le recomiendo usar el método persist de JPA.

Actualizar una entidad separada

Cuando cierra el contexto de persistencia actual o elimina explícitamente una entidad de él llamando a los métodos clear o detach en la interfaz EntityManager, la entidad se separa. Esto significa que ya no se almacena en la caché de 1er nivel y que Hibernate no replicará ninguno de los cambios aplicados a la base de datos.

Puede usar el método update de Hibernate o el método merge de JPA para asociar una entidad separada con un contexto de persistencia. Una vez hecho esto, Hibernate actualizará la base de datos en función de los valores de atributo de entidad.

El efecto del método update y merge parece ser el mismo, pero como verá en las siguientes secciones, hay una diferencia importante.

Método de fusión de JPA

El método de fusión de JPA copia el estado de una entidad separada a una instancia administrada de la misma entidad. Por lo tanto, Hibernate ejecuta una instrucción SQL SELECT para recuperar una entidad administrada de la base de datos. Si el contexto de persistencia ya contenía una instancia administrada de la entidad, Hibernate usa la existente en su lugar. A continuación, copia todos los valores de atributo a la entidad administrada y los devuelve al autor de la llamada.

Author managedAuthor = em.merge(a);

Después de activar el registro de instrucciones SQL, puede ver las instrucciones SELECT y UPDATE ejecutadas en la salida del registro.

11:37:21,172 DEBUG SQL:92 - select books0_.bookId as bookId1_2_0_, books0_.authorId as authorId2_2_0_, book1_.id as id1_1_1_, book1_.fk_author as fk_autho6_1_1_, book1_.format as format2_1_1_, book1_.publishingDate as publishi3_1_1_, book1_.title as title4_1_1_, book1_.version as version5_1_1_, author2_.id as id1_0_2_, author2_.firstName as firstNam2_0_2_, author2_.lastName as lastName3_0_2_, author2_.version as version4_0_2_ from BookAuthor books0_ inner join Book book1_ on books0_.authorId=book1_.id left outer join Author author2_ on book1_.fk_author=author2_.id where books0_.bookId=?11:37:21,180 INFO TestPersistSaveMerge:82 - Before commit11:37:21,182 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?

Cuando Hibernate vacía el contexto de persistencia para la próxima vez, su mecanismo de comprobación sucia comprueba todas las entidades administradas. Si detecta que la operación de fusión ha cambiado algún valor de atributo de entidad, activa la instrucción SQL UPDATE requerida.

Hay un detalle importante que debe saber cuando utiliza el método de combinación de JPA. Hibernate copia los valores de atributo de la entidad separada a la entidad administrada. Esto sobrescribe cualquier cambio que haya realizado en esta entidad dentro de la sesión actual.

Método de actualización de Hibernate

El método de actualización de Hibernate no activa una instrucción SQL SELECT. Simplemente adjunta la entidad al contexto de persistencia actual. A diferencia del método merge de JPA, no puedes perder ningún cambio llamando al método update. Si el contexto de persistencia ya contiene una instancia administrada de la entidad que desea actualizar, genera una excepción.

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

Cuando Hibernate realiza el siguiente flush, no realiza comprobaciones sucias. Esto no es posible porque Hibernate no leyó la última versión de la entidad de la base de datos. Simplemente ejecuta una instrucción de actualización SQL para la entidad reenganchada.

11:38:28,151 INFO TestPersistSaveMerge:121 - Before commit11:38:28,153 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?

La comprobación sucia que falta provoca una instrucción de actualización SQL innecesaria cuando la entidad y el registro de base de datos correspondiente contienen los mismos valores. Esto podría ser un problema si su DBA registró un desencadenador de actualización para la tabla de la base de datos. En estas situaciones, puede anotar su entidad con @SelectBeforeUpdate.

@Entity@SelectBeforeUpdatepublic class Author { ... }

Que indica a Hibernate que seleccione la entidad y realice una comprobación sucia antes de generar la instrucción SQL UPDATE. Como puede ver en la salida de registro, el comportamiento del método update ahora es similar al método merge de JPA.

19:08:16,530 INFO TestPersistSaveMerge:121 - Before commit19:08:16,531 DEBUG SQL:92 - select author_.id, author_.firstName as firstNam2_0_, author_.lastName as lastName3_0_, author_.version as version4_0_ from Author author_ where author_.id=?19:08:16,592 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?

Pero hay una diferencia significativa entre los 2 métodos. Cuando llame al método update, Hibernate solo seleccionará la entidad que proporcionó como parámetro de método. Pero cuando llamas al método merge de JPA, Hibernate también seleccionará todas las asociaciones con CascadeType.FUSIONAR. Por lo tanto, debería preferir el método de fusión de JPA si vuelve a unir un enorme gráfico de entidades.

cuál elegir?

No hay una respuesta general a estas preguntas. Como ha visto, ambos métodos tienen sus ventajas y desventajas. Debe decidir para su caso de uso específico si Hibernate necesita seleccionar la entidad antes de que active la instrucción SQL UPDATE. Y si ese es el caso, también debe considerar la profundidad de su gráfico de entidades y las implicaciones de rendimiento del comportamiento de obtención proporcionado.

Actualizar una entidad administrada

JPA e Hibernar facilitan la actualización de una entidad administrada. Si su entidad se encuentra en el estado de ciclo de vida administrado, por ejemplo, porque la obtuvo con una consulta JPQL o el método find del EntityManager, solo necesita cambiar los valores de los atributos de su entidad.

em = emf.createEntityManager();em.getTransaction().begin();a = em.find(Author.class, a.getId());a.setFirstName("Thorben");log.info("Before commit");em.getTransaction().commit();em.close();

Cuando Hibernate decide vaciar el contexto de persistencia, el mecanismo de comprobación sucia detectará el cambio y realizará la instrucción de actualización SQL requerida.

11:41:49,178 DEBUG SQL:92 - select author0_.id as id1_0_0_, author0_.firstName as firstNam2_0_0_, author0_.lastName as lastName3_0_0_, author0_.version as version4_0_0_ from Author author0_ where author0_.id=?11:41:49,191 INFO TestPersistSaveMerge:335 - Before commit11:41:49,193 DEBUG SQL:92 - update Author set firstName=?, lastName=?, version=? where id=? and version=?

No es necesario, y no debe llamar al método de guardado de Hibernate después de actualizar una entidad. Esto desencadena un evento saveOrUpdate adicional sin proporcionar ningún beneficio. Cuando Hibernate decide vaciar el contexto de persistencia, realizará la comprobación sucia de todos modos para detectar todos los cambios antes de ejecutar las instrucciones de actualización SQL requeridas.

You might also like

Deja una respuesta

Tu dirección de correo electrónico no será publicada.