问题
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
forcomprehension syntax, which is translated to method calls. Tables defined in Slick are Monads - they have the magicforeach,map,flatMap, andfiltermethods 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
foris syntactic sugar for nested method calls toflatMap/mapandfilter; unlike regular collections, the SlickTableobjects' versions ofmapandfilterreturn 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
q2is 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 withforcomprehensions despite not being a 'collection' (in the way thatListorMapis))You can see the underlying query with
q2.selectStatement.Scala's implicit lifting -
c.priceis not anIntbut rather a representation of a column value - so the expressionc.price < 9.0becomesc.price.<(Const(9.0))(anIntis 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 plainInts) - it simply returns a representation of the SQL AST corresponding toprice < 9that 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 columns, 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