21

Twitter と #clojure IRC チャンネルでこの質問をしましたが、まだ回答がありません。

Clojure-for-Ruby-programmers、Clojure-for-lisp-programmers.. に関する記事がいくつかありますが、欠けている部分はClojure for ActiveRecord プログラマーです。

MongoDB や Redis などとのやり取りに関する記事がありましたが、結局のところ、これらはキー バリュー ストアです。ただし、Rails のバックグラウンドを持っているため、継承の観点からデータベースについて考えることに慣れています。

Clojure/Compojure + MySQL ( ffclassic ) に関するいくつかの記事では、SQL について深く掘り下げています。もちろん、ORM がインピーダンス ミスマッチを引き起こす可能性もありますが、ActiveRecord のように考えた後では、他の方法で考えるのは非常に難しいという事実は変わりません。

リレーショナル DB は、本質的にセットであるため、オブジェクト指向パラダイムに非常に適していると思います。activerecord のようなものは、このデータのモデル化に非常に適しています。たとえばブログの場合 - 簡単に言えば

class Post < ActiveRecord::Base
  has_many :comments
 end


 class Comment < ActiveRecord::Base
   belongs_to :post
 end

これをClojureでどのようにモデル化しますか-これは非常に厳密にアンチOOです? おそらく、すべての関数型プログラミング言語に言及していれば、質問はより適切だったでしょうが、私は Clojure の観点 (および Clojure の例) からより興味があります。

4

3 に答える 3

19

現在、いくつかの ORM に似たライブラリが開発中です。

  • clj レコード
  • カルテ
  • おやこ (免責事項、これは私が書きました。)
  • ClojureQLは、私が見る限り SQL を生成するライブラリに近いものですが、言及する価値があります。

メーリング リストでは、最近、一部の (賢い) 人々が、これがどのように機能するかについて他のモデルについて説明しました。これらのライブラリはそれぞれ、問題に対してかなり異なるアプローチを取っているため、必ずそれらすべてを確認してください。

たとえば、Oyako を使用した拡張例を次に示します。このライブラリは本番環境に対応しておらず、まだ大規模な開発が行われているため、この例は 1 週間で無効になる可能性がありますが、それは達成されつつあります。いずれにせよ、それは概念実証です。しばらく待つと、誰かが良いライブラリを思い付くでしょう。

clojure.contrib.sqlDB から (JDBC を介して) レコードをフェッチし、レコードを表す不変のハッシュマップを作成できることに注意してください。データは最終的に法線マップになるため、マップで機能する無数の Clojure コア関数はすべて、このデータで既に機能しています。

ActiveRecord は他に何を提供しますか? いくつか考えられます。

簡潔な SQL クエリ DSL

私がこれを精神的にモデル化する方法: まず、テーブル間の関係を定義します。これにはミューテーションやオブジェクトは必要ありません。静的な説明です。AR はこの情報を一連のクラスに分散させますが、私はそれを別の (静的な) エンティティとして見ています。

定義された関係を使用すると、非常に簡潔な方法でクエリを作成できます。例えばオヤコの場合:

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

記録の保存・更新・削除

繰り返しますが、なぜオブジェクト指向スタイルのオブジェクトやミューテーション、実装の継承が必要なのですか? 最初にレコードを (不変のマップとして) フェッチし、一連の関数にスレッド化して、assoc必要に応じて新しい値を追加し、データベースに戻すか、関数を呼び出して削除します。

巧妙なライブラリは、メタデータを利用してどのフィールドが変更されたかを追跡し、更新を行うために必要なクエリの量を減らすことができます。または、レコードにフラグを立てて、DB 関数がレコードを元に戻すテーブルを認識できるようにします。Carte はカスケード更新 (親レコードが変更されたときにサブレコードを更新する) も行うと思います。

検証、フック

これの多くは、ORM ライブラリではなくデータベースに属していると思います。たとえば、カスケード削除 (親レコードが削除されたときに子レコードを削除する): AR にはこれを行う方法がありますが、DB 内のテーブルに句を投げてから DB に処理させるだけで、もう心配する必要はありません。多くの種類の制約と検証と同じです。

ただし、フックが必要な場合は、単純な古い関数またはマルチメソッドを使用して非常に軽量な方法で実装できます。過去のある時点で、CRUD サイクルのさまざまな時点でフックを呼び出すデータベース ライブラリがありましafter-savebefore-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))

簡単で拡張可能で、数秒で記述できます。OO が見えない。AR ほど洗練されていないかもしれませんが、シンプルで十分な場合もあります。

フックを定義する別の例については、このライブラリを参照してください。

移行

カルテにはこれらがあります。私はそれらについてあまり考えたことはありませんが、データベースのバージョン管理とそこへのデータの丸呑みは、Clojure の可能性の範囲を超えているようには思えません。

研磨

AR の利点の多くは、テーブルの命名規則や列の命名規則、および単語の大文字化や日付の書式設定などのすべての便利な関数から得られます。これは、OO と非 OO とは何の関係もありません。AR には多くの時間が費やされているため、非常に洗練されています。Clojure には、DB データを操作するための AR クラス ライブラリがまだないかもしれませんが、しばらくお待ちください。

そう...

自分自身を破壊する方法、自分自身を変更する方法、自分自身を保存する方法、自分自身を他のデータに関連付ける方法、自分自身をフェッチする方法などを知っているオブジェクトを持つ代わりに、単なるデータであるデータを持ち、そのデータで動作する関数を定義します。それを破棄し、DB で更新し、フェッチし、他のデータに関連付けます。これは、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 のような方法でコードを書くことのすべての利点も得られます。

于 2010-06-18T06:08:28.707 に答える
5

この質問はしばらく答えられていませんが、Korma http://sqlkorma.comは、SQL と Clojure の間の不協和音を減らすのに役立つもう 1 つのプロジェクトです。これは最近開発されたもので、最新の Clojure バージョンで動作するはずです。

于 2013-06-24T11:50:23.820 に答える
0

clojure.contrib.sql に基づいて構築された新しいマイクロ SQL DSL が開始されました。軽量の 76 loc で重量を量っている Githubにあります。

著者のブログには、DSL の背後にある多くの例と理論があり、Github プロファイル (新規ユーザー SO ハイパーリンク制限 bah) にリンクされています。その設計により、SQL をより慣用的な Clojure に抽象化できるようになると思います。

于 2010-09-08T07:23:30.140 に答える