*
方法:
これはデフォルトのプロジェクションを返します -これはあなたが説明する方法です:
「通常関心があるすべての列(または計算値)」。
テーブルには複数のフィールドがある場合があります。デフォルトの投影にはサブセットのみが必要です。デフォルトのプロジェクションは、テーブルのタイプパラメータと一致する必要があります。
一度に一つずつ取り上げましょう。ものなしで<>
、ただ*
:
// 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)
。呼び出されると、投影に従ってタプルのa List
を返します(String, Int)
。
val result: List[(String, Int)] = q.list
この場合、理解度のyield
節で必要な投影法を定義しましたfor
。
今について<>
とBar.unapply
。
これにより、マップされた投影と呼ばれるものが提供されます。
これまで、scalを使用して、列(または計算値)のプロジェクションを返すクエリをScalaで表現する方法を見てきました。したがって、これらのクエリを実行するときは、クエリの結果行をScalaタプルと考える必要があります。タプルのタイプは、(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
。2つの引数Bar, Bar.unapply _
は、この(Int, String)
プロジェクションをケースクラスにマッピングする方法をスリックに指示します。
これは双方向のマッピングです。Bar
ケースクラスのコンストラクタがあるので、それから行くために必要な情報だ(id: Int, name: String)
とBar
。そしてunapply
、あなたがそれを推測したならば、逆のためです。
どこunapply
から来たの?これは、任意の通常の場合クラスで利用可能な標準のScala方法である-ちょうど定義がBar
あなたに与えBar.unapply
ているその抽出戻って取得するために使用することができるid
と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
は、Query
への投影がマッピングされたBaz
です。呼び出されると、オブジェクトのa List
を返しBaz
ます。
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
理解をどのように使用するかについてのコメントに応えて:
どういうわけか、モナドは常に表示され、説明の一部であることを要求します...
内包表記はコレクションだけに固有のものではありません。これらはあらゆる種類のモナドで使用でき、コレクションはScalaで利用できる多くの種類のモナドの1つにすぎません。
しかし、コレクションはよく知られているので、説明の出発点として適しています。
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内包はメソッド(ネストされている可能性がある)メソッド呼び出しの構文糖です:上記のコードは(多かれ少なかれ)以下と同等です:
ns.filter(i => i*i % 2 == 0).map(i => i*i)
基本的には、使って何がfilter
、map
、flatMap
(つまり、メソッドモナドは)で使用することができる
for
の代わりに理解ns
。良い例はOptionモナドです。ここでは、同じ前の例だ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では、クエリはモナディックです-それらは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
のを繰り返し変換によってQuery(Bars)
それぞれの呼び出しでfilter
とmap
。コレクションの場合、これらのメソッドは実際にコレクションを反復してフィルタリングしますが、SlickではSQLの生成に使用されます。詳細はこちら:
Scala SlickはどのようにしてScalaコードをJDBCに変換しますか?