scala slick метод я не могу понять до сих пор



Я пытаюсь понять некоторые скользкие работы и то, что он требует.



вот пример:



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


может кто-нибудь объяснить мне, в чем цель * метод здесь, что такое <>, почему unapply? а что такое проекция-метод ~' возвращает экземпляр Projection2?

593   2  

2 ответов:

[обновление] -добавить (еще одно) объяснение for генераторы

  1. The * способ:

    возвращает проекция по умолчанию - вот как вы описываете:

    ' все столбцы (или вычисленные значения) я обычно заинтересованы в.

    ваша таблица может иметь несколько полей; вам нужно только подмножество для ваш проекция по умолчанию. Проекция по умолчанию должна соответствовать типу параметры таблицы.

    давайте возьмем его по одному. Без <> вещи, просто *:

    // 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)
    

    просто определение таблицы, как это позволит вам делать запросы, такие как:

    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)]
    

    проекция по умолчанию (Int, String) приводит к List[(Int, String)] для простых запросов, таких как эти.

    // 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.
    

    что это за тип q? Это Query с проекцией (String, Int). При вызове он возвращает List на (String, Int) кортежей в проекции.

     val result: List[(String, Int)] = q.list
    

    в этом случае вы определили проекцию, которую вы хотите в yield статья из for понимания.

  2. теперь о <> и Bar.unapply.

    это обеспечивает то, что называется Картографические Проекции.

    до сих пор мы видели, как slick позволяет выражать запросы в Scala что возвращает проекции столбцов (или вычисленные значения); поэтому при выполнении эти запросы вы должны думать о результате строки запрос как кортеж Scala. Тип Кортежа будет соответствовать проекции, которая определена (вашим for понимания, как и в предыдущем примере, по умолчанию * проекция). Вот почему field1 ~ field2 возвращает проекции Projection2[A, B] где A тип field1 и B тип 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 
    }
    

    мы имеем дело с кортежами, которые могут быть громоздким, если у нас слишком много столбцы. Мы хотели бы думать о результатах не как TupleN а объект с именованными полями.

    (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.
    

    как это работает? <> занимает проекция Projection2[Int, String] и возвращает отображенную проекцию на тип Bar. Два аргумента Bar, Bar.unapply _ скажи слику, как это (Int, String) проекция должна быть сопоставлена с классом case.

    это двусторонний отображение;Bar это конструктор класса case, так что это информация, необходимая для перехода от (id: Int, name: String) до Bar. И unapply если вы уже догадались, это для обратного.

    где unapply откуда? Это стандартный метод Scala, доступный для любой обычный класс case-просто определение Bar дает Bar.unapply что это соковыжималки это может быть использовано, чтобы получить обратно id и name что Bar был построен с:

    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)]
    

    таким образом, ваша проекция по умолчанию может быть сопоставлена с классом case, который вы больше всего ожидаете использовать:

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

    или вы даже можете иметь его в запрос:

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

    здесь типа q1 это Query С mapped проекция Baz. При вызове он возвращает List на Baz объекты:

     val result: List[Baz] = q1.list
    
  3. наконец, как в сторону, то .? предложения Подъем - путь Скала работа с ценностями, которых может и не быть.

     (id ~ name)   // Projection2[Int, String] // this is just for illustration
     (id.? ~ name) // Projection2[Option[Int], String]
    

    который, завершая, будет хорошо работать с вашим первоначальным определением 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. в ответ на комментарий о том, как ловкач использует for осмысленностей:

    каким-то образом монады всегда умудряются появляться и требовать, чтобы быть частью объяснения...

    для понимания не являются специфичными только для коллекций. Они могут быть использованы на любом виде монады, и коллекции просто один из многих видов типов монад, доступных в Scala.

    но как коллекции знакомы, они делают хороший старт точка для объяснения:

    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, ...)
    

    в Scala a для понимания является синтаксическим сахаром для метод (возможно вложенный) вызовы метода: приведенный выше код является (более или менее) эквивалентным:

    ns.filter(i => i*i % 2 == 0).map(i => i*i)
    

    в принципе, ничего с filter,map,flatMap методы (другими словами, монады) можно использовать в a for понимание вместо ns. Хороший пример это опции монады. Вот предыдущий пример где же это for оператор работает на обоих List а также Option монады:

    // (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
    

    в последнем примере, преобразование будет возможно вот так:

    // 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)) 
    

    в Slick запросы являются монадическими-это просто объекты с элемент map,flatMap и filter методы. Так что for понимание (показано в объяснении * метод) просто переводится в:

    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
    

    Как видите, flatMap,map и filter используются для сгенерируйте Query повторным преобразованием Query(Bars) с каждым вызовом filter и map. В случае коллекции эти методы фактически повторяют и фильтруют коллекцию но в Slick они используются для генерации SQL. Подробнее здесь: как Scala Slick перевести код Scala в JDBC?

Так как никто больше не ответил, Это может помочь вам начать работу. Я не очень хорошо знаю Слика.

с Слик документации:

Поднял Встраивания:

для каждой таблицы требуется * метод, содержащий проекцию по умолчанию. Этот описывает, что вы получаете обратно, когда возвращаете строки (в виде объект table) запроса. * Проекция Слика не должна совпадения в базе данных. Вы можно добавить новые столбцы (например, с помощью вычисленные значения) или опустить некоторые столбцы, как вам нравится. Не-поднятый тип соответствующая проекция задается в качестве параметра типа Стол. Для простых, не сопоставленных таблиц это будет один столбец тип или набор типов столбцов.

другими словами, Слик должен знать, как обращаться со строкой, возвращенной из базы данных. Метод, который вы определили, использует их функции комбинатора синтаксического анализатора для объединения определений столбцов в что-то, что можно использовать в строке.

Comments

    Ничего не найдено.