39 votes

Comment Hibernate décide de l'ordre de mise à jour/insertion/suppression.

Oublions d'abord Hibernate. Supposons que j'ai deux tables, A et B. Deux transactions mettent à jour les mêmes enregistrements dans ces deux tables, mais txn 1 met à jour B puis A, tandis que txn 2 met à jour A puis B. C'est un exemple typique de blocage. La façon la plus courante d'éviter cela est de prédéfinir l'ordre d'acquisition des ressources. Par exemple, nous devrions mettre à jour la table A puis B.

Retournez à Hibernate. Lorsque nous mettons à jour un grand nombre d'entités dans une session, une fois que j'ai vidé la session, les modifications des différentes entités vont générer des instructions d'insertion/mise à jour/suppression correspondantes dans la base de données. Hibernate a-t-il un algorithme pour décider de l'ordre de mise à jour des entités ? Si ce n'est pas le cas, quelle est la méthode utilisée par Hibernate pour éviter la situation de blocage décrite dans le premier paragraphe ?

Si Hibernate maintient l'ordre, comment puis-je connaître ou contrôler l'ordre ? Je ne veux pas que ma mise à jour explicite dans la base de données entre en conflit avec Hibernate et provoque un blocage.

61voto

David Edwards Points 1042

Le problème que vous décrivez n'est pas géré par la base de données et, d'après mon expérience, n'est pas non plus entièrement géré par Hibernate.

Vous devez prendre des mesures explicites pour éviter que cela ne devienne un problème.

Hibernate fait une partie du travail à votre place. Comme indiqué dans la réponse précédente, Hibernate veille à ce que, dans un flux isolé, les insertions, les suppressions et les mises à jour soient ordonnées de manière à ce qu'elles soient appliquées dans un ordre réalisable. Voir performExecutions(EventSource session) dans la classe AbstractFlushingEventListener :

Exécuter toutes les commandes SQL (et les mises à jour du cache de deuxième niveau) dans un ordre particulier afin que les contraintes de clés étrangères ne soient pas violées :

  1. Insertions, dans l'ordre où elles ont été effectuées
  2. Mises à jour
  3. Suppression d'éléments de collection
  4. Insertion d'éléments de collection
  5. Suppressions, dans l'ordre où elles ont été effectuées

Lorsque vous avez des contraintes uniques, il est très important de connaître cet ordre, en particulier si vous souhaitez remplacer un enfant unique (supprimer l'ancien/insérer le nouveau) mais que l'ancien et le nouveau enfant partagent les mêmes contraintes uniques (par exemple, la même adresse électronique). Dans ce cas, vous pouvez mettre à jour l'ancienne entrée au lieu de la supprimer ou de l'insérer, ou bien vous pouvez nettoyer l'ancienne entrée après l'avoir supprimée, puis continuer à l'insérer. Pour un exemple plus détaillé, vous pouvez consulter cet article .

Notez qu'il ne précise pas l'ordre des mises à jour. L'examen du code Hibernate m'amène à penser que l'ordre de mise à jour dépendra de l'ordre dans lequel les entités ont été ajoutées au contexte de persistance, PAS dans l'ordre de leur mise à jour. Cela peut être prévisible dans votre code, mais la lecture du code d'Hibernate ne m'a pas donné l'impression que je me fierais à cet ordre.

Il y a trois solutions qui me viennent à l'esprit :

  1. Essayez de régler hibernate.order_updates être vrai . Cela devrait permettre d'éviter les blocages lorsque plusieurs lignes d'une même table sont mises à jour, mais n'aidera pas à éviter les blocages entre plusieurs tables.
  2. Faites en sorte que vos transactions prennent une PESSIMISTE_ÉCRITURE verrouiller l'une des entités avant de procéder à des mises à jour. Le choix de l'entité dépendra de votre situation spécifique, mais tant que vous vous assurez qu'une entité est choisie de manière cohérente s'il y a un risque de blocage, cela bloquera le reste de la transaction jusqu'à ce que le verrou puisse être obtenu.
  3. Écrivez votre code de manière à détecter les blocages lorsqu'ils se produisent et réessayez d'une manière raisonnable. Le composant qui gère la répétition du blocage doit être situé en dehors de la limite de la transaction en cours. En effet, la session défaillante doit être fermée et la transaction associée doit être annulée. En cet article vous trouverez un exemple d'Aspect AOP de réessai automatique.

0voto

Potejciak Points 228

En ce qui concerne le premier exemple, ce genre de choses est géré par la base de données (renseignez-vous sur les niveaux d'isolation des transactions et les stratégies de verrouillage de votre base de données). Il existe de nombreuses façons différentes de gérer ce problème.

En ce qui concerne Hibernate, la javadoc de org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(EventSource) dit :

Exécuter toutes les mises à jour SQL et les mises à jour du cache de deuxième niveau, dans un ordre particulier de manière à ce que les contraintes relatives aux clés étrangères ne soient pas violées :

  1. Insertions, dans l'ordre où elles ont été effectuées
  2. Mises à jour Suppression d'éléments de collection
  3. Insertion d'éléments de collection
  4. Suppressions, dans l'ordre où elles ont été effectuées

Je suppose qu'il s'agit de la seule optimisation des requêtes SQL exécutées par Hibernate. Le reste des problèmes est géré par la base de données.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X