2012-12-16 15 views
85

Staram się zrozumieć niektóre śliskie prace i co one wymagają.scala slick method Nie mogę do tej pory zrozumieć

Tutaj 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 _) 
} 

Może ktoś wyjaśnić mi, co jest celem * metody tutaj, co jest <>, dlaczego unapply? a czym jest Projection - metoda ~ "zwraca instancję z Projection2?

Odpowiedz

189

[UPDATE] - dodanej (kolejny) wyjaśnienie for listowe

  1. * 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.

  2. 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 
    
  3. 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] 
    
  4. 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?

+11

Piękna odpowiedź. –

+0

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

+0

@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

6

Ponieważ nikt inny nie odpowiedział, może to pomóc w rozpoczęciu pracy. Nie znam Slicka bardzo dobrze.

Z Slick documentation:

Lifted Osadzanie:

Każdy stół wymaga metody * ocznej domyślny projekcję. Ten opisuje, co otrzymasz po zwróceniu wierszy (w postaci obiektu tabeli ) z zapytania. Projekcja Slicka * nie musi być zgodna z wersją w bazie danych. Możesz dodać nowe kolumny (np. Z wyliczonymi wartościami ) lub pominąć niektóre kolumny, jak chcesz. Niezniesiony typ odpowiadający * projekcji jest podany jako parametr typu do tabeli . W przypadku prostych, niezamapowanych tabel będzie to pojedyncza kolumna typu lub krotka typ kolumn.

Innymi słowy, slick musi wiedzieć, jak postępować z wierszem zwróconym z bazy danych. Zdefiniowana metoda używa ich funkcji łączenia parserów, aby połączyć definicje kolumn w coś, co można wykorzystać w wierszu.

+0

ook. i Projekcja tylko przedstawienie kolumn .. jak: końcowy klasa Projection2 [T1, T2] ( ręczne Val _1: Kolumna [T1] ręczne Val _2: Kolumna [T2] ) rozciąga Tuple2 (_1, _2) z Projekcją [(T1, T2)] {.. – ses

+0

Teraz ... jak to się stało: Bar ma metodę "nieaplikacji"? – ses

+2

Aha .. - wszystkie klasy przypadków implementują cechę produktu, a unapply to metoda produktu. Magia. – ses