scala metoda slick nie mogę zrozumieć do tej pory
[6]}staram się zrozumieć niektóre Slick działa i co to wymaga.
Oto przykład:
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 _)
}
Czy mógłby mi ktoś wyjaśnić jaki jest cel *
Metody tutaj, czym jest <>
, Dlaczego unapply
? czym jest metoda Projection-method ~
' zwraca instancję Projection2
?
2 answers
[aktualizacja] - dodano (jeszcze jedno) wyjaśnienie for
-
Metoda
*
:To zwraca domyślną projekcję - tak to opisujesz:
' wszystkie kolumny (lub obliczone wartości) jestem zazwyczaj zainteresowany'.
Twoja tabela może mieć kilka pól; potrzebujesz tylko podzbioru dla Twoja domyślna projekcja. Domyślna projekcja musi odpowiadać Typ parametry tabeli.
Zróbmy to po kolei. Bez<>
rzeczy, tylko*
:// 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)
Taka definicja tabeli pozwoli Ci tworzyć zapytania typu:
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ślna projekcja
(Int, String)
prowadzi doList[(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 toQuery
z projekcją(String, Int)
. Po wywołaniu zwracaList
Z(String, Int)
krotek zgodnie z projekcją.val result: List[(String, Int)] = q.list
W w tym przypadku zdefiniowałeś projekcję, którą chcesz w klauzuli
yield
for
zrozumienie. -
Teraz o
<>
iBar.unapply
.Daje to tak zwane odwzorowane projekcje .
Do tej pory widzieliśmy, jak slick pozwala wyrażać zapytania w Scali które zwracają rzut kolumn (lub obliczone wartości); tak więc podczas wykonywania te zapytania musisz myśleć o wierszu wynikowym zapytania jako o Scali tuple . Typ krotki będzie zgodny z projekcją określoną (przez
for
zrozumienie jak w poprzednim przykładzie, domyślnie*
projekcji). Dlategofield1 ~ field2
zwraca rzutProjection2[A, B]
gdzieA
jest typemfield1
iB
jest typemfield2
.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 krotkami, które mogą być uciążliwe, jeśli mamy zbyt wiele kolumny. Chcielibyśmy myśleć o wynikach NIE jako
TupleN
, ale raczej o niektórych obiekt o nazwie pola.(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?
<>
wykonuje rzutProjection2[Int, String]
i zwraca odwzorowaną projekcję typuBar
. Dwa argumentyBar, Bar.unapply _
powiedz slickowi, jak ta projekcja(Int, String)
musi być odwzorowana na klasę przypadków.Jest to mapowanie dwukierunkowe;
Bar
jest konstruktorem klasy case, więc jest to informacje potrzebne do przejścia z {[47] } doBar
. Iunapply
jeśli zgadłeś, jest na odwrót.Skąd pochodzi
unapply
? Jest to standardowa metoda Scala Dostępny dla każda zwykła Klasa przypadków-samo zdefiniowanieBar
dajeBar.unapply
, które jest ekstraktorem , który może być użyty do odzyskaniaid
iname
, żeBar
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)]
Aby Twoja domyślna projekcja mogła zostać zmapowana do klasy case, której najbardziej oczekujesz:
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żesz mieć go nawet na 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 Typ
q1
jestQuery
zodwzorowaniem naBaz
. Po wywołaniu zwracaList
ZBaz
obiekty:val result: List[Baz] = q1.list
-
Wreszcie, na marginesie,
.?
oferuje opcję Lifting - sposób Scala radzenie sobie z wartościami, które mogą nie być.(id ~ name) // Projection2[Int, String] // this is just for illustration (id.? ~ name) // Projection2[Option[Int], String]
Które, podsumowując, będzie dobrze pasować do 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 jak Slick używa
for
:Jakoś monady zawsze się pojawiają i domagają się Bądź częścią wyjaśnienia...
Dla składanie nie jest specyficzne tylko dla kolekcji. Mogą być używane na wszelkiego rodzaju Monadzie , a kolekcje są tylko jeden z wielu rodzajów monad dostępnych w Scali.
Ale jak kolekcje są znane, to dobrze zaczynają punkt dla wyjaśnienia:
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 Scali, dla rozumienia jest cukrem składniowym dla wywołania metody (ewentualnie zagnieżdżone): powyższy kod jest (mniej więcej) równoważne:
ns.filter(i => i*i % 2 == 0).map(i => i*i)
W zasadzie wszystko z
filter
,map
,flatMap
metody (innymi słowy, A Monad) mogą być stosowane wfor
zrozumienie zamiastns
. Dobry przykład jest opcja monad . Oto poprzedni przykład gdzie to samofor
stwierdzenie działa zarówno naList
orazOption
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 transformacja może wyglądać tak:
// 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 Slicku zapytania są monadyczne - są tylko obiektami z na
map
,flatMap
ifilter
metody. Więcfor
zrozumienie (pokazane w wyjaśnieniu metody*
) tłumaczy się tylko 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
ifilter
są używane do generowanieQuery
przez powtarzające się przekształcenieQuery(Bars)
z każdym wywołaniemfilter
imap
. W przypadku Kolekcje te metody faktycznie iterują i filtrują kolekcję ale w Slick są one używane do generowania SQL. Więcej szczegółów tutaj: jak Scala Slick tłumaczy Kod Scali na JDBC?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:43
Ponieważ nikt inny nie odpowiedział, Może to pomóc Ci zacząć. Nie znam Slicka zbyt dobrze.
Każda tabela wymaga metody * odpowiadającej domyślnej projekcji. To opisuje, co otrzymasz po powrocie wierszy (w postaci obiekt table) Z zapytania. Projekcja Slicka * nie musi pasuje do tego w bazie danych. Można dodawać nowe kolumny (np. z obliczone wartości) lub Pomiń niektóre kolumny, jak chcesz. Typ nie podnoszony odpowiadający rzutowi * jest podany jako parametr typu do Stolik. W przypadku prostych, Nie mapowanych tabel będzie to pojedyncza kolumna typ lub krotka typów kolumn.
Innymi słowy, slick musi wiedzieć, jak radzić sobie z wierszem zwróconym z bazy danych. Zdefiniowana metoda wykorzystuje funkcje parsera combinator, aby połączyć definicje kolumn w coś, co może być używane w wierszu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-17 01:13:11