Utilisation correcte de flush () dans JPA / Hibernate

111

Je rassemblais des informations sur la méthode flush (), mais je ne sais pas très bien quand l'utiliser et comment l'utiliser correctement. D'après ce que j'ai lu, je crois comprendre que le contenu du contexte de persistance sera synchronisé avec la base de données, c'est-à-dire en émettant des déclarations en suspens ou en actualisant les données d'entité.

Maintenant, j'ai le scénario suivant avec deux entités Aet B(dans une relation un-à-un, mais non appliquée ou modélisée par JPA). Aa un PK composite, qui est défini manuellement, et a également un champ IDENTITY généré automatiquement recordId. Cela recordIddoit être écrit dans l'entité en Btant que clé étrangère de A. J'économise Aet Ben une seule transaction. Le problème est que la valeur générée automatiquement A.recordIdne sont pas disponibles dans la transaction, à moins que je fais un appel explicite em.flush()après avoir appelé em.persist()le A. (Si j'ai un IDENTITY PK généré automatiquement, la valeur est directement mise à jour dans l'entité, mais ce n'est pas le cas ici.)

Peut-il em.flush()causer des dommages lors de son utilisation dans une transaction?

MicSim
la source

Réponses:

150

Les détails exacts em.flush()dépendent probablement de la mise en œuvre. En général, de toute façon, les fournisseurs JPA comme Hibernate peuvent mettre en cache les instructions SQL qu'ils sont censés envoyer à la base de données, souvent jusqu'à ce que vous validiez réellement la transaction. Par exemple, vous appelez em.persist(), Hibernate se souvient qu'il doit créer une base de données INSERT, mais n'exécute pas réellement l'instruction tant que vous ne validez pas la transaction. Afaik, cela se fait principalement pour des raisons de performances.

Dans certains cas, vous voulez en tout cas que les instructions SQL soient exécutées immédiatement; généralement lorsque vous avez besoin du résultat de certains effets secondaires, comme une clé générée automatiquement ou un déclencheur de base de données.

Ce qui em.flush()fait est de vider le cache des instructions SQL internes et de l'exécuter immédiatement dans la base de données.

Bottom line: aucun mal n'est fait, seulement vous pourriez avoir un impact (mineur) sur les performances puisque vous remplacez les décisions du fournisseur JPA en ce qui concerne le meilleur moment pour envoyer des instructions SQL à la base de données.

Flavio
la source
1
Ce qu'il a dit. Le comportement de em.flush () fait écho à java.io.Flushable.flush () où toutes les données mises en mémoire tampon sont envoyées à la destination appropriée.
Erik
4
si flush () envoie des données à la base de données? que se passe-t-il si une exception est levée après cela? Le gestionnaire d'entités annulera-t-il tout? même les données écrites dans le premier flush?
Jaime Hablutzel
101
flush () envoie des instructions SQL à la base de données comme INSERT, UPDATE etc. Il n'enverra pas de COMMIT, donc si vous avez une exception après un flush (), vous pouvez toujours avoir une restauration complète.
Flavio
17
Vous pouvez restaurer la base de données, mais cela n'annulera aucune modification des objets, par exemple, «version» auto-incrémentée, ID généré automatiquement, etc. De plus, le gestionnaire d'entités sera fermé après une annulation. Sachez simplement que si vous essayez de «fusionner» l'objet avec une autre session, la «version» auto-incrémentée en particulier peut provoquer une exception OptimisticLockException.
Peter Davis
11
Outre le déclenchement d'effets secondaires, une autre raison d'utiliser flush () est si vous voulez pouvoir lire les effets d'une opération dans la base de données en utilisant JPQL / HQL (par exemple dans un test). JPA ne peut pas utiliser les données mises en cache lors de l'exécution de ces requêtes, donc seuls les éléments qui se trouvent réellement dans la base de données seront lus.
sleske
2

En fait, em.flush()faites plus que simplement envoyer les commandes SQL mises en cache. Il essaie de synchroniser le contexte de persistance avec la base de données sous-jacente. Cela peut entraîner une consommation de temps importante sur vos processus si votre cache contient des collections à synchroniser.

Attention à l'utiliser.

Ricardo Nakashima
la source
1

Em.flush () peut-il causer des dommages lors de son utilisation dans une transaction?

Oui, il peut contenir des verrous dans la base de données pendant une durée plus longue que nécessaire.

Généralement, lorsque vous utilisez JPA, vous déléguez la gestion des transactions au conteneur (alias CMT - en utilisant l'annotation @Transactional sur les méthodes commerciales), ce qui signifie qu'une transaction est automatiquement lancée lors de la saisie de la méthode et validée / annulée à la fin. Si vous laissez EntityManager gérer la synchronisation de la base de données, l'exécution des instructions SQL ne sera déclenchée que juste avant la validation, entraînant des verrous de courte durée dans la base de données. Sinon, vos opérations d'écriture vidées manuellement peuvent conserver des verrous entre le vidage manuel et la validation automatique qui peuvent être longs en fonction du temps d'exécution de la méthode restant.

Note qu'une opération déclenche automatiquement un vidage: exécution d'une requête native sur la même session (l'état EM doit être vidangé pour être accessible par la requête SQL), insertion d'entités à l'aide de l'ID généré natif (généré par la base de données, l'instruction d'insertion doit donc être déclenché ainsi l'EM est capable de récupérer l'id généré et de gérer correctement les relations)

Gab
la source