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?

Author: ses, 2012-12-17

2 answers

[aktualizacja] - dodano (jeszcze jedno) wyjaśnienie for

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

  2. Teraz o <> i Bar.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). Dlatego field1 ~ field2 zwraca rzut 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 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 rzut Projection2[Int, String] i zwraca odwzorowaną projekcję typu Bar. Dwa argumenty Bar, 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] } do Bar. I unapply 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 zdefiniowanie Bar daje Bar.unapply, które jest ekstraktorem , który może być użyty do odzyskania 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)]
    

    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 jest Query zodwzorowaniem na Baz. Po wywołaniu zwraca List Z Baz obiekty:

     val result: List[Baz] = q1.list
    
  3. 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]
    
  4. W odpowiedzi na komentarz jak Slick używafor:

    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 w for zrozumienie zamiast ns. Dobry przykład jest opcja monad . Oto poprzedni przykład gdzie to samo for stwierdzenie działa zarówno na List 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 i filter metody. Więc for 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 i filter są używane do generowanie Query przez powtarzające się przekształcenie Query(Bars) z każdym wywołaniem filter i map. 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?

 189
Author: Faiz,
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.

Z :

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.

 6
Author: Dominic Bou-Samra,
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