问题
How does Slick translate code such as:
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)
Into JDBC?
Does it use Scala Virtualized? Does it use some other method?
回答1:
Slick's stable API achieves this via what it calls lifted embedding. Your example is clearly using the stable API (as you use ===
for equality and not ==
).
The beauty of Slick (and in turn Scala) is that - this much is achieved without using macros or Scala-Virtualized. (Side Note: Slick's experimental API does use macros - and this will allow you to use ==
instead of ===
or is
)
The translation to SQL is achieved using:
Scala's
for
comprehension syntax, which is translated to method calls. Tables defined in Slick are Monads - they have the magicforeach
,map
,flatMap
, andfilter
methods which allow them to be expressed infor
'loops' while Scala translates them to method calls (as correctly illustrated in the code provided by the other answer by @emil-ivanov).As with regular Scala collections, the
for
is syntactic sugar for nested method calls toflatMap
/map
andfilter
; unlike regular collections, the SlickTable
objects' versions ofmap
andfilter
return representations of a query, building it along with every filter condition (if
) or join (as ins <- Suppliers if s.id is c.supID
)So the type of
q2
is not your usual collection (as a for comprehension in Scala is typically used to return), but rather a representation of a query. (Just as the Scala Option Monad also works withfor
comprehensions despite not being a 'collection' (in the way thatList
orMap
is))You can see the underlying query with
q2.selectStatement
.Scala's implicit lifting -
c.price
is not anInt
but rather a representation of a column value - so the expressionc.price < 9.0
becomesc.price.<(Const(9.0))
(anInt
is lifted to the desired type), and<
is a just a method of the class that representsc.price
, aColumn
. The<
method does not do what<
usually does (in the case of plainInt
s) - it simply returns a representation of the SQL AST corresponding toprice < 9
that becomes part of the SQL that is generated and sent off to JDBC to execute.
There's a whole lot else going on, in terms of details, but I think the query monad and the implicit lifting are the chief ingredients.
回答2:
In Scala the for
"loop" isn't actually a special language construct, but rather syntactic sugar. Your first example
val q2 = for {
c <- Coffees if c.price < 9.0
s <- Suppliers if s.id === c.supID
} yield (c.name, s.name)
translates to something in the lines of:
val q2 = Coffees.withFilter(_.price < 9.0).flatMap(c =>
Suppliers.withFilter(_.id === c.supID).map(s =>
(c.name, s.name)
)
)
Now, the flatMap
, map
, withFilter
(and foreach
) do not actually filter the collection, but rather collect what's hapening in an AST (Abstract Syntax Tree), which is then handled to Slick to translate to SQL.
Also, c.price
, c.supID
are actually Slick column
s, whose <
, >
, ===
(and so on) methods don't return bool, but collect the comparison as well, which
is later passed down to be converted to SQL.
This is a talk by the creators, where most of this is described (correctly).
来源:https://stackoverflow.com/questions/13758445/how-does-scala-slick-translate-scala-code-into-jdbc