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?或者使用其他方法?
Scala的for
推导语法被翻译成方法调用。在Slick中定义的表是单子 - 它们具有神奇的foreach
、map
、flatMap
和filter
方法,使它们可以在Scala的for
“循环”中表达,而Scala将它们翻译为方法调用(如其他答案@emil-ivanov所提供的代码正确说明)。
与常规的Scala集合一样,for
是嵌套方法调用flatMap
/map
和filter
的语法糖;不同于常规集合,Slick Table
对象的版本map
和filter
返回一个查询的表示,并沿着每个过滤条件(if
)或连接(如s <- Suppliers if s.id is c.supID
)构建它
因此,q2
的类型不是您通常使用for推导来返回的集合类型(Scala通常用于返回),而是一个查询的表示。 (就像Scala Option Monad也可以使用for
推导,尽管它不是“集合”(像List
或Map
那样))
您可以使用q2.selectStatement
查看底层查询。
Scala的隐式提升 - c.price
不是一个Int
,而是一个列值的表示 - 因此表达式c.price < 9.0
变成了c.price.<(Const(9.0))
(将Int
提升为所需的类型),<
只是表示c.price
的类的一个方法。 <
方法不会执行<
通常做的事情(在普通Int
的情况下) - 它只是返回对应于price < 9
的SQL AST的表示形式,该AST成为生成和发送给JDBC以执行的SQL的一部分。
除此之外还有很多细节,但我认为查询单子和隐式提升是主要的因素。
在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)
)
)
现在,flatMap
、map
、withFilter
(以及foreach
)实际上并没有过滤集合,而是收集了发生在 AST(抽象语法树)中的内容,然后将其处理为 Slick 可以转换为 SQL 的形式。
此外,c.price
、c.supID
实际上是 Slick 的 column
,它们的<
、>
、===
(等等)方法不返回布尔值,而是收集比较,稍后将其传递下来以转换为 SQL。
这是一个由创建者进行的演讲,其中大部分内容都被正确描述了。