31

作为一个不熟悉 Python 的人,我经常听到很多关于 SQLAlchemy 的赞美。所以我想了解:

  1. 与jOOQQueryDSL等“类型安全的 SQL 构建器”相比,它提供了什么?

  2. 在 Java(或 Scala)世界中是否有更接近的等价物?我见过在这方面提到的Apache Empire-DB ......

4

5 回答 5

18

关于 SQLAlchemy 的一个值得注意的事情是它使表成为第一类对象。因此,核心 API 实际上是围绕表对象编写的,因此 API 本质上是关系型的。因此,在这个级别,即使 API 是 OO,它本质上也反映了 RDBMS 对象或函数,例如表、列、关系、连接、别名等。在这个级别上,SQLAlchemy 本质上为您提供了一个 OOSQL,其中 SQL 和关系数据库没有给出二等待遇。这也是 SQLAlchemy 真正闪耀的地方,因为这种抽象级别使您能够降级到“原始”关系级别,从而获得我真的没有见过任何其他 ORM 提供的巨大灵活性。有趣的是,在 ORM 中建模类继承所需的一些底层功能是在这一层实现的,例如。连接表继承http://docs.sqlalchemy.org/en/rel_0_7/orm/inheritance.html#joined-table-inheritance

(至少最近)更常用的 API 是声明式 API,它实际上是更多的 OO 并将业务域中的对象映射到我上面提到的对象(在大多数情况下是透明的)。这是 ORM 功能的用武之地,该 API 与其他 ORM API 有点相似,后者与域对象一起工作,这些操作直接转换为基础表操作。

据我所知,Scala 中的 ORM 仍在追赶 Java 中容易获得的东西(例如继承),即使它们提供了其他功能(例如类型安全、类似 LINQ 的构造),即使它们正在努力应对一些严重的问题,如 22 列限制。(我读过一些评论,很少有人想知道为什么有人需要超过 22 列,至少根据我的经验,在某些情况下我不会称之为罕见的情况,其中需要多个列)。

scala 中的 ORM(即使它们与 Java 有不同的风格)我认为仍在赶上所需的东西。关于 SQLAlchemy,是否有与我在 Java 或 Scala 中看到的足够接近的等价物?我没见过。

编辑:我忘记添加的一件事是,即使使用声明性 API,SQLAlchemy 仍然可以让您直接访问底层对象。因此,如果“类 Foo”以声明方式映射,则 Foo.__table__ 是您可以根据需要直接使用的表对象。

于 2012-08-16T07:04:17.390 回答
10

Squeryl提供的可组合性类似于他们在库主页上的“SQLALCHEMY'S PHILOSOPHY”中讨论的内容。您可以查询一个查询。

val query = from(table)(t => where(t.a === 1) select(t))
val composed = from(query)(q => where(q.b === 2) select(q))

它还分享了设计理念的很大一部分,主要是当事情变得复杂时,库应该“让开”并允许开发人员自己调整事情。虽然 Squeryl 进行对象映射,但我认为它更像是 DSL 而不是 ORM。

快速浏览一下 SQLAlchemy 功能列表,它没有共享的一些功能:

  1. 使用原始 SQL 的能力——在标准 ANSI SQL 中很难找到任何无法表达的东西。
  2. 继承映射——这里有一些个人意见,但我认为当图书馆这样做时,它通常违反了“让开”的原则。

当然,Squeryl 还提供了我认为 Python 库无法提供的主要功能,即编译器检查查询的类型安全性。

于 2012-05-10T16:57:44.520 回答
7

ScalaQuery(参见底部关于“slick”的注释)可以做的很简单:

for{
  a <- Article
  if a.dateCreated between(start, end)
  _ <- Query groupBy a.reporterID orderBy a.dateCreated.desc
} yield(a)

或通过组合的任意复杂:

val team = for{
  t <- Team
  s <- School if t.schoolID is s.id 
} yield (t,s)

val player = for{
  r <- Roster
  p <- Player if r.playerID is p.id
} yield (r, p)

val playerDetail = for{
  (r, p) <- player
} yield (p.id, p.firstName, p.lastName, r.jerseyID, r.position, r.gamesPlayed)

val scoring = for{
  (r, p) <- player
  s <- Scoring if p.id is s.playerID
  detail <- playerDetail 
} yield (r, p, s, detail)

val scoringDetail = for{
  (r, p, s, detail) <- scoring
  val (total, goals, assists) = 
    (s.playerID.count, s.goal.sum, (s.assist1.sum + s.assist2.sum))
  val ppg = (s.playerID.count / r.gamesPlayed)
} yield (goals, assists, total, ppg)

以下是获取团队统计数据的方法(可以针对联赛或单人视图进行修改):

val forScoring = for{
  start ~ end ~ teamID <- Parameters[JodaTime,JodaTime,Int]
  (r,p,s,player) <- scoring if r.teamID is teamID
  comp <- bindDate(start, end) if s.gameID is comp.id
  (goals, assists, total, ppg) <- scoringDetail
  _ <- Query groupBy p.id orderBy ( ppg.desc, total.asc, goals.desc )
} yield (player, goals, assists, total, ppg)

def getScoring(start: JodaTime, end: JodaTime, id: Int): List[TeamScoring] = {
  forScoring(start, end, id).list
}

我不认为在 Scala 中生成强类型的复杂查询是可能的,因此我只好自己移植经过尝试和真正的手写 SQL;也就是说,直到我遇到了 ScalaQuery,这是一个启示,就像 Scala 语言本身一样。

无论如何,你有选择,Squeryl 可能更符合 SQL Alchemy,不知道,探索一下,你可能不会失望,Scala 提供了如此多的优点,很难不感到头晕目眩这里、现在和之后;-)

ps Zeiger 和 Vogt在 Scala Days Skills Matters 上关于SLICK的精彩演讲,ScalaQuery 的下一个发展

于 2012-05-10T21:39:49.640 回答
7

您还可以使用最近添加到类型安全堆栈中的Slick 。

于 2012-08-16T13:30:18.863 回答
2

主动JDBC

它是“Active Record”设计模式的 Java 实现。它的灵感来自 Ruby on Rails 的 ActiveRecord ORM。它可能适合您的需要。

https://javalite.io/activejdbc

或者

JDBI

Java 的 SQL 便利库。它尝试使用集合、bean 等在惯用的 Java 中公开关系数据库访问,同时保持与 JDBC 相同级别的详细信息。

http://jdbi.org

于 2013-11-05T10:23:27.990 回答