*
metoda:
ta zwraca domyślny projekcję - co jest jak opisać:
„Wszystkie kolumny (lub obliczone wartości) Jestem zwykle zainteresowany” w
Twój stół może mieć kilka pól.; potrzebujesz tylko podzestawu dla domyślnej projekcji. Domyślna wersja musi być zgodna z parametrami tabeli .
Weźmy to pojedynczo.Bez <>
rzeczy, po prostu *
:
// 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)
Tylko definicji tabeli jak, który pozwoli Ci dokonać zapytań jak:
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)]
domyślny projekcja (Int, String)
prowadzi do List[(Int, String)]
dla prostych zapytań takich jak te.
// 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.
Jaki jest typ q
? Jest to Query
z projekcją (String, Int)
. Po wywołaniu zwraca krotki List
z (String, Int)
zgodnie z projekcją.
val result: List[(String, Int)] = q.list
W tym przypadku zdefiniowano projekcję chcesz w yield
klauzuli z for
zrozumieniem.
Teraz około <>
i Bar.unapply
.
Zapewnia to, co nazywa się Mapowane prognozy.
tej pory widzieliśmy, jak zręczny pozwala wyrazić kwerend w Scala które zwracają projekcję kolumn (lub obliczonych wartości); Więc podczas wykonywania tych zapytań trzeba myśleć o wiersz wyniku kwerendy jako krotki Scala. Rodzaj krotki będzie pasował do projekcji, która jest zdefiniowana (przez for
zrozumienia, jak w poprzednim przykładzie, z projekcja domyślny *
). Z tego powodu field1 ~ field2
zwraca odwzorowanie Projection2[A, B]
, gdzie A
jest typem field1
i B
jest typem 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
}
Mamy do czynienia z krotek, które mogą być uciążliwe, jeśli mamy zbyt wiele kolumn. Chcielibyśmy myśleć o wynikach nie jako o TupleN
, ale raczej o pewnym obiekcie z nazwanymi polami.
(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.
Jak to działa? <>
pobiera odwzorowanie Projection2[Int, String]
i zwraca odwzorowany odwzorowanie na typ Bar
. Obydwa argumenty mówią, że ta wersja (Int, String)
musi zostać odwzorowana na klasę case.
To odwzorowanie dwukierunkowe; Bar
jest konstruktorem klasy spraw, więc jest to informacja potrzebna do przejścia z (id: Int, name: String)
do Bar
. I unapply
jeśli zgadłeś, to jest na odwrót.
Skąd pochodzi unapply
?Jest to standardowa metoda Scala dostępne dla każdy zwykły klasa przypadek - po prostu definiowanie Bar
daje Bar.unapply
który jest wyciąg, które mogą być wykorzystane, aby wrócić do id
i name
że Bar
został zbudowany z:
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)]
Więc domyślny projekcja może być odwzorowany do klasy wypadek najbardziej oczekiwać, aby użyć:
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 _)
}
lub można nawet mieć go per-zapytanie:
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 _))
Tutaj rodzaj q1
jest Query
z odwzorowane projekcji Baz
. Po wywołaniu, zwraca List
z Baz
obiekty:
val result: List[Baz] = q1.list
Wreszcie, jak na marginesie, .?
oferuje Option podnoszenia - sposób Scala z czynienia z wartościami, które nie mogą być.
(id ~ name) // Projection2[Int, String] // this is just for illustration
(id.? ~ name) // Projection2[Option[Int], String]
Które, owijając się, będzie dobrze działać z oryginalnej definicji 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]
W odpowiedzi na komentarz na temat sposobu Slick zastosowania for
Ułatwienia:
Jakoś monady zawsze udaje się pokazać i poprosić o być częścią wyjaśnienia ...
Pojęcia nie są specyficzne tylko dla kolekcji. Mogą one być używane na różnego rodzaju Monau Monad, a kolekcje to tylko jeden z wielu rodzajów monad dostępnych w Scali.
Ale jak zbiory są zaznajomieni, robią dobrą wyjścia punkt za wyjaśnienie:
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, ...)
W Scala, dla zrozumienia jest cukier syntaktyczny dla metoda wywołuje metodę (ewentualnie zagnieżdżone): Powyższy kod oznacza (bardziej lub mniej), co odpowiada:
ns.filter(i => i*i % 2 == 0).map(i => i*i)
zasadzie cokolwiek z filter
, map
, flatMap
metody (innymi słowy, Monad) mogą być używane w zrozumieniu for
w miejsce ns
. Dobry przykład: to Option monad.Oto poprzedni przykład gdzie ten sam for
oświadczenie działa zarówno na List
jak Option
monady:
// (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
W ostatnim przykładzie, przekształcenie byłoby może wyglądać takiego:
// 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))
W wersji Slick kwerendy są monadyczne - są po prostu obiektami z metodami i map
, flatMap
i filter
. Więc for
zrozumienie (pokazane w objaśnieniu sposobu *
) po prostu przekłada się na:
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
Jak widać, flatMap
, map
i filter
służą do wygenerować Query
przez wielokrotnym transformacji Query(Bars)
przy każdej inwokacji filter
i map
. W przypadku kolekcji metody te faktycznie powodują iterację i filtrowanie kolekcji , ale w wersji Slick są używane do generowania kodu SQL. Więcej szczegółów: How does Scala Slick translate Scala code into JDBC?
Piękna odpowiedź. –
W bloku wyjaśniającym "1": nie jest oczywiste, że "val q =" to WrappingQuery, wygląda jak lista podczas czytania kodu. Jak to możliwe, że przekształca się w Query ...? (Nadal bawię się twoimi wyjaśnieniami, aby zrozumieć, jak to działa.) Dziękuję za to!) –
ses
@ses - dodano (nieco długie) wyjaśnienie na ten temat ... Zobacz także http://stackoverflow.com/questions/13454347/monads-with-java-8/13455602 # 13455602 - Uświadomiłem sobie, że to prawie ta sama treść. – Faiz