39

我正在阅读有关 clojure 网络堆栈的网站:

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

它对 clojure 的 ORM 有这样的说法:

“出于显而易见的原因,没有用于 Clojure 的 SQL/关系数据库 ORM。”

我可以看到的明显原因是,当您执行 clojure.contrib.sql 或 clojureql 查询时,对象的映射会自动发生。然而,似乎需要一些额外的工作来完成一对多或多对多的关系(尽管可能不需要太多的工作)。

我发现这篇文章是一对多的:http: //briancarper.net/blog/493/

我不确定我是否同意;它似乎假设两个表都是从数据库中提取的,然后在内存中过滤连接的表。在实践中,我认为 sql 查询将指定 where 条件。

所以我想知道,是否有一些相当明显的方法可以通过 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)))

有什么方法可以使这个概念自动化,或者这是否尽可能好?

4

8 回答 8

30

我很久以前问过这个问题,但我遇到了以下问题,并决定将其添加为答案,以防有人感兴趣:

http://sqlkorma.com/

于 2012-01-22T18:31:25.160 回答
29

在 Clojure 中不需要 ORM 的“明显”原因是因为惯用的 Clojure 本身没有对象。

在 Clojure 程序中表示数据的最佳方式是简单数据结构(映射和向量)的惰性序列。与成熟的 ORM 相比,将这些映射到 SQL 行要简单得多,并且阻抗不匹配的情况要少得多。

此外,关于您的问题中与形成复杂 SQL 查询有关的部分......阅读您的代码,与 SQL 本身相比,它并没有任何明显的优势。不要害怕 SQL!它的功能很棒:关系数据操作。

于 2011-09-17T23:08:48.567 回答
11

仍然没有高级库来创建我所知道的复杂关系查询。有很多方法可以解决这个问题(您提供的链接是一种方法),但即使 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 版中添加它。

于 2011-09-16T07:35:08.873 回答
7

冒着与一些重量级人物在水中游泳的风险(彻底混合我的隐喻;) - 当然,ORM 的最佳特性之一是,在绝大多数情况下,务实的程序员必须永远不要使用甚至考虑 SQL。在最坏的情况下,可能需要一些带有几个查询结果的 hacky 编程,因为当需要优化时,这将转换为原始 SQL,当然,;)。

说出于“明显”的原因不需要 ORM,这在某种程度上是不合时宜的。进一步开始使用 DSL 对 SQL 进行建模会加剧这个错误。在绝大多数 Web 框架中,对象模型一种 DSL,用于描述 Web 应用程序存储的数据,而 SQL 只是将其与数据库通信所需的声明性语言。

使用 ROR、django 或 Spring 时的步骤顺序:

  1. 以 OOP 格式描述您的模型
  2. 打开 REPL 并制作一些示例模型
  3. 构建一些视图
  4. 在网络浏览器中检查结果

好的,所以您可能会使用稍微不同的顺序,但希望您明白这一点。在 SQL 或描述它的 DSL 中思考是一个很长的路要走。相反,模型层将所有 SQL 抽象出来,允许我们创建数据对象,这些对象对我们希望在网站中使用的数据进行密切建模。

我完全同意 OOP 不是灵丹妙药,但是,在 Web 框架中建模数据绝对是它的优势所在,并且利用 clojure 定义和操作 Java 类的能力在这里似乎是一个很好的匹配。

问题中的示例清楚地展示了 SQL 的痛苦程度,而像 Korma 这样的 DSL 只是部分解决方案:“假设我们在数据库中有一些表......” - 呃,我以为我的 DSL 会创建那些为了我?或者这只是 OOP 语言做得更好的事情?;)

于 2013-11-12T22:06:55.260 回答
4

你检查过 Korma 库http://sqlkorma.com/吗?它允许您定义表关系和抽象连接。我认为 clojure 没有任何 ORM 的一个主要原因是因为它们违背了 Rich Hickey 的简单思想,该语言是基于此的。看看这个演讲: http: //www.infoq.com/presentations/Simple-Made-Easy

于 2014-01-25T15:10:44.217 回答
1

称为聚合的库可以解决您在这里遇到的大部分问题。它不是一个完整的 ORM,但如果你告诉它数据库模式的关系图,那么它会提供自动遍历关系图的 CRUD 实现。如果您已经在使用 Yesql 或原始 SQL 查询之类的东西,它会很有用,因为它很容易插入到使用简单结果映射的实现中。

于 2016-03-08T11:38:51.253 回答
0

无论您是否想使用它们,已经有聚合 并且现在有巨嘴鸟(他们显然阅读了与您相同的链接)。

于 2017-09-27T23:22:21.260 回答
0

ORM 是过早的优化。Walkable 是 Clojure 的新 sql 库,采用整体方法。在这里查看https://github.com/walkable-server/walkable

对于那些对花哨的自述文件持怀疑态度的人的真实示例:https ://github.com/walkable-server/realworld

于 2018-05-04T12:02:52.347 回答