38 votes

ORM pour clojure ?

Je lisais ce site sur la pile web Clojure :

http://brehaut.net/blog/2011/ring_introduction

et il a ceci à dire sur ORM pour clojure :

"Pour des raisons évidentes, il n'y a pas d'ORM SQL/base de données relationnelle pour Clojure."

La raison évidente que je vois est que le mappage en objet se fait automatiquement lorsque vous faites une requête clojure.contrib.sql ou clojureql. Cependant, il semble qu'un peu de travail supplémentaire soit nécessaire pour faire des relations un-à-plusieurs ou plusieurs-à-plusieurs (bien que peut-être pas trop de travail).

J'ai trouvé ce texte sur le one-to-many : http://briancarper.net/blog/493/

Je ne suis pas sûr d'être d'accord avec cela ; cela semble supposer que les deux tables sont extraites de la base de données et que la table jointe est filtrée en mémoire. En pratique, je pense que la requête SQL devrait spécifier le critère "where".

Je me demande donc s'il existe un moyen assez évident de créer automatiquement des relations de type "un vers plusieurs" via clojureql ou clojure.contrib.sql ? La seule chose à laquelle je peux penser est quelque chose comme ceci (en utilisant l'exemple typique d'un billet de blog/commentaire) :

(defn post [id] 
    @(-> (table :posts)
        (select (where :id id))))
(defn comments [post_id]
    @(-> (table :comments) 
         (select (where :post_id post_id))))
(defn post-and-comments [id]
    (assoc (post id) :comments (comments id)))

Y a-t-il un moyen d'automatiser ce concept ou est-ce aussi bon qu'il l'est ?

29voto

Kevin Points 10204

J'ai posé cette question il y a un certain temps, mais je suis tombé sur ce qui suit et j'ai décidé de l'ajouter comme réponse au cas où cela intéresserait quelqu'un :

http://sqlkorma.com/

29voto

levand Points 4020

La raison "évidente" pour laquelle vous n'avez pas besoin d'ORM en tant que tel dans Clojure est que Clojure idiomatique n'a pas d'objets, en soi.

La meilleure façon de représenter les données dans un programme Clojure est sous forme de séquences paresseuses de structures de données simples (cartes et vecteurs). Le mappage de ces données en lignes SQL est beaucoup moins complexe, et présente beaucoup moins de déséquilibre d'impédance, qu'un ORM complet.

De plus, en ce qui concerne la partie de votre question relative à la formation d'une requête SQL complexe... à la lecture de votre code, il n'y a pas vraiment d'avantages clairs par rapport à SQL lui-même. N'ayez pas peur de SQL ! Il est excellent pour ce qu'il fait : la manipulation de données relationnelles.

10voto

Nicolas Buduroi Points 2558

À ma connaissance, il n'existe toujours pas de bibliothèque de haut niveau permettant de créer des requêtes relationnelles complexes. Il y a de nombreuses façons de s'attaquer à ce problème (le lien que vous avez fourni en est une) mais même si ClojureQL fournit un DSL très agréable sur lequel vous pouvez vous baser, il lui manque encore certaines fonctionnalités importantes. Voici un exemple rapide d'une macro qui génère des jointures imbriquées :

(defn parent-id [parent]
  (let [id (str (apply str (butlast (name parent))) "_id")]
    (keyword (str (name parent) "." id))))

(defn child-id [parent child]
  (let [parent (apply str (butlast (name parent)))]
    (keyword (str (name child) "."  parent "_id"))))

(defn join-on [query parent child]
  `(join ~(or query `(table ~parent)) (table ~child)
         (where
          (~'= ~(parent-id parent)
               ~(child-id parent child)))))

(defn zip [a b] (map #(vector %1 %2) a b))

(defmacro include [parent & tables]
  (let [pairs (zip (conj tables parent) tables)]
    (reduce (fn [query [parent child]] (join-on query parent child)) nil pairs)))

Avec cela, vous pouvez faire (include :users :posts :comments) et d'en tirer ce SQL :

SELECT users.*,posts.*,comments.*
  FROM users
  JOIN posts ON (users.user_id = posts.user_id)
  JOIN comments ON (posts.post_id = comments.post_id)

Il y a cependant un problème majeur avec cette technique. Le problème principal est que les colonnes retournées pour toutes les tables seront regroupées dans la même map. Comme les noms des colonnes ne peuvent pas être qualifiés automatiquement, cela ne fonctionnera pas s'il y a une colonne portant le même nom dans différentes tables. Cela vous empêchera également de regrouper les résultats sans avoir accès au schéma. Je ne pense pas qu'il y ait un moyen de contourner le fait de ne pas connaître le schéma de la base de données pour ce genre de choses, donc il y a encore beaucoup de travail à faire. Je pense que ClojureQL restera toujours une bibliothèque de bas niveau, vous devrez donc attendre qu'une autre bibliothèque de plus haut niveau existe ou créer la vôtre.

Pour créer une telle bibliothèque, vous pouvez toujours jeter un œil à la classe DatabaseMetaData de JDBC pour fournir des informations sur le schéma de la base de données. Je travaille toujours sur un analyseur de base de données pour Lobos qui l'utilisent (et quelques trucs personnalisés) mais je suis encore loin de commencer à travailler sur les requêtes SQL, que je pourrais ajouter dans la version 2.0.

6voto

Henry Florence Points 1833

Au risque de nager dans les eaux avec certains des gros bonnets de l'OS (pour mélanger mes métaphores, à fond ;) - l'une des meilleures caractéristiques de l'ORM est certainement que, dans la grande majorité des cas, le programmeur pragmatique doit jamais utiliser ou même penser à SQL. Au pire, il peut s'avérer nécessaire de faire un peu de programmation bricolée avec les résultats de quelques requêtes, en sachant que cela sera converti en SQL brut lorsque l'optimisation sera nécessaire, bien sûr ;).

Dire que l'ORM n'est pas nécessaire pour la raison "évidente", c'est passer à côté de l'essentiel. De plus, commencer à utiliser un DSL pour modéliser SQL ne fait qu'aggraver cette erreur. Dans la grande majorité des frameworks web, le modèle objet est un DSL utilisé pour décrire les données stockées par l'application web, et SQL le langage déclaratif nécessaire pour communiquer ces données à la base de données.

Ordre des étapes lors de l'utilisation de ROR, django ou Spring :

  1. Décrire vos modèles dans un format OOP
  2. Ouvrir REPL et créer quelques modèles d'exemple
  3. Construire des vues
  4. Vérifier les résultats dans le navigateur web

Ok, vous pouvez utiliser un ordre légèrement différent, mais j'espère que vous avez compris. Penser en SQL ou en un DSL qui le décrit est un long chemin à parcourir. Au lieu de cela, la couche modèle fait abstraction de tout le SQL, ce qui nous permet de créer des objets de données qui modélisent étroitement les données que nous souhaitons utiliser sur le site Web.

Je suis tout à fait d'accord sur le fait que la POO n'est pas une solution miracle, cependant, la modélisation des données dans un cadre web est quelque chose pour laquelle elle est définitivement bonne, et l'exploitation de la capacité de clojure à définir et à manipuler des classes Java semble être une bonne combinaison ici.

Les exemples de la question montrent clairement à quel point le SQL peut être pénible, et les DSL comme Korma ne sont qu'une partie de la solution : "Supposons que nous ayons quelques tables dans une base de données..." - Euh, je pensais que mon DSL allait les créer pour moi ? Ou est-ce que c'est juste quelque chose qu'un langage OOP fait mieux ? ;)

4voto

Avez-vous vérifié la bibliothèque de Korma http://sqlkorma.com/ ? Il vous permet de définir les relations entre les tables et de faire abstraction des jointures. Je pense que la raison principale pour laquelle il n'y a pas d'ORM pour clojure est qu'ils vont à l'encontre des idées de simplicité de Rich Hickey sur lesquelles le langage a été fondé. Regardez cette conférence : http://www.infoq.com/presentations/Simple-Made-Easy

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X