| « Oracle rachèterait JBoss | Subversion# » |
Evitons le vietnam Hibernate
Un an après avoir rédigé cet article sur le mapping o/r pour DNG, force est de constater que cette approche est devenue au fil du temps le quotidien de bons nombres d'architectes ou développeurs (dont je fais partie). Il ne se passe pas un jour où je n'entends parler d'Hibernate.
Avec la multiplication des projets de cette nature, que ce soit d'ailleurs dans le monde Java ou .NET (moins en .NET car nhibernate est encore moins implanté), j'avoue être confronté aux premiers retours terrains. Et ils sont plutôt désastreux. Oui, j'ai bien dis désastreux. C’est aussi ce qui me pousse à écrire ce billet.
Rien qu'en l'espace d'une semaine, j'ai eu 4 retours (provenant de missions d’audit, collègues, amis ou SSII) démontrant les ravages du mapping (souvent avec Hibernate d'ailleurs). Il n'y a pas si longtemps, un certain Ted Neward parlait déjà du mapping o/r en ces termes : le futur Vietnam de l'informatique moderne. Sans aller jusque là, je dirais simplement que si l'on continue dans cette voie, nous risquons tous d’aller aux devant d'importantes difficultés. Avec pour principal dégât collatéral, de décrédibiliser la technologie elle-même.
Tout d'abord, soyons clairs. Dans 99,99% des cas Hibernate n'est PAS en cause. Ce qui est en cause, c'est essentiellement la manière dont les gens l'utilisent. Pour avoir fait partie des premiers à pousser cette approche aux débuts de DNG (et je continuerais car j’y crois vraiment), il faut désormais faire preuve de plus de pédagogie. Peut-être aussi avons-nous tous cédé à un moment aux sirènes du « hype » qui consiste à « vendre » l'idée avant de la « faire comprendre ». Utiliser Hibernate sans formation préalable c’est comme mettre une Ferrari dans les mains d’un jeune conducteur. Tôt ou tard, il finira par se planter. C’est un peu ce qui est en train d’arriver avec Hibernate.
En premier lieu, un outil de mapping n’est pas une technologie magique, une sorte de silver bullet qui va prendre en charge sans effort l’accès aux données. Non seulement, le mapping requiert de l’effort (donc a un coût), mais il requiert aussi et surtout de la rigueur. Je m’aperçois que ceux qui font du mapping le font généralement sans se soucier de la manière dont les requêtes sont générées. Hibernate pour oublier SQL ou pour remplacer des batchs n’est jamais la solution. Hibernate pour naviguer aveuglément dans un graphe d’objets est encore moins la solution. Car il faudra s'y faire, la persistance est tout sauf "transparente". Il suffit de jeter un œil dans toute la complexité d'un fichier de mapping pour s'en apercevoir.
A l’époque, dans cet article, j’expliquais déjà (comme d’autres) les méfaits du syndrôme N+1 Select. Et que vois-je aujourd’hui dans les projets ? Pas une requête CreateCriteria, pas une requête HQL, le tout en total eager fetching (Hibernate 2.1 est par défaut dans ce mode). A croire que personne ne met show_sql=true dans un fichier de config ou n’analyse un temps soit peu les échanges avec la base. Résultats, des requêtes SQL générées de 70 pages imprimées (véridique), parfois 300 ou 400 requêtes par formulaires et des DBA qui crient au scandale. Et je passe les mauvaises utilisations du cache de premier et second niveau ou ceux qui arrivent à faire partager une session Hibernate par 3000 utilisateurs. Hibernate c'est aussi outer-join, inverse=true ou batch-size.
Pour avoir utilisé Hibernate sur des projets, c’est un outil d’une rude efficacité. Il vous fera gagner du temps mais seulement si vous avez dès le départ intégré ses spécificités. Si vous démarrez un projet avec cet outil, assurez-vous que l’équipe de développement soit formée. La documentation est d’une richesse incommensurable (en Français et en Anglais). Il existe des centaines d’articles et de nombreux livres sur le sujet dont l’excellent Hibernate 3 d’Anthony Patricio. Des formations aussi.
Pourquoi insister autant ? Car lorsqu’une application Hibernate a été développée entièrement avec des mauvaises pratiques (couche de service conçue en syndrome n+1 select, absence de lazy fetching), même avec toute l’expertise et la bonne volonté du monde, on ne peut que constater les dégâts. La réécriture complète est malheureusement souvent la seule solution. Il n'est pas trop tard pour bien faire ...
12 commentaires
• un champ ID qui stocke l’identifiant
• un champ CONTENT qui stocke le contenu sérialisé (xml ou binaire)
et cela pour chaque « gros » objet (le jeu étant le découpage objet(s) table)
Florent.
1. hibernate et batch ne font pas bon ménage
2. au-delà des compromis entre le modèle relationnel et sa traduction dans le monde objet, il y a pour les applications web un handicap : si on veut pas attacher la session hibernate on se retrouve avec des objets détachés et là il faut bien étudier les scénarios fonctionnels manipulant beaucoup d'objets pour utiliser le lazy-loading par exemple etc ...
on est loin de la solution miracle mais hibernate est un bon outil.
Je reste assez sceptique quand je vois des moteurs SGBDO implémentés en frontal dans les frameworks, et effaré qu'on recommande de coller tout en blob avec un ID pour avoir un beau, un pur, un irréprochable modèle objet. A quoi bon utiliser une base dans ce cas ?
Il ne s'agit même pas d'impédance mismatch, juste d'une réalité. Un objet, il faut aller le chercher, le modifier, le retourner (et gérer une éventuelle exception parce que entre temps quelqu'un l'a supprimé...).
Ce n'est pas pour rien, à mon avis, que des outils comme LLBLGen proposent à côté du mode "magique" (je charge une commande, et je récupère on ne sait comment ses items, son client...) un mode "explicite" (je charge une commande, et svp donnez moi aussi ses items mais pas son client).
Le mode magique est séduisant mais très pointu à piloter correctement, parce qu'il implique de comprendre parfaitement ce qui se passe derrière (une requête par item, ou une pour tous les items de la commande...?).
Elargissons le débat: l'abstraction "tout objet" a ses limites. Et fuit un peu. Par exemple, si myOrder.Customer = myCustomer, pourquoi myCustomer.Orders ne contient pas instantanément myOrder? Si je change myCustomer.Name="toto" et puis je rollback, pourquoi je ne retrouve pas la valeur initiale? Pourquoi c'est si dûr de faire une requête sur un graphe d'objets, et si facile de faire du SQL?
Je suis assez d'accord avec ce qui a été dit, mais cela ne se limite pas à Hibernate. En effet, cela est vrai pour toutes les technologies qui masquent la complexité. Même si l'on connait bien Hibernate, mais que l'on ne connait pas le fonctionnement des bases de données alors on risque d'être confronté à de graves problèmes. Hibernate convient tout à fait à du batch, mais il faut le configurer et l'utiliser avec cette préoccupation là. Le gros problème est souvent que l'on met des "débutants" en pensant que puisque l'outil masque la complexité, alors c'est utilisable par tous. J'ai rencontré des projet qui avaient des problèmes similaires avec l'utilisation des EJB et services CORBA. Par ailleurs, les seules personnes techniques avec assez d'expérience et de recul qui seraient aptes à suivre les projets et à déceler les problèmes se reconvertissent en mangers ou administrarifs car les postes techniques ne sont malheureusement pas valorisés en France.
- de connaître SQL un minimum et de se pencher dès le début du dév sur le type de requêtes générées par l'utilisation qu'on fait de la couche OR/M (avoir conscience des outer join and co)
- de surveiller la performance et la volumétrie générée dès le début du dév (manuellement, ou en mettant en place des mesures automatiques à l'intégration)
Bref, à utiliser avec précaution, mais ça ne génère pas nécessairement des catastrophes.
Ce constat est sans doute responsable de la difficulté de la mise en oeuvre d'outil tels qu'Hibernate : l'approche mapping O/R échoue à nous abstraire complètement du modèle relationnel sous-jacent et cela a deux impacts :
– l'utilisation d'un tel outil est complexe car elle nécessite deux niveaux de compétences élevés dans deux modèles conceptuels très différents,
– corollaire à ce qui précède : l'approche ORM valorise la sur-compétence de quelques développeurs plutôt que d'officier dans une meilleure répartition des compétences, tâches et responsabilités au sein d'un projet
...
La suite de la réflexion que m'a inspiré ce billet est sur mon blog, pour ceux que cela intéresse.
Le problème ne viens pas d'hibernate il vient de la compétence des gens qui utilisent ce type de framework.
Dans l'article de Sami la dernière phrase m'a fait sursauter :
"La réécriture complète est malheureusement souvent la seule solution. Il n'est pas trop tard pour bien faire ..." !!!
En quoi une mauvaise utilisation d'hibernate entraine la réecriture ?
Dans la plupart des projets en echec que j'ai vus, l'echec était en grande partie organisationnel. Des gens avec peu de compétences qui n'arrivaient pas à capturer les requirements et à les transmettre. Une organisation des équipes calamiteuse, une communication d'autiste, et des développeurs qui se retrouvaient avec des modèles UML, sorties de l'esprit torturé de designeur/concepteur, soit disant OO.
Je pense que le gars qui pense faire de l'OO en ouvrant une application UML a un sérieux problème. Je constate que les meilleurs concepteurs OO sont des programmeurs ou des gens qui travaillent tous les jours avec des développeurs sur des problèmes concrets.
Je pense que la compétence en conception OO s'acquière sur le terrain en pratiquant le TDD et le DDD.
Bref je constate que les projets qui réussissent sont des projets qui sont organisés autour des méthodologies de type "Agile".
L'avenir est aux petites équipes de gens très compétents qui en s'appuyant sur des framework tres techniques seront capablent de livrer, très vite, de la fonctionnalité de qualité.
Mais ce n'est pas un discours que les decideurs veulent entendre car cela implique d'avoir des programmeurs très bien payés et une revalorisation de ce type de poste.
Ca me fait toujours mourir de rire quand un "Architecte" se pointe sur un projet pour poser les "fondations architecturales". Souvent c'est un gars qui ne programme plus depuis longtemps et qui se maintient difficilement à niveau en lisant des articles de vulgarisation.
Résultat des courses : généralement une architecture clé en main (genre recette de cuisine) déjà obsolète et qui tient rarement le choc lors de son utilisation par les développeurs.
Il est marrant de voir également le manque de feedback et de métriques sur les projets en général (certains objecteront que ce n'est pas très "industriel" ;)).
Bref a vouloir faire faire a des incompétents une Ferrari, avec les meilleurs outils du monde on ne produit généralement qu'une caisse a savon.
1) je participe au développement
2) je suis le projet jusqu'au bout (c'est à dire bien après sa mise en production)
Je suis choqué que ce ne soit pas partout comme ça.
La question est la suivante : dans une application lourde, (standalone, client/server, etc.), comment synchroniser élégamment les objets POJO de la couche présentation avec les objets du serveur. Un exemple tout bête : On affiche un graphe (au sens d diagramme) dans une fenêtre. Ses objets sont modifiés par d’autres clients. Comment les rafraîchir « automatiquement », « simplement » ?
Je déborde un chouille de la problématique énoncée dans l’article de Sami. Mais je pense que c’est un point important d’une architecture moderne (autre que WEB). ORM ou pas.
J’imagine plein de « machin » mais j’aimerais savoir si vous avez déjà étudié la question : la finalité du design de l’architecture, c’est quand même un petit peu ce que l’on présente à nos utilisateurs. Et j’aimerais que la rigueur des autres couches servent un peu. Voila sinon, j’ai plus de question.