Die *
Methode:
Dies gibt die Projektion Standard - das ist, wie Sie beschreiben:
‚Alle Spalten (oder Werte berechnet) Ich bin in der Regel interessiert‘ in
Ihre Tabelle kann mehrere Felder haben. Sie benötigen nur eine Teilmenge für Ihre Standardprojektion. Die Standardprojektion muss mit den Parametern des Typs der Tabelle übereinstimmen.
Lassen Sie uns eins nach dem anderen nehmen.Ohne die <>
Sachen, nur die *
:
// 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)
Nur eine Tabellendefinition so lassen Sie Abfragen wie machen:
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)]
die Standardprojektion von (Int, String)
führt zu einer List[(Int, String)]
für einfache Abfragen wie zum wie diese.
// 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.
Was ist der Typ von q
? Es ist ein Query
mit der Projektion (String, Int)
. Bei Aufruf wird eine List
(String, Int)
Tupel gemäß der Projektion zurückgegeben.
val result: List[(String, Int)] = q.list
In diesem Fall haben Sie die Projektion Sie des for
Verständnis in der yield
Klausel wollen definiert.
Jetzt über <>
und Bar.unapply
.
Dies bietet, was Mapped Projections genannt wird.
Bisher haben wir gesehen, wie glatt Sie Abfragen in Scala auszudrücken ermöglicht , die eine Projektion von Spalten (oder berechnete Werte) zurückkehren; Also bei der Ausführung diese Abfragen müssen Sie an die Ergebniszeile einer Abfrage als ein Scala-Tupel denken. Der Typ des Tupel entspricht der Projektion, die definiert ist (durch Ihre for
Verständnis wie im vorherigen Beispiel von der Standard *
Projektion). Aus diesem Grund field1 ~ field2
eine Projektion von Projection2[A, B]
zurück, wo A
die Art der field1
ist und B
die Art der field2
ist.
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
}
Wir mit Tupeln zu tun hat, was mühsam sein kann, wenn wir zu viele Spalten haben. Wir möchten uns die Ergebnisse nicht als TupleN
sondern eher als ein Objekt mit benannten Feldern vorstellen.
(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.
Wie funktioniert das? <>
nimmt eine Projektion Projection2[Int, String]
und gibt eine projizierte Projektion auf den Typ Bar
zurück. Die beiden Argumente Bar, Bar.unapply _
erzählen, wie diese (Int, String)
Projektion einer Fallklasse zugeordnet werden muss.
Dies ist ein Zwei-Wege-Mapping; Bar
ist der Fall Klassenkonstruktor, das ist also die Informationen benötigt, um von (id: Int, name: String)
zu einem Bar
gehen. Und unapply
wenn Sie es erraten haben, ist für das Gegenteil.
Woher kommt unapply
?Dies ist ein Standard Scala Verfahren für jeder gewöhnliche Fallklasse - nur definieren Bar
gibt Ihnen eine Bar.unapply
die ist ein Extraktor, die verwendet werden können, die id
und name
zurück zu bekommen, dass die Bar
wurde mit gebaut:
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)]
So können Ihre Standard-Projektion auf den Fall Klasse zugeordnet werden Sie am meisten erwarten zu verwenden:
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 _)
}
Oder Sie können es sogar pro-Abfrage haben:
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 _))
Hier ist die Art von q1
ist ein Query
mit einem Projektion Baz
abgebildet. Wenn es aufgerufen wird, gibt sie ein List
von Baz
Objekte:
val result: List[Baz] = q1.list
schließlich als Nebenbei bemerkt, die .?
bietet Option Lifting - die Scala Weg von mit Werten zu tun, die nicht sein kann.
(id ~ name) // Projection2[Int, String] // this is just for illustration
(id.? ~ name) // Projection2[Option[Int], String]
Which, Einwickeln, wird mit Ihrer ursprünglichen Definition von Bar
gut funktionieren:
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]
Als Reaktion auf den Kommentar, wie Slick Anwendungen for
Comprehensions:
Irgendwie immer Monaden verwalten zu zeigen und zu werden Teil der Erklärung sein ...
Für Übersichten gelten nicht nur Sammlungen. Sie können auf jeder Art von Monad verwendet werden, und Sammlungen sind nur eine der vielen Arten von Monade-Typen in Scala verfügbar.
Aber als Sammlungen vertraut sind, machen sie einen guten Ausgang Punkt für eine Erklärung:
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, ...)
In Scala, ein für das Verständnis ist syntaktischer Zucker für Methode (möglicherweise verschachtelte) Methode aufruft: Der obige Code ist (mehr oder weniger) entspricht:
ns.filter(i => i*i % 2 == 0).map(i => i*i)
Grundsätzlich etwas mit filter
, map
, flatMap
Methoden (mit anderen Worten, ein Monad) kann in einem for
Verständnis statt ns
verwendet werden. Ein gutes Beispiel ist das Option monad.Hier ist das vorherige Beispiel , wo die gleiche for
Anweisung funktioniert sowohl auf der List
sowie Option
Monaden:
// (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
Im letzten Beispiel würde die Transformation aussehen vielleicht wie folgt aus:
// 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))
In Slick sind Abfragen monadisch - sie sind nur Objekte mit den Methoden map
, flatMap
und filter
. So ist das for
Verständnis (in der Erklärung der *
Methode dargestellt) übersetzt nur:
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
Wie Sie sehen können, flatMap
, map
und filter
werden verwendet, um erzeugen ein Query
durch die wiederholte Transformation von Query(Bars)
mit jedem Aufruf von filter
und map
. Im Fall von Sammlungen iterieren und filtern diese Methoden tatsächlich die Sammlung , aber in Slick werden sie verwendet, um SQL zu generieren. Weitere Details hier: How does Scala Slick translate Scala code into JDBC?
Schöne Antwort. –
In '1' Erklärung Block: Es ist nicht offensichtlich, dass 'val q =' WrappingQuery, es sieht aus wie eine Liste beim Lesen des Codes. Wie ist es möglich, dass es in Query umgewandelt wird? (Ich spiele immer noch mit deinen Erklärungen, um zu verstehen, wie es funktioniert. Danke dafür!) –
ses
@ses - hinzugefügt eine (etwas lang) Erklärung dazu ... Siehe auch http://StackOverflow.com/questions/13454347/monads-with-java-8/13455602 # 13455602 - Ich erkannte, dass es fast der gleiche Inhalt ist. – Faiz