Uso correcto de flush () en JPA / Hibernate

110

Estaba recopilando información sobre el método flush (), pero no tengo muy claro cuándo usarlo y cómo usarlo correctamente. Por lo que leí, tengo entendido que el contenido del contexto de persistencia se sincronizará con la base de datos, es decir, emitiendo declaraciones pendientes o actualizando los datos de la entidad.

Ahora tengo el siguiente escenario con dos entidades Ay B(en una relación uno a uno, pero no impuesta ni modelada por JPA). Atiene un PK compuesto, que se configura manualmente, y también tiene un campo de IDENTIDAD generado automáticamente recordId. Esto recordIddebe escribirse en la entidad Bcomo una clave externa para A. Estoy ahorrando Ay Ben una sola transacción. El problema es que el valor generada automáticamente A.recordIdno está disponible dentro de la transacción, a menos que puedo hacer una llamada explícita de em.flush()después de llamar em.persist()a A. (Si tengo un IDENTITY PK generado automáticamente, entonces el valor se actualiza directamente en la entidad, pero ese no es el caso aquí).

¿Puede em.flush()causar algún daño al usarlo dentro de una transacción?

MicSim
fuente

Respuestas:

148

Probablemente los detalles exactos de em.flush()dependen de la implementación. De todos modos, en general, los proveedores de JPA como Hibernate pueden almacenar en caché las instrucciones SQL que se supone que deben enviar a la base de datos, a menudo hasta que usted realmente confirma la transacción. Por ejemplo, llama em.persist(), Hibernate recuerda que tiene que hacer una INSERT en la base de datos, pero en realidad no ejecuta la instrucción hasta que confirma la transacción. Afaik, esto se hace principalmente por razones de rendimiento.

En algunos casos, de todos modos, desea que las instrucciones SQL se ejecuten inmediatamente; generalmente cuando necesita el resultado de algunos efectos secundarios, como una clave autogenerada o un activador de base de datos.

Lo que em.flush()hace es vaciar la caché interna de instrucciones SQL y ejecutarla inmediatamente en la base de datos.

En pocas palabras: no se hace daño, solo usted podría tener un impacto (menor) en el rendimiento, ya que está anulando las decisiones del proveedor de JPA con respecto al mejor momento para enviar instrucciones SQL a la base de datos.

Flavio
fuente
1
Lo que dijo. El comportamiento de em.flush () se hace eco de java.io.Flushable.flush () donde todos los datos almacenados en búfer se envían a cualquier destino apropiado.
Erik
4
si flush () envía datos a la base de datos? ¿Qué sucede si se lanza una excepción después de eso? ¿El administrador de la entidad revertirá todo? incluso los datos escritos en la primera descarga?
Jaime Hablutzel
101
flush () envía instrucciones SQL a la base de datos como INSERT, UPDATE, etc. No enviará COMMIT, por lo que si tiene una excepción después de flush (), aún puede tener una reversión completa.
Flavio
17
Puede revertir la base de datos, pero no revertirá ningún cambio en los objetos, por ejemplo, 'versión' autoincrementada, ID autogenerado, etc. Además, el administrador de entidades se cerrará después de una reversión. Solo tenga en cuenta que si intenta 'fusionar' el objeto con otra sesión, la 'versión' autoincrementada en particular puede causar una OptimisticLockException.
Peter Davis
11
Además de desencadenar efectos secundarios, otra razón para usar flush () es si desea poder leer los efectos de una operación en la base de datos usando JPQL / HQL (por ejemplo, en una prueba). JPA no puede usar datos almacenados en caché al ejecutar estas consultas, por lo que solo se leerán las cosas que están realmente en la base de datos.
sábado
2

En realidad, em.flush()haga algo más que enviar los comandos SQL almacenados en caché. Intenta sincronizar el contexto de persistencia con la base de datos subyacente. Puede consumir mucho tiempo en sus procesos si su caché contiene colecciones para sincronizar.

Precaución al usarlo.

Ricardo Nakashima
fuente
1

¿Em.flush () puede causar algún daño al usarlo dentro de una transacción?

Sí, puede mantener bloqueos en la base de datos durante más tiempo de lo necesario.

Generalmente, cuando usa JPA, delega la administración de transacciones al contenedor (también conocido como CMT, usando la anotación @Transactional en los métodos comerciales), lo que significa que una transacción se inicia automáticamente al ingresar al método y se confirma / revierte al final. Si deja que EntityManager maneje la sincronización de la base de datos, la ejecución de las declaraciones SQL solo se activará justo antes de la confirmación, lo que provocará bloqueos de corta duración en la base de datos. De lo contrario, las operaciones de escritura de vaciado manual pueden retener bloqueos entre el vaciado manual y el compromiso automático, lo que puede ser largo según el tiempo restante de ejecución del método.

Observa que algunas operaciones desencadenan automáticamente un vaciado: ejecutar una consulta nativa contra la misma sesión (el estado de EM debe vaciarse para que sea accesible por la consulta SQL), insertando entidades usando el ID generado nativo (generado por la base de datos, por lo que la declaración de inserción debe ser desencadenado por lo que el EM es capaz de recuperar la identificación generada y gestionar adecuadamente las relaciones)

Charla
fuente