Scala Slick如何将Scala代码翻译成JDBC?

19

Slick 是如何翻译下面这段代码的:

val q2 = for {
  c <- Coffees if c.price < 9.0
  s <- Suppliers if s.id === c.supID
} yield (c.name, s.name)
for(t <- q2) println("  " + t._1 + " supplied by " + t._2)

使用JDBC?

它是否使用了Scala Virtualized?或者使用其他方法?

2个回答

32
Slick的“稳定API”通过所谓的“提升嵌入”实现了这一点。你的例子显然使用稳定的API(因为你使用“===”而不是“==”进行相等比较)。Slick(以及Scala)的美妙之处在于,即使没有使用宏或Scala-Virtualized,也能够实现这一点。(副注:Slick的“实验性API”确实使用了宏,这将允许你使用“==”而不是“===”或“is”)。
将其转换为SQL的方法如下:
  1. Scala的for推导语法被翻译成方法调用。在Slick中定义的表是单子 - 它们具有神奇的foreachmapflatMapfilter方法,使它们可以在Scala的for“循环”中表达,而Scala将它们翻译为方法调用(如其他答案@emil-ivanov所提供的代码正确说明)。

    与常规的Scala集合一样,for是嵌套方法调用flatMap/mapfilter的语法糖;不同于常规集合,Slick Table对象的版本mapfilter返回一个查询的表示,并沿着每个过滤条件(if)或连接(如s <- Suppliers if s.id is c.supID)构建它

    因此,q2类型不是您通常使用for推导来返回的集合类型(Scala通常用于返回),而是一个查询的表示。 (就像Scala Option Monad也可以使用for推导,尽管它不是“集合”(像ListMap那样))

    您可以使用q2.selectStatement查看底层查询。

  2. Scala的隐式提升 - c.price不是一个Int,而是一个列值的表示 - 因此表达式c.price < 9.0变成了c.price.<(Const(9.0))(将Int提升为所需的类型),<只是表示c.price的类的一个方法。 <方法不会执行<通常做的事情(在普通Int的情况下) - 它只是返回对应于price < 9的SQL AST的表示形式,该AST成为生成和发送给JDBC以执行的SQL的一部分。

除此之外还有很多细节,但我认为查询单子和隐式提升是主要的因素。


非常棒的文章。隐式提升非常聪明。 - Dominic Bou-Samra

15

在Scala中,for循环实际上并不是一种特殊的语言结构,而是一种语法糖。您的第一个示例

val q2 = for {
  c <- Coffees if c.price < 9.0
  s <- Suppliers if s.id === c.supID
} yield (c.name, s.name)

翻译成类似于以下内容:

val q2 = Coffees.withFilter(_.price < 9.0).flatMap(c =>
    Suppliers.withFilter(_.id === c.supID).map(s =>
        (c.name, s.name)
    )
)

现在,flatMapmapwithFilter(以及foreach)实际上并没有过滤集合,而是收集了发生在 AST(抽象语法树)中的内容,然后将其处理为 Slick 可以转换为 SQL 的形式。

此外,c.pricec.supID实际上是 Slick 的 column,它们的<>===(等等)方法不返回布尔值,而是收集比较,稍后将其传递下来以转换为 SQL。

这是一个由创建者进行的演讲,其中大部分内容都被正确描述了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接