私はいくつかのスリック作品とそれが必要とするものを理解しようとします。
ここに例があります:
package models
case class Bar(id: Option[Int] = None, name: String)
object Bars extends Table[Bar]("bar") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
// This is the primary key column
def name = column[String]("name")
// Every table needs a * projection with the same type as the table's type parameter
def * = id.? ~ name <>(Bar, Bar.unapply _)
}
誰かがここで*
メソッドの目的、<>
とは何か、なぜunapply
なのか説明してもらえますか?プロジェクションとは何ですか-メソッド~
'はProjection2
のインスタンスを返しますか?
[UPDATE]-for
comprehensions に関する説明を(まだ)追加しました
*
メソッド:
これは、デフォルトの射影を返します-これがあなたの説明です:
'すべての列(または計算値)通常/に興味があります。
テーブルにはいくつかのフィールドがあります。デフォルトの投影のサブセットのみが必要です。デフォルトの投影法は、テーブルの型パラメーターと一致する必要があります。
一度に一つずつ取りましょう。 <>
がなければ、*
のみ:
// 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
のタイプは何ですか?これは、投影(String, Int)
を持つQuery
です。呼び出されると、プロジェクションに従って(String, Int)
タプルのList
を返します。
val result: List[(String, Int)] = q.list
この場合、yield
内包表記のfor
句で必要な射影を定義しました。
<>
とBar.unapply
について。
これは、 Mapped Projections と呼ばれるものを提供します。
これまでのところ、 projections of columns (または計算値)を返すクエリをScalaで表現することができます。したがって、これらのクエリを実行するときはクエリの結果行を考える必要がありますas a Scala Tuple。タプルのタイプは、定義されているProjectionと一致します(前の例の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)
投影をケースクラスにマッピングする方法をSlickに指示します。
これは双方向のマッピングです。 Bar
はケースクラスコンストラクターであるため、(id: Int, name: String)
からBar
に移動するために必要な情報です。そして、unapply
推測した場合、逆です。
unapply
はどこから来たのですか?これは、通常のケースクラスで使用できる標準のScalaメソッドです。Bar
を定義するだけで、id
を取得するために使用できるextractorであるBar.unapply
が得られます。および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
に mapped プロジェクションを持つBaz
です。呼び出されると、List
オブジェクトのBaz
を返します。
val result: List[Baz] = q1.list
最後に、余談ですが、.?
はOption Lifting-可能性のない値を処理する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で利用できる多くの種類のモナド型の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
メソッド(つまり、 Monad )を使用するものは、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
およびfilter
の呼び出しごとにQuery(Bars)
を繰り返し変換することにより、map
を生成するために使用されます。コレクションの場合、これらのメソッドは実際にコレクションを反復およびフィルター処理しますが、SlickではSQLの生成に使用されます。詳細はこちら: Scala_ SlickはScalaコードをJDBCにどのように変換しますか?
誰も答えていないので、これはあなたが始めるのを助けるかもしれません。私はスリックをよく知りません。
リフト埋め込み:
すべてのテーブルには、デフォルトの投影を含む*メソッドが必要です。これは、クエリから(テーブルオブジェクトの形式で)行を返すときに何が返されるかを示します。 Slickの*投影法は、データベースの投影法と一致する必要はありません。必要に応じて、新しい列を追加する(計算値を使用するなど)か、一部の列を省略できます。 *投影に対応する非リフト型は、Tableの型パラメーターとして指定されます。単純なマップされていないテーブルの場合、これは単一の列タイプまたは列タイプのタプルになります。
つまり、slickは、データベースから返された行を処理する方法を知る必要があります。定義したメソッドは、パーサーコンビネーター関数を使用して、列定義を行で使用できるものに結合します。