Подтвердить что ты не робот

ORM для clojure?

Я читал этот сайт о веб-стеке clojure:

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

и это говорит об ORM для clojure:

"Для очевидных причин нет ORM-модулей SQL/Relational DB для clojure.

Очевидная причина, по которой я вижу, заключается в том, что сопоставление объекта происходит автоматически, когда вы выполняете запрос clojure.contrib.sql или clojureql. Однако кажется, что для выполнения отношений "один-ко-многим" или "ко многим" требуется некоторая дополнительная работа (хотя, возможно, и не слишком много работы).

Я нашел эту запись для одного-ко-многим: http://briancarper.net/blog/493/

Я не уверен, с чем согласен; предполагается, что обе таблицы извлекаются из базы данных, а затем объединенная таблица фильтруется в памяти. На практике я думаю, что sql-запрос будет определять критерии.

Итак, мне осталось интересно, есть ли довольно очевидный способ автоматически делать отношения "один ко многим" через clojureql или clojure.contrib.sql? Единственное, что я могу придумать, это что-то вроде этого (с использованием типичного примера блога/комментария):

(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)))

Есть ли способ автоматизировать это понятие или это так хорошо, как оно получается?

4b9b3361

Ответ 1

По-прежнему нет библиотеки высокого уровня для создания сложных реляционных запросов, о которых я знаю. Там много способов решить эту проблему (ссылка, которую вы предоставили, является одним из способов), но даже если ClojureQL обеспечивает действительно приятную DSL, на которой вы можете опираться, она по-прежнему пропускает некоторые важные функции. Вот быстрый и грязный пример макроса, который генерирует скопированные соединения:

(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)))

С этим вы могли бы сделать (include :users :posts :comments) и получить этот 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)

Есть одна серьезная проблема с этой техникой. Основная проблема заключается в том, что возвращенные столбцы для всех таблиц будут объединены вместе в одну и ту же карту. Поскольку имена столбцов не могут быть квалифицированы автоматически, они не будут работать, если столбец одинаково назван в разных таблицах. Это также не позволит вам группировать результаты без доступа к схеме. Я не думаю, что есть способ избежать незнания схемы базы данных для такого рода вещей, поэтому еще предстоит много работы. Я думаю, что ClojureQL всегда останется низкоуровневой библиотекой, поэтому вам придется подождать, пока какая-либо другая библиотека более высокого уровня не будет существовать или не создаст свой собственный.

Чтобы создать такую ​​библиотеку, вы всегда можете посмотреть класс JDBC DatabaseMetaData, чтобы предоставить информацию о схеме базы данных. Я все еще работаю над анализатором базы данных для Lobos, которые используют его (и некоторые пользовательские материалы), но я все еще далек от начала работа над SQL-запросами, которые я мог бы добавить в версии 2.0.

Ответ 2

Я задал этот вопрос совсем недавно, но я наткнулся на следующее и решил добавить его в качестве ответа в случае, если кто-то заинтересован:

http://sqlkorma.com/

Ответ 3

"Очевидная" причина, по которой вам не нужна ORM как таковая в Clojure, состоит в том, что идиоматический Clojure не имеет объектов, как таковых.

Лучший способ представления данных в программе Clojure - это ленивые последовательности простых строчек данных (карты и векторы). Сопоставление этих строк с SQL намного менее сложное и имеет гораздо меньшее несоответствие импеданса, чем полномасштабная ORM.

Кроме того, что касается части вашего вопроса, связанного с формированием сложного SQL-запроса... чтение вашего кода, у него действительно нет явных преимуществ перед самим SQL. Не бойтесь SQL! Это здорово для того, что он делает: манипуляции реляционными данными.

Ответ 4

Рискуя плавать в водах с некоторыми из тяжелых тяжелых нападающих (чтобы полностью смешивать мои метафоры;) - несомненно, одна из лучших особенностей ORM заключается в том, что в подавляющем большинстве случаев прагматический программист никогда не использовать или даже думать о SQL. В худшем случае может потребоваться какое-то хакерское программирование с результатами нескольких запросов на основе того, что это будет конвертировано в исходный SQL, когда эта оптимизация требуется, конечно;;).

Сказать, что ORM не требуется для "очевидной" причины, несколько не хватает точки. Далее, чтобы начать использовать DSL для моделирования SQL, эта ошибка повторяется. В подавляющем большинстве веб-фреймворков объектная модель представляет собой DSL, используемую для описания данных, хранящихся в веб-приложении, а SQL - просто декларативный язык, необходимый для связи с базой данных.

Порядок шагов при использовании ROR, django или Spring:

  • Опишите ваши модели в формате OOP
  • Откройте REPL и создайте примеры моделей
  • Построить несколько видов
  • Проверить результаты в веб-браузере

Хорошо, поэтому вы можете использовать несколько иной порядок, но, надеюсь, вы понимаете. Мышление в SQL или DSL, которое описывает это, далеко вниз по списку. Вместо этого слой модели абстрагирует весь SQL-запрос, позволяя нам создавать объекты данных, которые тесно моделируют данные, которые мы хотим использовать на веб-сайте.

Я бы полностью согласился с тем, что ООП не является серебряной пулей, однако моделирование данных в веб-инфраструктуре - это то, что определенно полезно, и использование возможности clojure определять и манипулировать Java-классами, похоже, будет хорошим совпадением здесь,

Примеры в этом вопросе наглядно демонстрируют, насколько болезненным может быть SQL, а DSL, такие как Korma, являются лишь частью решения: "Предположим, что у нас есть некоторые таблицы в базе данных..." - er, я думал, что мой DSL идет создать для меня? Или это только то, что делает язык ООП лучше?;)

Ответ 5

Вы проверили библиотеку Korma http://sqlkorma.com/? Он позволяет определять отношения таблиц и абстракции над объединениями. Я думаю, что главная причина в том, что ORM для clojure не существует, потому что они противоречат идеям Рича Хики о простоте, на которых основан язык. Посмотрите этот разговор: http://www.infoq.com/presentations/Simple-Made-Easy

Ответ 6

Библиотека, известная как aggregate, может позаботиться о большинстве чехов, которые у вас есть. Это не полномасштабный ORM, но если вы расскажете ему график отношений для вашей схемы базы данных, тогда он предоставляет реализации CRUD, которые автоматически ходят график отношений. Это полезно, если вы уже используете что-то вроде Yesql или сырых SQL-запросов, потому что оно легко подключается к реализации, использующей простые карты результатов.

Ответ 7

Если вы хотите использовать их или нет, уже было aggregate и теперь toucan (и они, очевидно, читают ту же ссылку, что и вы).