-
The *
method:
这将返回默认投影- 这就是你的描述:
'我的所有列(或计算值)usually有兴趣。
您的表可以有多个字段;你只需要一个子集
您的默认投影。默认投影必须与类型匹配
表的参数。
让我们一次一个地进行。如果没有<>
的东西,只是*
:
// 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
of (String, Int)
根据投影的元组。
val result: List[(String, Int)] = q.list
在这种情况下,您已经在中定义了所需的投影yield
条款
的for
理解。
-
现在关于<>
and Bar.unapply
.
这提供了所谓的映射投影.
到目前为止,我们已经了解了如何使用 slick 在 Scala 中表达查询
返回一个柱的投影(或计算值);所以执行时
这些查询你必须考虑结果行一个查询的作为 Scala 元组。
元组的类型将与定义的投影相匹配(由您定义)for
理解如前面的例子,默认情况下*
投影)。
这就是为什么field1 ~ field2
返回的投影Projection2[A, B]
where
A
是类型field1
and 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)
to a Bar
. And unapply
如果你猜到了,那就是相反的。
哪里有unapply
来自?这是一个标准的 Scala 方法,可用于
任何普通的案例类 - 只是定义Bar
给你一个Bar.unapply
哪个
是一个提取器可以用来取回id
and name
那Bar
是用:
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
is a Query
with a mapped投影到Baz
。
调用时,它返回一个List
of Baz
对象:
val result: List[Baz] = q1.list
-
最后,顺便说一句,.?
offers 选项提升- 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]
-
回应关于 Slick 如何使用的评论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 中,for 理解式是 for 的语法糖
method(可能是嵌套的)方法调用:上面的代码
(或多或少)相当于:
ns.filter(i => i*i % 2 == 0).map(i => i*i)
基本上,任何与filter
, map
, flatMap
方法(换句话说,Monad)可以用在for
理解代替ns
。一个很好的例子
是个选项单子。这是前面的例子
哪里相同for
声明适用于List
也Option
monads:
// (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 中,查询是一元的 - 它们只是带有
这map
, flatMap
and 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
and filter
用于
生成一个Query
通过反复变换Query(Bars)
每次调用filter
and map
。如果是
集合这些方法实际上迭代并过滤集合
但在 Slick 中它们用于生成 SQL。更多详细信息请参见此处:Scala Slick 如何将 Scala 代码转换为 JDBC?