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?
2 ответов:
[обновление] -добавить (еще одно) объяснение
forгенераторы
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понимания.теперь о
<>и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наконец, как в сторону, то
.?предложения Подъем - путь Скала работа с ценностями, которых может и не быть.(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]в ответ на комментарий о том, как ловкач использует
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методы (другими словами, монады) можно использовать в aforпонимание вместо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