Slick: Model Option[(col1, col2)], 所以两者都缺失或都被定义

Slick: Model Option[(col1, col2)], so that both are missing or both defined

我有 table 多个 Option[X] 列。我想模拟这样一个事实,即对于这些列,定义了 all 或缺少 all。如何做到这一点,鉴于列被定义为个人 Option[X]?

def startDate = column[Option[LocalDate]]("start_date", O.Default(None))

def endDate = column[Option[LocalDate]]("end_date", O.Default(None))

def dateRange: Rep[Option[(LocalDate,LocalDate)]] = ??? // How to make something like this?

您可以在使用 Slick 构建投影时嵌套映射。也就是说,您可以定义 * 以包含其他 mapTo(对于案例 class)或 <> 调用(对于自定义映射)。

使用 Int 而不是 LocalDate 的示例, 我们可以从我们想要处理的案例 class 开始:

case class Row(
  name  : String,
  range : Option[(Int, Int)],
  id    : Long = 0L
)

注意 range 字段是单个值(元组),但在数据库中我们可以将其表示为两个列,分别称为 startend:

class RowTable(tag: Tag) extends Table[Row](tag, "row") {
  def id    = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def name  = column[String]("name")
  def start = column[Option[Int]]("start")
  def end   = column[Option[Int]]("end")

  def range = (start, end) <> (into_range, from_range)

  def * = (name, range, id).mapTo[Row]
}

Row* 投影是从我称为 range 的另一个映射构建的。

range 映射位于 startend 两列上。我们提供给 <> 的是两个函数:一个函数将一对选项转换成我们想要的类型(元组的一个选项);另一个功能则相反。这些函数反映了标准库函数 tupledunappyunapply 是一个 extractor)。

我们可以随心所欲地编写这些函数,我已经用模式匹配将它们写出来了:

def into_range(pair: (Option[Int], Option[Int])): Option[(Int, Int)] =
  pair match {
    case (Some(x), Some(y)) => Some((x, y))
    case _                  => None
  }

def from_range(r: Option[(Int, Int)]): Option[(Option[Int], Option[Int])] =
  r match {
     case Some((x, y)) => Some((Some(x), Some(y)))
     case _            => Some((None, None))
  }

总之,您的 dateRange 等同于此示例中的 range 映射,但我们没有直接调用它,而是将其包含在我们的默认投影 * 中。