"Juste une dernière chose..." marmonna Columbogue, se retournant vers l'équipe de développement déjà prête à quitter la salle de réunion. Il ajusta son imperméable froissé et plissa les yeux. "Ce bug qui fait planter l'application depuis la migration vers Spring Boot 3... quelque chose me tracasse."
L'équipe soupira collectivement. Quand le Lieutenant Columbogue de la brigade des bugs informatiques commençait avec son fameux "juste une dernière chose", l'affaire était loin d'être classée.
L'affaire du FetchGraph disparu
Tout avait commencé par une simple mise à jour... Spring Boot 2.7 arrivait en fin de support, et la migration vers Spring Boot 3 semblait être une formalité administrative.
Du moins, c'est ce que tout le monde pensait.
"Voyez-vous, ce qui m'intrigue, c'est que tout fonctionnait parfaitement avant. Et maintenant, ces quelques requêtes de base de données retournent des résultats complètement différents. C'est vraiment bizarre !", s'interroge Columbogue en se triturant les cheveux.
Il sortit de sa poche un bout de papier froissé.
"Ma femme, elle ne comprend rien à l'informatique, mais elle dit toujours qu'il faut revenir à la scène du crime et surtout au bon vieux papier-crayon."
Les premiers indices
Au fil de ses gribouillis sur une feuille de papier chiffonnée, le Lieutenant entama un monologue.
“En lançant les tests automatiques, ils réussissent presque tous.
Les seuls qui sont en erreur retournent beaucoup plus d’éléments que prévu !
Comme si on multipliait la taille de cette base de données …
Et les tests incriminés sont ceux qui font appel à un DAO bien précis…
Ah ! C’est vraiment étrange. Quand je vais dire ça à ma femme !"
S'arrêtant pour voir si nous l'écoutions toujours, Columbogue continua.
"Regardez, on peut voir ici que les méthodes concernées utilisent toutes beaucoup de jointures afin de récupérer un objet complet via la fonctionnalité FetchGraph.
On est d'accord pour dire que la taille de notre base de données ne double pas en fonction de la version de notre framework n'est-ce pas ?
Donc le coupable est forcément soit notre code, soit Spring soit Hibernate…
Reste à trouver lequel !”
L'Interrogatoire des suspects
Columbogue passa les jours suivants à interroger la documentation d'Hibernate, à fouiller dans les forums et à consulter les tickets JIRA. Il grattait constamment sa tête ébouriffée.
"Ma femme me dit toujours que je suis trop obstiné," dit-il en s'adressant à un développeur junior. "Mais voyez-vous, quand quelque chose ne colle pas, je ne peux pas lâcher l'affaire."
Il parcourut divers fils de discussion sur le forum Hibernate dont certains semblaient sans liens avec le problème initial mais qui lui permirent d’identifier un premier suspect. De plus, à toute fin utile, il créa un ticket sur le JIRA du projet Hibernate avec ses premières questions.
Mais le suspect n’était pas dupe et ne répondit jamais à ce ticket …
Il en était sûr à présent ! Le comportement de la fonctionnalité FetchGraph avait changé de manière significative entre Hibernate 5 et 6, et ce changement n'était clairement pas documenté.
Mais, comme d’habitude, le Lieutenant Columbogue allait devoir ruser pour coincer sa proie...

La reconstitution du crime
Le lendemain, le Lieutenant convoqua toute l'équipe.
"Voyez-vous, dans cette affaire, nous avons énormément d’acteurs : le changement de version de Spring Boot a entraîné une mise à jour de beaucoup de dépendances."
L'équipe opina. La dénommée "Migration Jakarta et Java 17" avait forcé à mettre à jour toute librairie qui utilisait une classe javax en entrée ou en sortie pour retourner à présent une classe jakarta. Le nombre de librairies susceptibles d’être responsables d'un changement de comportement était colossal et donnait le tournis aux développeurs junior.
"Pour résoudre cette affaire, il était nécessaire d’isoler le problème afin de nous concentrer sur les suspects principaux," déclara Columbogue. "Ainsi, j'ai créé un petit projet de test sur GitHub pour reproduire exactement le comportement suspect avec le minimum d’acteurs impliqués, à savoir seulement Hibernate."
"Comment avez-vous su directement que c'était Hibernate ?" demanda un développeur.
"Voyez-vous, lorsque j'ai exposé mon problème à plusieurs équipes, c'est la seule équipe à m'avoir répondu d'emblée qu'il n'y avait pas de problème." répondit le Lieutenant avec un sourire en coin.
Il présenta ensuite son prototype, réduit à l'essentiel pour démontrer le problème. Les développeurs regardèrent avec étonnement comment le détective avait réussi à isoler précisément le bug.
"Regardez ces deux requêtes SQL," dit Columbogue en étalant des impressions sur la table. "Celle-ci vient d'Hibernate 5, et celle-là d'Hibernate 6. Même HQL, mais le SQL généré est complètement différent! Il était donc normal de ne plus recevoir les mêmes données !"
Le détective pointa du doigt les différences:
Hibernate 5
select customer0, addresses1 (simplifié)
from customer customer0_
left outer join address addresses1_
on customer0_.CUST_STREET_ID=addresses1_.STREET_ID
and addresses1_.ZIP_CODE in (4000, 4020, 4030)
and (addresses1_.STREET_ID='avenue')
Hibernate 6
select c1_0, a2_0 (simplifié)
from customer c1_0
left join address a1_0
on c1_0.CUST_STREET_ID=a1_0.STREET_ID
and (a1_0.ZIP_CODE in (4000, 4020, 4030))
and a1_0.STREET_ID='avenue'
left join address a2_0
on c1_0.CUST_STREET_ID=a2_0.STREET_ID
and (a2_0.ZIP_CODE in (4000, 4020, 4030))
and (a2_0.ZIP_CODE in (4000, 4020, 4030))
"Vous voyez ça? Deux left joins au lieu d'un seul! Et la condition supplémentaire n'est pas appliquée au bon endroit !"
L'équipe n'en croyait pas leurs yeux. Ce bug semblait trop gros pour être vrai. Et dans une librairie aussi populaire qu'Hibernate qui plus est !
"Voyez-vous, dans Hibernate 5, quand on combinait un left join dans la requête avec le fetchgraph sur le même attribut, tout fonctionnait harmonieusement. Mais dans Hibernate 6, c'est comme si ces deux-là ne pouvaient plus cohabiter.
Le FetchGraph ne veut plus être contraint !"
Le plan d'action
"Encore une p’tite question," dit Columbogue, alors que l'équipe pensait avoir enfin compris le problème. "Vous avez une piste de comment nous allons résoudre ce problème sans perturber toute l'application?"
Devant le silence de l'équipe, il proposa une stratégie en plusieurs étapes:
- Identifier un contournement pour la fonctionnalité FetchGraph compatible avec Hibernate 6;
- L'implémenter d'abord dans Spring Boot 2.7 avec Hibernate 5;
- Tester et valider la solution;
- Procéder ensuite à la migration vers Spring Boot 3.
"C'est comme quand ma femme réorganise notre appartement," expliqua-t-il. "Elle ne déplace jamais tous les meubles en même temps. Elle commence par un coin, s'assure que tout fonctionne, puis passe au suivant."
Mais ... il restait à trouver ce fameux contournement.
La résolution
Après plusieurs semaines d'investigation et de tests, Columbogue et l'équipe trouvèrent une solution élégante: cloner les DAOs problématiques et les adapter pour qu'ils fonctionnent avec la nouvelle logique d'Hibernate 6, tout en maintenant les anciens en place jusqu'à ce que tout soit validé.
"Parfois, la solution la plus simple est la meilleure," dit Columbogue en rangeant ses notes dans sa poche d'imperméable. "Ma femme dit toujours qu'il ne faut pas jeter trop vite nos objets, elle attend d'être certaine d'avoir leurs remplaçants."
Grâce à cela, la migration vers Spring Boot 3 fut finalement complétée avec succès.
Épilogue
Alors que l'équipe célébrait la résolution de l'affaire, Columbogue s'arrêta à la porte.
"Juste une dernière chose," dit-il en se retournant. "N'oubliez pas de documenter tout ça. La prochaine fois qu'un détective devra résoudre une affaire similaire, il appréciera d'avoir le dossier complet."
Il ajusta son imperméable et disparut dans le couloir, prêt pour sa prochaine enquête dans les méandres des frameworks Java.
