Слайк слева/справа/внешний соединяется с опцией
В примерах Slick есть несколько примеров объединения, когда один из результирующих столбцов может быть нулем, так как это может иметь место при выполнении левого, правого или внешнего объединений. Например:
val explicitLeftOuterJoin = for {
(c, s) <- Coffees leftJoin Suppliers on (_.supID === _.id)
} yield (c.name, s.name.?)
Но что, если я хочу вернуть весь отображаемый объект? Я имею в виду:
val explicitLeftOuterJoin = for {
(c, s) <- Coffees leftJoin Suppliers on (_.supID === _.id)
} yield (c, s.?)
Это не работает, поскольку он жалуется на "не смог найти неявное значение для параметра доказательства типа scala.slick.lifted.TypeMapper [Suppliers]". В основном я бы хотел, чтобы он вернул список кортежей (Coffee, Option [Supplier])
Почему это не работает и что за это исправление? Тем более, что это прекрасно работает:
val q = for {
c <- Coffees
s <- Suppliers
} yield (c, s)
(Я знаю, что внутреннее соединение)
Ответы
Ответ 1
ОБНОВЛЕНИЕ:. Это будет разрешено и просто будет работать в Slick 3.0 на конец 2014 года, больше нет необходимости в следующем обходном пути
Это ограничение Slick в данный момент. Вы должны позвонить.? по каждому столбцу отдельно. Тем не менее, вы можете поместить функцию под названием ?
в класс таблицы, которая делает это в центральном месте и тем самым получает.? на полных строках. Этот примерный примерный код содержит общее решение, включающее некоторый сгенерированный код. У нас также есть PR, который добавляет автогенерацию? метод.
В конечном итоге мы поддержим вариант внешних соединений в Slick, где Slick полностью осведомлен о задействованных типах, и вам не нужно указывать.? в любом месте. На данный момент нам приходится сталкиваться с обходными решениями, связанными с генерированием кода.
Ответ 2
Не самое чистое решение (использует scalaz 7.0.6 и бесформенный 2.0.1), но теперь это работает (Slick 2.0.1):
Используя вышеприведенный проект ?
, можно создать проекцию Slick, которая преобразует кортеж Option
values = > Option[TupleN]
= > Option[YourClass]
.
Добавить Option
Проекция
Примечание. sequence
используется для преобразования кортежа значений Option
в Option[TupleN]
. Код для sequence
определяется в нижней части этого ответа.
Добавить в Suppliers
. Предполагается, что Supplier
- это класс case.
import scalaz._, Scalaz._
import SequenceTupleOption._
def option = (id.?, name.?, street.?) <> (optionApply, optionUnapply)
def optionApply(t: (Option[Int], Option[String], Option[String])): Option[Comment] = {
sequence(t).map(Supplier.tupled)
}
def optionUnapply(oc: Option[Supplier]): Option[(Option[Int], Option[String], Option[String])] = None
Использование Option
Проекция
val explicitLeftOuterJoin = for {
(c, s) <- Coffees leftJoin Suppliers on (_.supID === _.id)
} yield (c, s.option)
Дополнительно: sequence
, конвертирует кортеж Option
значения в Option[TupleN]
Это трудная часть, которую написал Травис Браун. sequence
преобразует из кортежа значений Option
в Option[TupleN]
(используя scalaz и бесформенный).
import scalaz._, Scalaz._
import shapeless._, ops.hlist.{ RightFolder, Tupler }
object SequenceTupleOption {
object applicativeFolder extends Poly2 {
implicit def caseApplicative[A, B <: HList, F[_]](implicit
app: Applicative[F]
) = at[F[A], F[B]] {
(a, b) => app.ap(a)(app.map(b)(bb => (_: A) :: bb))
}
}
def sequence[T, EL <: HList, L <: HList, OL <: HList, OT](t: T)(implicit
gen: Generic.Aux[T, EL],
eq: EL =:= L,
folder: RightFolder.Aux[L, Option[HNil], applicativeFolder.type, Option[OL]],
tupler: Tupler.Aux[OL, OT]
): Option[OT] =
eq(gen.to(t)).foldRight(some(HNil: HNil))(applicativeFolder).map(tupler(_))
}
Использование sequence
:
import scalaz._, Scalaz._
import SequenceTupleOption._
case class Person(id: Int, name: String, age: Int)
val t = (Option(1), Option("Bob"), Option(40))
val person: Option[Person] = sequence(t).map(Person.tupled) // Some(Person(1,Bob,40))
Высокоуровневый обзор того, что делает sequence
(не соответствующие типы):
- Преобразует кортеж
Option
в бесформенный HList[Option[_]]
.
-
sequence
над HList[Option[_]]
до Option[HList[_]]
- Преобразуйте
HList
обратно в кортеж.
Ответ 3
В дополнение к ответу выше:
Если у вас есть класс, который расширяет таблицу, а ваша проекция выглядит примерно так:
def * = (col1, col2, col3)
чем ваш? функция будет выглядеть так:
def ? = (col1.?, col2.?, col3.?)
Если вы определили такую функцию, вы можете написать:
for {
(x,y) <- x leftJoin y on (...)
} yield (x, y.?)
Ответ 4
В Slick 3.1.1 правильный ответ будет просто (как упоминалось в некоторых комментариях):
for {
(c, s) <- coffees joinLeft suppliers on (_.supID === _.id)
} yield (c, s)