关系数据库中的一个_model_数据如何在clojure中? [英] how does one _model_ data from relational databases in clojure?

查看:124
本文介绍了关系数据库中的一个_model_数据如何在clojure中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经在twitter上提出这个问题以及#clojure IRC频道,但没有回应。



有几篇关于Clojure-for-Ruby-程序员,Clojure-for-lisp程序员..但是缺少的部分是 ActiveRecord程序员的Clojure



与MongoDB,Redis等交互 - 但这些都是一天结束时的关键价值存储。但是,来自Rails背景,我们习惯于在继承方面考虑数据库 - has_many,polymorphic,belongs_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的例子)

解决方案



在邮寄名单上,一些(智能)人最近描述了一些其他型号的这可能工作。这些库中的每一个都采用了相当不同的方法来处理这个问题,所以一定要看看它们。



这是一个使用Oyako的扩展示例。这个库不是生产准备好,仍然在大量开发,所以示例可能在一个星期无效,但它到达那里。这是一个概念验证在任何情况下。给它一些时间,有人会想出一个好的图书馆。



请注意, clojure.contrib.sql 允许您从DB(通过JDBC)获取记录,并以表示记录的不可变哈希映射结束。因为数据以正常的映射结束,所有在Map上工作的Clojure核心函数都已经在这个数据上了。



ActiveRecord还给你什么?我可以想到几件事情。



一个简洁的SQL查询DSL



精神:首先你定义表之间的关系。这不需要突变或对象。这是一个静态描述。 AR将这些信息传播到一系列类中,但我将它看作一个单独的(静态)实体。



然后,您可以使用定义的关系非常简洁的方式。以Oyako为例:

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

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

然后你会有一些 foo 对象, $ c>:bar 键。



在Oyako,数据图只是一张地图。查询本身是一个地图。返回的数据是地图(地图矢量)的矢量。所以你最终得到一个标准的,简单的方法来构造和操纵和迭代所有这些东西,这是很好的。添加一些糖(宏和正常函数),让您更容易简洁地创建和操作这些地图,它最终是相当强大。这只是一个方法,有很多方法。



如果你看一个像 Sequel 另一个例子,你有以下的东西:

  Artist.order :name).last 

但是为什么这些函数必须是位于对象内部的方法? Oyako中的等价物可能是:

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



保存/更新/删除记录



再次,为什么你需要OO风格的对象或变异或实现继承为此?首先获取记录(作为不可变映射),然后通过一系列函数, assoc 将新值传递给它,然后将其填充回数据库或通过调用它上面的函数来删除它。



一个聪明的库可以利用元数据来跟踪哪些字段已被修改,以减少所需的查询量做更新。或者标记记录,使DB功能知道哪个表将其粘回。



验证,挂钩







$ b b

其中大部分我认为属于数据库,而不是在ORM库。例如,级联删除(删除父记录时删除子记录):AR有一种方法可以做到这一点,但是你可以在DB中的表上抛出一个子句,然后让你的DB处理它,并且再也不用担心。与许多种约束和验证一样。



但是如果你想要钩子,他们可以使用简单的老函数或多重方法以非常轻量的方式实现。在过去的某个时候,我有一个数据库库,在CRUD循环中的不同时间调用钩子,例如 after-save before-delete 。他们是在表名称上调度的简单多方法。这可以让你根据需要将它们扩展到你自己的表。

 (defmulti before-delete(fn [x] for x)))
(defmethod before-delete:default [& _]);;不做任何事情
(defn delete [x](when(before-delete x)(db-delete!x)(after-delete x)))

然后,作为最终用户,我可以写成:

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

容易和可扩展,花了几秒钟写。没有OO在视线。



查看

迁移



Carte有这些。我没有考虑太多,但是对数据库进行版本控制,并将数据填入它并不超出Clojure的可能性范围。



波兰语



很多AR的好处来自命名表和命名列的所有约定,以及所有的方便功能,大写字和格式化日期等。这与OO与非OO无关; AR只是有很多抛光,因为很多时间已经进入它。可能Clojure没有AR类库用于处理DB数据,但给它一些时间。



所以...



而不是有一个知道如何销毁自己的对象,改变自身,保存自身,将自身与其他数据,获取自身等联系起来,而不是只有数据的数据,然后定义对数据进行处理的函数:保存,破坏数据库,在数据库中更新数据库,获取数据,将其与其他数据相关联。这就是Clojure对数据的一般操作,数据库中的数据没有什么不同。

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

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

Foo.create!(:id => 1)

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

像那样。它从对象的工作方式的内外,但它提供相同的功能。但是在Clojure中,你还可以得到以FP方式编写代码的所有好处。


I have asked this question on twitter as well the #clojure IRC channel, yet got no responses.

There have been several articles about Clojure-for-Ruby-programmers, Clojure-for-lisp-programmers.. but what is the missing part is Clojure for ActiveRecord programmers .

There have been articles about interacting with MongoDB, Redis, etc. - but these are key value stores at the end of the day. However, coming from a Rails background, we are used to thinking about databases in terms of inheritance - has_many, polymorphic, belongs_to, etc.

The few articles about Clojure/Compojure + MySQL (ffclassic) - delve right into sql. Of course, it might be that an ORM induces impedence mismatch, but the fact remains that after thinking like ActiveRecord, it is very difficult to think any other way.

I believe that relational DBs, lend themselves very well to the object-oriented paradigm because of them being , essentially, Sets. Stuff like activerecord is very well suited for modelling this data. For e.g. a blog - simply put

class Post < ActiveRecord::Base
  has_many :comments
 end


 class Comment < ActiveRecord::Base
   belongs_to :post
 end

How does one model this in Clojure - which is so strictly anti-OO ? Perhaps the question would have been better if it referred to all functional programming languages, but I am more interested from a Clojure standpoint (and Clojure examples)

解决方案

There are a couple ORM-like libraries in the works nowadays.

  • clj-record
  • Carte
  • Oyako (Disclaimer, I wrote this one.)
  • ClojureQL is more of an SQL-generating lib from what I can see, but it deserves mention.

On the mailing list, some (smart) people recently described some other models for how this might work. Each of these libraries takes a fairly different approach to the problem, so be sure to take a look at them all.

Here's an extended example using Oyako, for example. This library isn't production ready and still under heavy development, so the example may be invalid in a week, but it's getting there. It's a proof-of-concept in any case. Give it some time and someone will come up with a good library.

Note that clojure.contrib.sql already lets you fetch records from a DB (via JDBC) and end up with immutable hash-maps representing records. Because the data ends up in normal maps, all of the myriad Clojure core functions that work on maps already work on this data.

What else does ActiveRecord give you? I can think of a couple of things.

A concise SQL-query DSL

The way I model this mentally: First you define the relationship between the tables. This doesn't require mutation or objects. It's a static description. AR spreads this information out in a bunch of classes, but I see it as a separate (static) entity.

Using the defined relationships, you can then write queries in a very concise manner. With Oyako for example:

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

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

Then you'll have some foo objects, each with a :bar key that lists your bars.

In Oyako, the "data map" is just a map. The query itself is a map. The returned data is a vector of maps (of vectors of maps). So you end up with a standard, easy way to construct and manipulate and iterate over all of these things, which is nice. Add some sugar (macros and normal functions), to let you concisely create and manipulate these maps more easily, and it ends up being quite powerful. This just just one way, there are a lot of approaches.

If you look at a library like Sequel for another example, you have things like:

Artist.order(:name).last

But why do these functions have to be methods that live inside of objects? An equivalent in Oyako might be:

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

Save/update/delete records

Again, why do you need OO-style objects or mutation or implementation inheritance for this? First fetch the record (as an immutable map), then thread it through a bunch of functions, associng new values onto it as needed, then stuff it back into the database or delete it by calling a function on it.

A clever library could make use of metadata to keep track of which fields have been altered, to reduce the amount of querying needed to do updates. Or to flag the record so the DB functions know which table to stick it back into. Carte even does cascading updates (updating sub-records when a parent record is altered), I think.

Validations, hooks

Much of this I see as belonging in the database rather than in the ORM libary. For example, cascading deletes (deleting child records when parent records are deleted): AR has a way to do this, but you can just throw a clause onto the table in the DB and then let your DB handle it, and never worry again. Same with many kinds of constraints and validations.

But if you want hooks, they can be implemented in a very lightweight way using plain old functions or multimethods. At some point in the past I had a database library that called hooks at different times in the CRUD cycle, for example after-save or before-delete. They were simple multimethods dispatching on table names. This lets you extend them to your own tables as you like.

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

Then later as an end user I could write:

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

Easy and extensible, and took a couple seconds to write. No OO in sight. Not as sophisticated as AR maybe, but sometimes simple is good enough.

Look at this library for another example of defining hooks.

Migrations

Carte has these. I haven't thought much about them, but versioning a database and slurping data into it doesn't seem beyond the realm of possibility for Clojure.

Polish

A lot of the good of AR comes from all the conventions for naming tables and naming columns, and all the convenience functions for capitalizing words and formatting dates and such. This has nothing to do with OO vs. non-OO; AR just has a lot of polish because a lot of time has gone into it. Maybe Clojure doesn't have an AR-class library for working with DB data yet, but give it some time.

So...

Instead of having an object that knows how to destroy itself, mutate itself, save itself, relate itself to other data, fetch itself, etc., instead you have data that's just data, and then you define functions that work on that data: saves it, destroys it, updates it in the DB, fetches it, relates it to other data. This is how Clojure operates on data in general, and data from a database is no different.

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

Something like that. It's inside-out from the way objects work, but it provides the same functionality. But in Clojure you also get all the benefits of writing code in an FP kind of way.

这篇关于关系数据库中的一个_model_数据如何在clojure中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆