的*
方法:
这将返回默认的投影 - 这是你如何描述:
“所有的列(或计算值)我通常感兴趣”的
您的表可以有多个领域。你只需要一个子集 你的默认投影。默认投影必须匹配表格的 参数。
让我们一次一个。如果没有<>
的东西,只是*
:
// First take: Only the Table Defintion, no case class:
object Bars extends Table[(Int, String)]("bar") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def * = id ~ name // Note: Just a simple projection, not using .? etc
}
// Note that the case class 'Bar' is not to be found. This is
// an example without it (with only the table definition)
就像一个表定义会让你进行查询,如:
implicit val session: Session = // ... a db session obtained from somewhere
// A simple select-all:
val result = Query(Bars).list // result is a List[(Int, String)]
的(Int, String)
默认投影导致List[(Int, String)]
这样的简单查询如同这些。
// SELECT b.name, 1 FROM bars b WHERE b.id = 42;
val q =
for (b <- Bars if b.id === 42)
yield (b.name ~ 1)
// yield (b.name, 1) // this is also allowed:
// tuples are lifted to the equivalent projection.
q
是什么型号?这是一个Query
与投影(String, Int)
。 当被调用时,它根据投影返回一个List
的(String, Int)
元组。
val result: List[(String, Int)] = q.list
在这种情况下,你已经确定你的for
理解的yield
条款 在希望的投影。
现在关于<>
和Bar.unapply
。
这提供了所谓的映射预测。
到目前为止,我们已经看到了如何华而不实,您可以表达斯卡拉 返回一个投影列(或计算值)的查询;因此,在执行 这些查询时你得想查询作为斯卡拉元组结果行的。 元组的类型将与定义的投影相匹配(通过您在前例中的 for
理解,由默认*
投影)。 这就是为什么field1 ~ field2
返回Projection2[A, B]
一个投影,其中 A
是field1
和B
类型是的field2
类型。
q.list.map {
case (name, n) => // do something with name:String and n:Int
}
Queury(Bars).list.map {
case (id, name) => // do something with id:Int and name:String
}
我们正在处理的元组,这可能是麻烦的,如果我们有太多的 列。我们想把结果看作TupleN
而不是 具有命名字段的对象。
(id ~ name) // A projection
// Assuming you have a Bar case class:
case class Bar(id: Int, name: String) // For now, using a plain Int instead
// of Option[Int] - for simplicity
(id ~ name <> (Bar, Bar.unapply _)) // A MAPPED projection
// Which lets you do:
Query(Bars).list.map (b.name)
// instead of
// Query(Bars).list.map { case (_, name) => name }
// Note that I use list.map instead of mapResult just for explanation's sake.
这是如何工作的? <>
需要投影Projection2[Int, String]
和 的类型Bar
返回一个映射投影。这两个参数Bar, Bar.unapply _
告诉光滑如何(Int, String)
投影必须映射到案例类。
这是一个双向映射; Bar
就是如此类的构造函数,所以这是从(id: Int, name: String)
去一个Bar
所需的 信息。如果您已经猜到了,那么 是相反的。
来自哪里unapply
?这是一个标准的Scala方法可供 任何普通案例类 - 只是定义Bar
给你一个Bar.unapply
其 是提取可以用来找回了 Bar
与建造的id
和name
:
val bar1 = Bar(1, "one")
// later
val Bar(id, name) = bar1 // id will be an Int bound to 1,
// name a String bound to "one"
// Or in pattern matching
val bars: List[Bar] = // gotten from somewhere
val barNames = bars.map {
case Bar(_, name) => name
}
val x = Bar.unapply(bar1) // x is an Option[(String, Int)]
所以你默认的投影可以映射到的情况下类,你最希望使用:
object Bars extends Table[Bar]("bar") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def * = id ~ name <>(Bar, Bar.unapply _)
}
或者你甚至可以把它每查询:
case class Baz(name: String, num: Int)
// SELECT b.name, 1 FROM bars b WHERE b.id = 42;
val q1 =
for (b <- Bars if b.id === 42)
yield (b.name ~ 1 <> (Baz, Baz.unapply _))
这里的q1
类型是一个一个Query
映射投影Baz
。 调用时,它返回Baz
的List
对象:
val result: List[Baz] = q1.list
最后,顺便说一句,在.?
提供选项起重 - 的 Scala的方式处理值可能不是。
(id ~ name) // Projection2[Int, String] // this is just for illustration
(id.? ~ name) // Projection2[Option[Int], String]
其中,包裹起来,将很好地与原有的Bar
定义工作:
case class Bar(id: Option[Int] = None, name: String)
// SELECT b.id, b.name FROM bars b WHERE b.id = 42;
val q0 =
for (b <- Bars if b.id === 42)
yield (b.id.? ~ b.name <> (Bar, Bar.unapply _))
q0.list // returns a List[Bar]
为响应评论用途如何油滑for
内涵:
不知何故,单子总是设法出现和要求到 作为解释的一部分...
理解不仅限于集合。 它们可以用于任何种类的Monad,集合 只是Scala中可用的各种monad类型之一。
但作为收藏品都很熟悉,他们提出了一个很好的起点 点解释:
val ns = 1 to 100 toList; // Lists for familiarity
val result =
for { i <- ns if i*i % 2 == 0 }
yield (i*i)
// result is a List[Int], List(4, 16, 36, ...)
在Scala中,一个用于理解是语法糖 方法(可能是嵌套)方法调用:上面的代码 是(或多或少)等效于:
ns.filter(i => i*i % 2 == 0).map(i => i*i)
基本上,filter
什么,map
,flatMap
方法(换句话说,Monad)可以用在 for
的理解中,代替ns
。一个很好的例子是 是Option monad。下面是前面的例子 在同一for
声明工程对 List
双方以及Option
单子:
// (1)
val result =
for {
i <- ns // ns is a List monad
i2 <- Some(i*i) // Some(i*i) is Option
if i2 % 2 == 0 // filter
} yield i2
// Slightly more contrived example:
def evenSqr(n: Int) = { // return the square of a number
val sqr = n*n // only when the square is even
if (sqr % 2 == 0) Some (sqr)
else None
}
// (2)
result =
for {
i <- ns
i2 <- evenSqr(i) // i2 may/maynot be defined for i!
} yield i2
在最后一个例子,转型或许会看起来 这样的:
// 1st example
val result =
ns.flatMap(i => Some(i*i)).filter(i2 => i2 %2 ==0)
// Or for the 2nd example
result =
ns.flatMap(i => evenSqr(i))
在Slick中,查询是monadic - 它们只是 ,map
,flatMap
和filter
方法的对象。所以for
理解 (在*
方法的说明中所示),只是转换为:
val q =
Query(Bars).filter(b => b.id === 42).map(b => b.name ~ 1)
// Type of q is Query[(String, Int)]
val r: List[(String, Int)] = q.list // Actually run the query
正如你所看到的,flatMap
,map
和filter
用于 由Query(Bars)
反复变换生成Query
每次调用filter
和map
。对于 集合,这些方法实际上会迭代并过滤集合 ,但在Slick中它们用于生成SQL。更多细节在这里: How does Scala Slick translate Scala code into JDBC?
美丽的答案。 –
在'1'解释块中:'val q ='是WrappingQuery,并不明显,它在阅读代码时看起来像列表。它如何转换为Query ..? (我还在玩你的解释,以了解它是如何工作的,谢谢你!) –
ses
@ses - 添加了一个(稍微长一点的)关于这个的解释...另外,看看http://stackoverflow.com/questions/13454347/monads-with-java-8/13455602#13455602 - 我意识到这是几乎相同的内容。 – Faiz