как делает _model_ данные из реляционных баз данных в clojure?

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

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

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

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

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

class Post < ActiveRecord::Base
  has_many :comments
 end


 class Comment < ActiveRecord::Base
   belongs_to :post
 end

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

20
задан Sandeep 18 June 2010 в 05:35
поделиться

1 ответ

В настоящее время в разработке находится пара 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 , в котором перечислены ваши столбцы.

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

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

Artist.order(:name).last

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

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

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

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

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

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

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

Но если вам нужны перехватчики, их можно реализовать очень легким способом, используя простые старые функции или мультиметоды. Раньше у меня была библиотека базы данных, которая вызывала ловушки в разное время цикла CRUD, например после сохранения или до удаления . Это были простые методы диспетчеризации по именам таблиц. Это позволяет вам по своему усмотрению расширять их до ваших собственных таблиц.

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

Легко и расширяемо, и это заняло пару секунд. Никакого OO не видно. Может быть, не так изощренно, как AR, но иногда достаточно простого.

Посмотрите на эту библиотеку , чтобы увидеть еще один пример определения ловушек.

Миграции

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

Польский

Много пользы от AR исходит из всех соглашений для именования таблиц и столбцов, а также всех вспомогательных функций для написания слов с заглавной буквы, форматирования дат и т. Д. Это не имеет ничего общего с OO vs.не-ОО; 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.

18
ответ дан 30 November 2019 в 01:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: