L'ORM serait-il une « grosse erreur » ?
Selon un développeur, « l'ORM est un anti-pattern qui ne devrait pas exister »
Le 2014-12-24 03:31:36, par Amine Horseman, Expert éminent sénior
Il existe plusieurs design patterns utilisés dans la programmation orientée objet, et certains tellement utilisés qu’ils deviennent presque incontournables lorsqu’on veut apprendre le développement. L’Object Relationnal Mapping (ORM) est l’un de ces patterns à succès. Il permet d’accéder à une base de données relationnelle à partir d’objets. On peut le résumer comme une technique qui crée l’illusion d’une base de données orientée objet à partir d’une base de données relationnelle.
Ce modèle est implémenté dans plusieurs langages de programmation aujourd’hui à tel point qu’il devient une référence dans les livres. Toutefois, dans un article, Yegor Bugayenko, Directeur de technologie à Teamed.io, déclare que « l’ORM est un anti-pattern terrible qui viole tous les principes de la programmation orientée objet ».
Ce n’est pas la première fois que quelqu’un critique l’ORM. Le débat est long et ne date pas d'hier. On lui avait déjà reproché d’être techniquement limité, faible en performances et difficile à apprendre. Toutefois, pour Yegor Bugayenko, l'idée derrière l’ORM est fausse. Selon lui, « son invention était peut-être la deuxième grosse erreur en POO après la référence NULL ».
La raison qu’il donne est simple ; au lieu d’encapsuler l’interaction avec la base de données dans un objet et fournir par conséquent un point d'entrée unique, il fait l’inverse : il définit les routines qui permettent de communiquer avec la base dans l’objet ORM tandis que les données sont extraites « dans la chose que nous ne pouvons même pas appeler un objet ». Ce qui rend la détection des erreurs plus difficile. Mais en plus, « le SQL n’est pas caché » ce qui ajoute au programmeur la contrainte d’en connaître la syntaxe.
Figure : comparaison entre 1) technique utilisée par l'ORM, 2) technique utilisée par les SQL-speaking objects
Selon lui, la solution serait d’encapsuler le tout dans un seul et même objet (comme décrit dans la figure). Cet objet, que Yegor Bugayenko appelle « SQL-speaking object », sera en charge de toutes les opérations et permettra de cacher tous les détails de la communication avec la base de données : « Il n'y a pas de transactions, pas de sessions ou de factories. Nous ne savons même pas si ces objets sont en train de parler avec la base de données ou s’ils gardent toutes les données dans des fichiers textes ».
Dans son article, il présente quelques exemples en insistant sur le fait que les SQL-speaking objects sont plus simples et respectent de loin les principes de l’orienté objet, à l’inverse de l’ORM « qui ne devrait exister dans aucune application, que ce soit une petite application web ou un gros système pour entreprise ».
Source : DZone
Et vous ?
Êtes-vous d’accord avec l’avis de Yegor Bugayenko ? Pourquoi ?
Ce modèle est implémenté dans plusieurs langages de programmation aujourd’hui à tel point qu’il devient une référence dans les livres. Toutefois, dans un article, Yegor Bugayenko, Directeur de technologie à Teamed.io, déclare que « l’ORM est un anti-pattern terrible qui viole tous les principes de la programmation orientée objet ».
Ce n’est pas la première fois que quelqu’un critique l’ORM. Le débat est long et ne date pas d'hier. On lui avait déjà reproché d’être techniquement limité, faible en performances et difficile à apprendre. Toutefois, pour Yegor Bugayenko, l'idée derrière l’ORM est fausse. Selon lui, « son invention était peut-être la deuxième grosse erreur en POO après la référence NULL ».
La raison qu’il donne est simple ; au lieu d’encapsuler l’interaction avec la base de données dans un objet et fournir par conséquent un point d'entrée unique, il fait l’inverse : il définit les routines qui permettent de communiquer avec la base dans l’objet ORM tandis que les données sont extraites « dans la chose que nous ne pouvons même pas appeler un objet ». Ce qui rend la détection des erreurs plus difficile. Mais en plus, « le SQL n’est pas caché » ce qui ajoute au programmeur la contrainte d’en connaître la syntaxe.
Figure : comparaison entre 1) technique utilisée par l'ORM, 2) technique utilisée par les SQL-speaking objects
Selon lui, la solution serait d’encapsuler le tout dans un seul et même objet (comme décrit dans la figure). Cet objet, que Yegor Bugayenko appelle « SQL-speaking object », sera en charge de toutes les opérations et permettra de cacher tous les détails de la communication avec la base de données : « Il n'y a pas de transactions, pas de sessions ou de factories. Nous ne savons même pas si ces objets sont en train de parler avec la base de données ou s’ils gardent toutes les données dans des fichiers textes ».
Dans son article, il présente quelques exemples en insistant sur le fait que les SQL-speaking objects sont plus simples et respectent de loin les principes de l’orienté objet, à l’inverse de l’ORM « qui ne devrait exister dans aucune application, que ce soit une petite application web ou un gros système pour entreprise ».
Source : DZone
Et vous ?
-
Logan MauzaizeRédacteur/ModérateurGénial, il propose l'idée de tout recoder à la main ?
Je vois rien dans ce qu'il propose qui est une réelle différence avec le système actuel à part que le sien est complétement "figé" et lie complétement l'interface avec son interface de communication avec la base de données. Mais attendez .... ca ressemblerait pas à JPA avec les annotations ?
Franchement quit à faire du SQL, autant partir sur MyBatis. Apache Cayenne me semble également intéressant mais je n'ai pas eu l'occasion de le mettre en pratique.
Bon lisons l'article, "ne jugeons pas un livre à sa couverture" m'a-t-on dit.Envoyé par Yegor Bugayenko Envoyé par Yegor Bugayenko
Sinon il y a les applications Forms avec des procédures stockées. On serait presque à se demander pourquoi les Forms ne sont pas aussi populaires ...Envoyé par Yegor Bugayenko Envoyé par Yegor Bugayenko Envoyé par Yegor Bugayenko
En alternative, il nous propose l'encapsulation dans un "Callable" comme si les transactions étaient nécessairement limité par appel d'un simple morceau de code quand parfois, une orchestration entre plusieurs systèmes ou délimité par un contexte (ex : une requête) est nécessaire.
Bon l'article est bouclé et revenons aux problèmes posés par les ORMs.Envoyé par Yegor Bugayenko Envoyé par Yegor Bugayenko
Vous souhaitez ajouter le support d'une nouvelle base de données ? Il suffit de dupliquer toutes les classes existantes ...Envoyé par Yegor Bugayenko Envoyé par Yegor Bugayenko des requêtes. C'est sûr que les ORMs ont de gros problèmes de performance ...
Et quid de la performance de l'écriture d'une telle application ? A vu de nez, je dirais qu'en une semaine vous devriez avoir les "bases" d'un Pet Clinic fonctionnant sur unique SGBD. Choisissez bien dès le départ car pour en changer il va falloir revoir une bonne partie de vos classes.
Voici une petite liste de petites contraintes que je retiens (je vous laisse proposer votre propre liste):
- Aucune cohérence assurée entre la transaction et le modèle mémoire.
- Aucune possibilité d'extension sur la façon de travailler avec un concept.
- Ajouter un champ nécessitera d'impacter au minimum 4-5 classes.
- Aucune piste pour gérer des mises à jour et les relations.
Quid des griefs contre les ORMs:
- De mauvaises performances : le seul travaille de l'ORM c'est le mapping, et lui sera toujours nécessaire. Le reste est entre les mains de l'utilisateur avec une facilité pour les choses simples et des points d'extensions plus ou moins complexes pour les besoins spécifiques.
- Non-encapsulation totale du SQL : on travaille avec un SGBD et il faudra toujours faire avec. Les concepts sous-jacents s'appliquent également aux couches supérieures. Par ailleurs, je ne vois pas l'ORM comme un encapsulateur du relationnel mais comme un facilitateur pour joindre le monde relationnel et l'objet. Ceux qui le vendent comme une encapsulation sont d'aussi mauvais vendeur que notre auteur.
Au final la vraie question, que veut-il encore nous vendre ? http://jdbc.jcabi.com/
Si vous souhaitez vraiment une solution simple et efficace, je vous propose DbOom
PS : Moi, je ne touche aucune rétro-commission surla ventel'utilisation des solutions que je vous propose.le 24/12/2014 à 12:08 -
Pierre Louis ChevalierExpert éminent séniorCe qui est un troll pour certain est peut être un sujet intéressants pour d'autres, c'est une notion totalement subjective, en tout cas cela peu lancer un débat intéressant, le troll qui à lancé le débat en somme on s'en fou, c'est le débat qui suis qui est intéressant à lire.
Je te signale que developpez est un hébergeur et un club, donc si tu penses être capable de lancer des discussions plus intéressantes que ça pourquoi ne pas le faire, tu peu écrire des news, des billets blog et des articles sur developpez.com, fait toi plaisirle 28/12/2014 à 2:01 -
GugelhupfModérateurJe n'ai pas tout à fait compris l'intérêt de l'article publié par Bugayengo.
Certes les ORM (je pense à l'approche JPA en particulier) :
- apportent une surcouche qui ralentit le programme.
- génèrent des requêtes SQL parfois aberrantes quand on observe au niveau débug.
- ont pas mal de spécificités (notamment la gestion des transactions, quoi que... en activant le mode JTA on peut se passer des transactions)
- ne sont pas adaptés pour créer des batchs
- ne sont pas adaptés pour écrire des requêtes SELECT complexes
... mais pour une application web de gestion ou un blog par exemple, ils simplifient grandement l'écriture du code pour des opérations de types INSERT/UPDATE/DELETE et les SELECT simples.
L'auteur crée la méthode suivante pour insérer un objet de type Post dans sa base :
Code : 1
2
3
4
5
6
7
8
9
10public Post add(Date date, String title) { return new PgPost( this.dbase, new JdbcSession(this.dbase) .sql("INSERT INTO post (date, title) VALUES (?, ?)") .set(new Utc(date)) .set(title) .insert(new SingleOutcome<Integer>(Integer.class)) ); }
Code : em.persist(monPgPost);
L'auteur crée la méthode suivante qui retourne un iterable pour récupérer tous les objets de type Post dans sa base :
Code : 1
2
3
4
5
6
7
8
9
10
11
12
13
14public Iterable<Post> iterate() { return new JdbcSession(this.dbase) .sql("SELECT id FROM post") .select( new ListOutcome<Post>( new ListOutcome.Mapping<Post>() { @Override public Post map(final ResultSet rset) { return new PgPost(rset.getInteger(1)); } } ) ); }
Code : Post post = em.find(Post.class, monId);
le 24/12/2014 à 10:30 -
dfiad77proMembre expérimentéUn ORM (je pense à Entity Framework que je connais mieux)
peut aussi être utilisé conjointement avec d'autre méthodes ( proc stockée, etc.).
On est même pas obliger d'utiliser la persistance tout le temps.
J'ai de nombreux projets à flux importants qui utilisent un ORM et ne sont pas lents...
Cela dit un ORM nécessite beaucoup d'expérience pour être utilisé de manière optimale dans une architecture complexe.
Sa simplicité apparente est un leurre.
Dire qu'une Techno est Anti-Patern, sans proposer mieux, c'est l'excuse à la mode
( je l'entends souvent de la part des dev Java à propos de C# sur les méthodes d'extension et Linq) le 24/12/2014 à 11:45 -
OButterlinModérateurIl ne faut pas mettre au même niveau la transaction et l'ORM, ce sont 2 choses différentes...
On parle uniquement de l'aspect "vue sous forme objet" d'une (ou plusieurs) table(s) relationnelles.
De mon point de vue, l'ORM à surtout le gros défaut d'être utilisé n'importe comment dans la plupart des cas.
Quand on voit le nombre de sous-entités générées automatiquement (parce qu'on a une contrainte d'intégrité référentielle), ça frôle le délire.
Pour un peu, en accédant à une entité, on pourrait être amené à charger un graphe complet d'objets de la base de données...
Alors oui, ils ont rajouté le lazy loading pour éviter ça, mais en pratique, on ne sait plus comment paramétrer les requêtes pour les optimiser (et c'est même impossible lorsqu'on mixe du EAGER avec du [left] join fetch).
L'expérience me fait dire qu'il faudrait gérer les entités et leur relations entre elles juste d'un point de vue "Métier" et non pas d'un point de vue "Vue". En d'autre terme, si une facture à n lignes, alors oui, on peut créer un Set de lignes et persister le tout via la facture parce que le tout dépend de la facture. A contrario, il est (plutôt) mal venu de mapper l'article de la ligne juste parce qu'on en a besoin dans la vue. L'article a sa vie propre et ne dépend pas de la ligne. Lié à l'article, dans certains cas, on aura besoin de connaître le stock et la cascade commence
Je vais certainement en faire bondir certains mais pour moi, le pattern DTO est bien plus efficace que le tout ORM.
Et là, on peut mettre n'importe quoi derrière, de l'ORM ou du SQL natif... ou les 2 pourquoi pas...
Par avance, toutes mes excuses aux dogmatiques et gourou de l'ORM, je l'utilise, mais il ne faut pas non plus prétendre que c'est la panacée
Et si l'argument est "on n'a pas besoin de connaître SQL" alors qu'on manipule des tables de base de données relationnelles, le mieux serait peut-être de changer de métierle 24/12/2014 à 15:07 -
MingolitoMembre extrêmement actifAu cas ou tu l'aurais pas compris "l'auteur" de ce billet n'est pas sur le forum, il y à juste eu une personne qui à relayé ce troll pour lancer un débat, et qui semble ne pas participer au débat non plus. Bref tu chie dans le vide.
Ce qui est dommage c'est que justement tu ne semble pas avoir compris l'intérêt d'un forum, à savoir de pouvoir débattre entre gentleman et échanger des idées et des témoignages, alors que apparement tu penses que un forum est un WC qui te sert à faire caca sur tous le monde. Après tout si ça peu te défouler amuse toi...le 29/12/2014 à 0:06 -
santana2006Membre régulierLa meilleure solution étant de faire le rollback de tout et de revenir au langage assembleur tout court.
A force de creuser dans cette question de pattern, nous deviendrons nous même anti-patterns
N'oublier pas qu'un pattern est là pour faciliter et cadrer les dévs, c'est aussi la vocation des frameworks de dév, entre autres, les ORMs.
Un pattern est là pour résoudre un problème, de manière correcte/standard/maintenable, tout comme un ORM. pour ce dernier le problème étant de détacher le monde OO du monde relationnel, qui est pour moi plus crucial que penser à la manière dont les trucs collaborent dans un niveau plus bas, que le niveau qui m'intéresse réellement, et qui est au final la couche fonctionnel propre au métier que je suis entrain d'implémenter.
Aussi, c'est parfois pas la technologie X qui est anti-pattern, même l'usage et la manière d'intégrer et d'utiliser cette technologie X qui est anti-pattern.le 25/12/2014 à 10:12 -
_skipExpert éminentEn fait tous les projets récents sur lesquels j'ai bossé utilisaient à un moment ou à un autre des DTO. Pouvoir mapper des tables BDD vers des classes java c'est bien, mais essayer de faire de ces classes des entités absolument omnipotentes dans lesquels on implémente des logiques métiers poussées, de la grosse validation, puis qu'on édite directement dans une vue grâce à du databinding, je n'y crois pas trop. C'est là généralement qu'on se prend en pleine gueule les problèmes de lazy loading, de merging d'ancienne entité dans des nouvelles sessions, de delete-orphans qui marchent pas comme on le voudrait etc.... Perso, je trouve que laisser un objet JPA/hibernate vivre plus longtemps que sa session, c'est une erreur (pourtant il y a des bouquins qui disent tout le contraire!).
En ce sens, je trouve parfaitement raisonnable de créer des DTOs, soit de vulgaires tas de getter setter qui correspondent à la vue, aux champs à remplir par l'utilisateur, et de les populer avec une couche ORM. Cela rend la communication entre les controlleurs et les interfaces graphiques facile à lire et à comprendre, les données à disposition correspondent bien à ce qui est affiché ou édité et on se retrouve pas avec trop de logique dans les vues. C'est d'ailleurs de plus en plus un passage obligé dans le développement web avec les frameworks javascripts qui consomment et renvoient du JSON, un pojo simple, il suffit de le passer un coup dans Jackson. A contrario, faire ça avec des objets qui ont des références lazy loadées ou des éléments non sérialisables, c'est tout de suite la porte ouverte aux ennuis.
Donc c'est clair, personne n'aime copier des données dans un DTO, c'est juste que c'est souvent indispensable sous peine de s'exposer à d'autres soucis nettement plus graves.le 25/12/2014 à 12:40 -
Pierre Louis ChevalierExpert éminent séniorTu as peut être pas tord mais dans ce cas tu peu jeter à la poubelle 99% de la presse gratuite web, donc tu peu jeter developpez à la poubelle vu que c'est gratuit.
Tu crois que developpez à les moyens de payer 50 journalistes qui coûtent 10 000 euros par tête avec charges ?
La personne qui à posté la news n'à pas dit que cette "opinion" était bonne ou exacte, la personne te demande ton avis justement.
Comme tu ne sembles pas avoir compris dois je en conclure sur des faits que tu ne sait pas lire ?
Si tu es si fort fait en des news, avec des sources et tout et tout, on les lira avec plaisir.le 28/12/2014 à 18:25 -
Logan MauzaizeRédacteur/ModérateurIl est vrai que l'article est d'une grosse daube. Il n'y a qu'à voir les commentaires en bas de page. Par curiosité, j'ai été feuilleté le blog du monsieur et les idées (et les commentaires qui vont avec) sont tous du même acabit. Mais au-délà de la qualité de l'article, la question à le droit d'être posée ici.
Et je pense, comme tu le dis toi-même : Et tu en fais partie. L'article est mauvais, ce n'est pas le sujet. Mais il soulève une question de fond, libre à toi de donner ton avis sur le fond (et non sur la forme).
Après juste pour recentrer l'idée de départ, il n'est pas question de remettre en cause le principe d'ORM mais la notion de "framework ORM".
Excellent conseil. Après je ne sais pas s'il est possible de vraiment "mapper" la base de données ou si on est obligé de rester très proche du modèle de la base ?
Sinon il y a également QueryDSL qui fonctionne également avec JPA et MongoDB et autres. C'est une sorte d'EntityFramework.
Concernant les DTOs, il sont quasiment obligatoires dès que l'on travaille avec des bases "legacy" ou plus généralement des bases relationnelles qui n'ont pas été conçues pour stocker de l'objet. Et comme les cours de conception de BDD relationnel ne présentent jamais cet aspect, je pense que les DTOs ont encore de beaux jours devant eux.
Ensuite il y a le problème des "vues" (différentes restitutions et manipulations des entités). Et les DTOs résolvent souvent très simplement ce problème. De plus, je trouve que l'approche de mapping par annotation force à considérer les entités comme des objets de la base de données.
Pour rester dans le ton des fêtes, je trouve que le foie gras et le chocolat ne se marient pas bien. Mais cela ne remet pas en cause l'appréciation que j'ai de chacun séparément.
Pour faire plus pragmatique, on peut comparer une vis et un marteau.
Il y a de nombreuses autres façons de stocker les données d'un objet : relationnel, document, big table, graphe, BD orientée objet. Mais chacune nécessite des concessions.
En fait le problème est plus large que l'objet. Stocker/Restituer de l'information structurée est un problème en soit. Et notre métier c'est de trouver une solution convenable.
A côté des NPE on peut rajouter les ClassCast, les overflow, les erreurs de signes et autres. En fait dès que l'on fait une supposition/hypothèse sur la nature de la donnée, on fait une erreur. C'est pourquoi j'apprécie énormément Ada et Ceylon(il y en a surement d'autres).
Concernant "Optional", je trouve que c'est une mauvaise idée car en son absence on est sûr de rien et avec, on ne sait pas si c'est nécessairement non-null.
Rien à voir avec l'ORM mais uniquement avec la conception de ta couche d'accès aux données. Bien sûr des solutions comme Hibernate offre des services pour faciliter tout cela. Mais rien ne t'empêche de concevoir une application non-portable avec Hibernate, ou même une application portable avec du pure JDBC. C'est d'ailleurs l'un des buts de JDBC : offrir une interface de communication unifiée pour tous les SGBDR.
Après dans les détails on remarque que ce n'est pas si simple entre un support partielle de JDBC et les bugs des Driver (côté client) et les différences de syntaxes/types de données des SGBDR (côté serveur), il faut souvent se limiter à quelques vendeurs.le 29/12/2014 à 12:33