как можно _модель_ данных из реляционных баз данных в clojure? - PullRequest
20 голосов
/ 18 июня 2010

Я задавал этот вопрос в твиттере, а также на IRC-канале #clojure, но пока не получил ответов.

Было несколько статей о программистах Clojure-для-Ruby, программистах Clojure-for-lisp ... но чего не хватает, так это Clojure для программистов ActiveRecord .

Были статьи о взаимодействии с MongoDB, Redis и т. Д., Но это ключевые хранилища ценностей в конце дня. Однако, исходя из опыта работы с Rails, мы привыкли думать о базах данных с точки зрения наследования - has_many, polymorphic, assign_to и т. Д.

Несколько статей о Clojure / Compojure + MySQL ( ffclassic ) - углубиться в sql. Конечно, возможно, что ORM вызывает несоответствие импеданса , но факт остается фактом, что после такого мышления, как ActiveRecord, очень трудно думать иначе.

Я полагаю, что реляционные БД очень хорошо поддаются объектно-ориентированной парадигме, поскольку они, по сути, являются множествами. Такие вещи, как activerecord, очень хорошо подходят для моделирования этих данных. Например, блог - проще говоря

class Post < ActiveRecord::Base
  has_many :comments
 end


 class Comment < ActiveRecord::Base
   belongs_to :post
 end

Как это можно смоделировать в Clojure, который так строго против OO? Возможно, вопрос был бы лучше, если бы он касался всех функциональных языков программирования, но меня больше интересует точка зрения Clojure (и примеры Clojure)

Ответы [ 3 ]

18 голосов
/ 18 июня 2010

В настоящее время в работе есть пара ORM-подобных библиотек.

  • clj-record
  • Carte
  • Oyako (Отказ от ответственности, я написал это.)
  • ClojureQL - это больше генерирующая SQL библиотека из того, что я могувидите, но это заслуживает упоминания.

В списке рассылки некоторые (умные) люди недавно описали некоторые другие модели того, как это может работать .Каждая из этих библиотек имеет свой подход к проблеме, поэтому обязательно взгляните на них все.

Вот расширенный пример , например, с использованием Oyako.Эта библиотека не готова к работе и все еще находится в стадии интенсивной разработки, поэтому пример может быть недействительным через неделю, но он уже готов.В любом случае, это подтверждение концепции.Дайте ему немного времени, и кто-нибудь найдет хорошую библиотеку.

Обратите внимание, что clojure.contrib.sql уже позволяет вам извлекать записи из БД (через JDBC) и в итоге иметь неизменные хеш-карты, представляющие записи.Поскольку данные попадают в карты нормалей, все бесчисленные функции ядра Clojure, которые работают с картами, уже работают с этими данными.

Что еще дает ActiveRecord?Я могу подумать о нескольких вещах.

Краткий SQL-запрос DSL

Способ, которым я мысленно моделирую это: сначала вы определяете отношения между таблицами.Это не требует мутации или объектов.Это статичное описание.AR распространяет эту информацию в кучу классов, но я вижу ее как отдельную (статическую) сущность.

Используя определенные отношения, вы можете затем писать запросы очень лаконично.Например, с помощью Oyako:

(def my-data (make-datamap db [:foo [has-one :bar]]
                              [:bar [belongs-to :foo]]))

(with-datamap my-data (fetch-all :foo includes :bar))

Затем у вас будет несколько foo объектов, каждый с ключом :bar, который перечисляет ваши бары.

В Oyako "карта данных""это просто карта.Сам запрос представляет собой карту.Возвращаемые данные - это вектор карт (векторов карт).Таким образом, вы получите стандартный, простой способ создания, управления и итерации по всем этим вещам, что приятно.Добавьте немного сахара (макросы и обычные функции), чтобы вам было проще создавать и манипулировать этими картами, и в итоге получится довольно мощно.Это всего лишь один способ, существует множество подходов.

Если вы посмотрите на библиотеку, например Sequel , для другого примера, у вас есть такие вещи, как:

Artist.order(:name).last

Но почему эти функции должны быть методами, которые живут внутри объектов?Эквивалентом в Oyako может быть:

(last (-> (query :artist) 
          (order :name)))

Сохранение / обновление / удаление записей

Опять же, зачем вам нужны объекты в стиле OO или мутация или наследование реализации для этого?Сначала извлеките запись (в виде неизменяемой карты), затем пропустите ее через несколько функций, assoc добавив в нее новые значения по мере необходимости, затем вставьте ее обратно в базу данных или удалите, вызвав для нее функцию.

Умная библиотека может использовать метаданные для отслеживания того, какие поля были изменены, чтобы уменьшить количество запросов, необходимых для обновления.Или пометить запись, чтобы функции БД знали, в какую таблицу ее вставить.Думаю, Carte даже делает каскадные обновления (обновляя вложенные записи при изменении родительской записи).

Проверки, перехватчики

Многое из этого я вижу как принадлежащее в базе данных, а не вОРМ библиотекарь.Например, каскадное удаление (удаление дочерних записей при удалении родительских записей): у AR есть способ сделать это, но вы можете просто бросить предложение в таблицу в БД, а затем позволить вашей БД справиться с этим, и больше никогда не беспокоиться.То же самое со многими видами ограничений и проверок.

Но если вам нужны хуки, они могут быть реализованы очень легким способом с использованием простых старых функций или мультиметодов.Когда-то в прошлом у меня была библиотека базы данных, которая в разное время вызывала перехватчики в цикле CRUD, например after-save или before-delete.Это были простые мультиметоды, рассылающие по именам таблиц.Это позволяет вам расширять их на свои собственные таблицы.

(defmulti before-delete (fn [x] (table-for x)))
(defmethod before-delete :default [& _]) ;; do nothing
(defn delete [x] (when (before-delete x) (db-delete! x) (after-delete x)))

Тогда позже, как конечный пользователь, я мог написать:

(defmethod before-delete ::my_table [x] 
  (if (= (:id x) 1)
    (throw (Exception. "OH NO! ABORT!"))
    x))

Легко и расширяемо, и написание заняло пару секунд. ОО не видно. Не так сложно, как AR, но иногда просто достаточно хорошо.

Посмотрите на в этой библиотеке , чтобы найти другой пример определения хуков.

Миграции

У карт есть эти. Я не особо задумывался о них, но управление версиями базы данных и добавление в нее данных не представляется возможным для Clojure.

Польский

Преимущество AR заключается во всех условных обозначениях именования таблиц и столбцов именования, а также во всех вспомогательных функциях для написания заглавных букв и форматирования дат и тому подобного. Это не имеет ничего общего с ОО против не-ОО; У AR просто много блеска, потому что много времени ушло на это. Возможно, у Clojure пока нет библиотеки AR-классов для работы с данными БД, но дайте ей немного времени.

Итак ...

Вместо того, чтобы иметь объект, который знает, как уничтожить себя, мутировать себя, сохранить себя, связать себя с другими данными, извлечь себя и т. Д., Вместо этого у вас есть данные, которые являются просто данными, и затем вы определяете функции, которые работают над этим Данные: сохраняет, уничтожает, обновляет в БД, извлекает, связывает с другими данными. Вот как Clojure работает с данными в целом, и данные из базы данных ничем не отличаются.

Foo.find(1).update_attributes(:bar => "quux").save!

=> (with-db (-> (fetch-one :foo :where {:id 1})
                (assoc :bar "quux")
                (save!)))

Foo.create!(:id => 1)

=> (with-db (save (in-table :foo {:id 1})))

Нечто подобное. Это изнутри от того, как работают объекты, но обеспечивает ту же функциональность. Но в Clojure вы также получаете все преимущества написания кода в виде FP.

5 голосов
/ 24 июня 2013

На этот вопрос давно не было ответа, но Korma http://sqlkorma.com - это еще один проект, который также помогает уменьшить диссонанс между SQL и Clojure. Над ним работали совсем недавно, и он должен работать с более свежими версиями Clojure.

0 голосов
/ 08 сентября 2010

Запущен новый DSL micro SQL, созданный на основе clojure.contrib.sql. Он на Github весом в 76 лок.

Существует множество примеров и теорий, лежащих в основе DSL, в блоге автора, на который он ссылается в своем профиле Github (ограничение количества гиперссылок для нового пользователя SO). Я считаю, что его дизайн позволяет абстрагироваться от SQL в гораздо более идиоматическую Clojure.

...