20 points par carnoxen 2024-12-05 | 22 commentaires | Partager sur WhatsApp

Résumé

JPA/Hibernate est devenu un framework très utilisé parce qu’il permet de ne plus avoir à écrire de SQL dans le code Java. Pourtant, je voudrais défendre l’idée qu’il ne faut pas l’utiliser dans un nouveau projet.

Raisons

Une documentation officielle extrêmement longue

Une fois convertie en PDF, la documentation officielle fait pas moins de 406 pages, soit plus que Le Seigneur des anneaux (231 pages) ou que le document standard SQL (288 pages). Il n’est pas nécessaire de faire un master pour apprendre à interroger une base de données.

Mutabilité

  1. Même si une entité a besoin d’un élément, on lui impose un constructeur sans argument.
  2. En mettant les mots-clés final ou abstract sur une classe d’entité, il est impossible d’empêcher l’héritage.
  3. Reflection/Introspection ignore le principe d’encapsulation de l’OOP.
  4. Quelqu’un peut injecter du code malveillant et détruire totalement les données.

Chargement paresseux et cache

  1. L’annotation @Lazy est l’une des pires technologies pour les débutants. Mais il est difficile de l’éviter quand la conception du domaine ne correspond pas à Hibernate ou lorsqu’on ne peut pas écrire de requêtes.
  2. Le mécanisme de cache est difficile à comprendre. Et même une fois compris, il faut mettre en cache l’entité elle-même, pas le résultat de la requête.

Synchronisation mémoire-base de données (Flush)

La technique dite de Flush synchronise les objets stockés en mémoire avec la base de données. Cela entraîne deux problèmes.

  • Quand Flush se déclenche, les modifications en mémoire sont déjà considérées comme terminées, ce qui rend pratiquement impossible l’usage d’un autre outil de persistance que Hibernate,
  • et si un conflit survient pendant le Flush, on peut obtenir une erreur de Stack Trace sans rapport direct avec le code.

Récupérer uniquement certaines colonnes d’une table

Si l’on veut accéder à une seule colonne d’une entité, l’approche SQL est aussi simple que ceci.

select url from image   
where id = 'F462E8D9-9DF7-4A58-9112-EDE0434B4ACE';  

Mais Hibernate récupère systématiquement toutes les colonnes de l’entité. Pour l’éviter, il faut passer par un processus complexe.

Définir des contraintes sur une colonne

Pour définir des contraintes sur une colonne donnée, il faut empiler plusieurs annotations comme ci-dessous.

...  
    @NotNull  
    @NotEmpty  
    @Email  
    private String email;  
...  

Cette approche entraîne les problèmes suivants.

  • Il est impossible d’écrire des tests unitaires pour ces conditions.
  • Pendant une opération de Flush, il est trop tard pour comprendre comment ce traitement s’applique.
  • Les exceptions possibles sont génériques et peu utiles.
  • Les règles métier ne sont traitées que comme des règles techniques.

Problèmes de stratégie

  1. Les mises à jour du framework sont désastreuses, ignorent la compatibilité descendante et créent une dépendance totale. L’entreprise qui le développe finit par considérer comme normal d’utiliser son propre framework pour verrouiller le marché. Il faut arrêter ce cercle vicieux.
  2. Obtenir une preuve de concept (Proof of Concept) uniquement à travers un framework ne fera que rétrécir votre perspective, et c’est encore plus vrai dans le cas de JPA/Hibernate. Il ne faut lui accorder aucune tolérance.

Que faut-il faire ?

Écrivez du SQL

Avec SQL seul, tout est possible. Tous les programmeurs le connaissent, les requêtes sont intuitives, et aucun framework n’est nécessaire.

Mon manager me demande d’utiliser Hibernate

Démissionnez, ou travaillez à découpler le code du framework.

Si vous l’utilisez déjà...

  1. Ne rendez pas le constructeur par défaut ni les setters public.
  2. Utilisez des chaînes comme des UUID plutôt que des identifiants générés par SQL.
  3. Utilisez le nom XXXDao au lieu de XXXRepository.
  4. N’utilisez pas l’annotation @SequenceGenerator.
  5. Séparez les classes de domaine et les classes DAO avec des interface.
  6. N’utilisez pas les relations to-many (@OneToMany, etc.) et, si possible, évitez même le mapping d’entités.

Conclusion

JPA/Hibernate, abandonnez-le.

  • Il existe une documentation plus courte et meilleure pour résoudre les problèmes métier.
  • Ne vous enfermez pas dans une manière de concevoir qui va trop vite.
  • Faites preuve de bienveillance envers le prochain développeur qui devra maintenir votre code.

Qu’utilisez-vous ?

  1. JPA/Hibernate
  2. Une autre technologie ORM

22 commentaires

 
askaskm 2024-12-16

Je suis contre l’idée d’abandonner JPA/Hibernate.

« La documentation officielle très longue »
Même SQL est difficile au début. Est-ce facile de parfaitement comprendre les jointures complexes, les sous-requêtes, les fonctions de procédure, etc. ?
Pour JPA, il suffit au départ de comprendre les concepts essentiels. Pour le reste, on peut chercher plus en profondeur quand le besoin se présente.
Et il y a les LLM.

« Le problème de la mutabilité et de la Reflection »
Ce sont des inquiétudes qui viennent d’une mauvaise compréhension du fonctionnement du framework.
En pratique, il est très rare que cela cause de vrais problèmes.
Au contraire, grâce à la Reflection, le mapping d’objets est automatisé, ce qui améliore fortement la productivité.

« Chargement paresseux et cache »
@Lazy serait « la pire des technologies » ? C’est une fonctionnalité très utile pour résoudre le problème du N+1 et optimiser les performances.
Le mécanisme de cache aide au contraire beaucoup à améliorer les performances.

« Obtenir seulement certaines colonnes d’une table »
Avec JPQL ou les projections, on peut facilement récupérer uniquement les colonnes nécessaires.
Et il suffit de l’utiliser avec QueryDSL.

Je pense que l’objectif de l’ORM n’est pas de remplacer complètement SQL, mais d’aider les développeurs à se concentrer davantage sur la logique métier..

 
bbulbum 2024-12-09

Je suis plutôt pessimiste à l’égard des ORM, mais j’ai l’impression que l’article ne propose pas vraiment d’alternative suffisante.

Quand on part sur une approche très centrée ORM, ça peut vraiment devenir sans fin, et comme mentionné plus haut, on risque de se débattre dans une documentation encore plus vaste que celle du SQL jusqu’à en dépérir.

En ce moment, je développe un projet perso sans utiliser d’ORM, mais à force de réfléchir à une conception qui tienne compte de la réutilisabilité, il m’arrive de me dire qu’au fond je suis simplement en train de construire mon propre ORM, haha.

 
ilbanin00 2024-12-07

Le fait d’utiliser un framework permet toujours de partager un paradigme commun avec d’autres développeurs qui utilisent le même framework, et j’ai l’impression que ce point est systématiquement ignoré dans ce genre d’articles qui disent de ne pas utiliser ce type d’outils.

 
dothx 2024-12-06

Quand il y a beaucoup de tables et beaucoup de colonnes (par exemple 50 tables, avec plus de 100 colonnes par table), utiliser simplement du SQL devient un véritable enfer.

En revanche, je pense que mettre en place JPA/Hibernate pour construire un petit service est un énorme gaspillage.

Comme toujours, ce genre d’avis dépend vraiment des cas.

(L’exemple pris ici aussi concerne des colonnes qui ne sont que 3 ou 4...)

 
jpumpkin94 2024-12-06

Je pense que la dernière question de l’article ci-dessus devrait être légèrement reformulée.

Dans l’écosystème Java, on peut sans doute résumer le sujet en 1. ORM vs 2. non-ORM.

  1. Côté ORM, en pratique, c’est presque exclusivement la combinaison JPA/Hibernate qui est utilisée.
  2. Il y a aussi MyBatis, JOOQ, SpringDataJDBC, etc. Là, on gère généralement le SQL directement.

Les approches 1 et 2 ayant toutes deux des avantages et des inconvénients bien marqués, il n’est pas approprié d’en tirer une conclusion aussi extrême que dans l’article ci-dessus.

Dans notre cas,
nous utilisons à la fois l’ORM JPA/Hibernate/QueryDSL et MyBatis.

Nous cherchons à maximiser la productivité avec l’ORM,
et nous utilisons MyBatis pour les requêtes que l’ORM couvre difficilement.

Et quel que soit le choix entre 1 et 2 ci-dessus, il faut bien connaître le SQL.

 
carnoxen 2024-12-06

Moi aussi, j’aimerais bien le modifier, mais il n’y a pas de fonctionnalité de ce genre sur le site...

 
kallare 2024-12-06

On dirait qu’on fait semblant d’ignorer pourquoi les ORM sont devenus populaires au départ.

Il y a certes un certain coût d’apprentissage, mais une fois qu’on s’y habitue, le gain de productivité est bien réel.

Le SQL peut sembler simple, mais la fatigue de coder chaque requête une par une… Sans compter que dès qu’une table change, il faut aussi modifier toutes les requêtes liées une par une, donc la maintenance SQL est loin d’être triviale. Plus c’est petit et simple, plus la quantité de travail augmente (c’est bien pour ça que la question de la productivité revient sans cesse).

En plus, les erreurs SQL explosent au runtime et sont difficiles à attraper, et quand on commence à gérer une à une les défenses contre des attaques comme l’injection SQL, on finit de toute façon par ajouter du code qui génère les requêtes (souvent en partant d’un template simple…). Et au bout du compte, on recrée quelque chose qui ressemble à un ORM, donc autant utiliser directement un ORM, non ?

Ça me rappelle l’article publié il y a quelques jours
https://fr.news.hada.io/topic?id=17955

 
laracool 2024-12-06

Je suis d’accord.
J’ai l’impression qu’il arrive souvent qu’on ne comprenne pas pleinement pourquoi on utilise un ORM ni quels sont ses avantages.

En plus, il ne semble pas y avoir beaucoup de personnes qui essaient d’analyser ou de comprendre le SQL réellement exécuté via l’ORM.
J’aimerais qu’on fasse davantage connaître le fait qu’au-delà de la simple commodité, cela peut aussi aider à mieux comprendre l’optimisation SQL et le fonctionnement des bases de données en profondeur.

 
jamsya 2024-12-06

Je pense qu’il n’est pas nécessaire de tomber dans l’un ou l’autre extrême, du genre « il faut absolument l’utiliser » ou « il ne faut surtout pas l’utiliser » haha ;;
Pour ma part, quand j’ai besoin de productivité, j’utilise un ORM,
et pour les requêtes complexes qu’un ORM ne peut pas couvrir, ou celles qui demandent davantage d’optimisation, je les traite en requête SQL brute.
ORM ou requête brute, je pense qu’il faut simplement choisir ce qui convient selon le contexte, ce qu’on veut construire et la manière de le faire.

 
xhfleodhkd 2024-12-06

En général, je pense que cela peut se défendre pour des données bien normalisées dans la base et qui n’ont pas vraiment besoin de gros join,
mais si on ne peut pas tout gérer correctement via un DBA, en commençant par la normalisation de la base, alors un ORM peut aussi être un bon choix. En particulier, les avantages qu’on obtient quand on récupère les données via des relations plutôt que par des join montrent très bien pourquoi on en vient à utiliser un ORM.
Bien sûr, je suis d’accord avec l’idée qu’un framework peut limiter la progression du développeur, et qu’il faut réduire cette dépendance au framework,
mais j’ai du mal à adhérer à l’idée qu’il ne faudrait jamais utiliser d’ORM de manière catégorique.
J’ai l’impression que cela part du principe que toutes les entreprises ont un DBA et développent avec de vraies méthodologies comme le DDD ou le TDD,
et, dans la pratique, si on procédait réellement comme ça sur le terrain, je me demande à quel point le code pourrait devenir encore plus chaotique.

 
aer0700 2024-12-06

Quand je fais un backend en PYTHON, j’utilise à peu près toujours SQLALCHEMY ou DJANGO ORM.
À force, écrire du SQL directement ou utiliser un ORM me semble assez similaire une fois qu’on s’y est habitué, donc je n’y pensais pas particulièrement. Mais il existe aussi des avis disant qu’il ne faut pas utiliser d’ORM.
Je suis d’accord avec l’idée de réduire la dépendance au framework. Il ne faudrait pas se retrouver à ne savoir utiliser que DJANGO ORM sans être capable de manipuler le SQL...

 
beoks 2024-12-06

Hum, eh bien, je ne suis pas d’accord. J’exploite actuellement un service qui compte environ 3�000 tables, et comme le domaine est extrêmement complexe, écrire une seule requête me demandait en général plusieurs dizaines de lignes. Et si l’on ajoute les requêtes dynamiques, cela devient vraiment un casse-tête. Comme tout est complexe, il y avait aussi beaucoup de bugs et la maintenance était difficile. Je pense que, dans un domaine complexe, un ORM est plus avantageux.

 
xhfleodhkd 2024-12-06

Dans mon cas, j’ai déjà eu l’expérience de maintenir une base de données non normalisée.
À l’époque, j’écrivais les requêtes dynamiques en SQL classique sans utiliser d’ORM,
et il arrivait alors que le code devienne plus difficile à lire.
Je pense qu’il y a matière à l’adopter non seulement pour des domaines complexes, mais aussi pour des domaines insuffisamment normalisés.

 
carnoxen 2024-12-06

Oh, eh bien, il n’est peut-être pas nécessaire de voir ça d’un mauvais œil.

 
tsboard 2024-12-06

Personnellement, je recommanderais simplement d’utiliser SQL tel quel. Dans l’écosystème JS, on utilise beaucoup des outils comme Prisma, mais SQL n’est pas un langage si difficile que ça, et je suis un peu réticent face au niveau d’abstraction inutile que cela semble imposer pour les E/S de base de données.

 
znjadong 2024-12-06

J’ai aussi l’impression que c’est parce que, côté ORM en js/ts, il y a particulièrement beaucoup de produits mal fichus.

 
carnoxen 2024-12-06

Avec quelque chose comme JDBC, ça devrait suffire, non ? Ça me rappelle ce qu’une personne avec qui j’avais travaillé m’avait dit auparavant : « JPA est lent, alors utilisez autre chose. »

 
roxie 2024-12-06

On dirait une histoire sortie tout droit d’une légende.

 
caniel 2024-12-06

J’ai l’impression que la tendance est peut-être de revenir aux fondamentaux plutôt qu’aux frameworks.
HTMX, SQL, etc.

 
carnoxen 2024-12-06

Il y a l’inconvénient de devoir réinventer la roue, certes.

 
misolab 2024-12-06

Il y a aussi des avantages...
2. MyBatis (ce n’est pas un ORM, haha)

 
carnoxen 2024-12-06

J’aurais plutôt dû passer à un DAO qu’à un ORM.