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 :
- Insertions, dans l'ordre où elles ont été effectuées
- Mises à jour
- Suppression d'éléments de collection
- Insertion d'éléments de collection
- 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 :
- 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.
- 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.
- É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.