如何将具有非默认字段类型的案例 class 映射到 Slick 中的 table?

How can I map a case class with non-default field types to a table in Slick?

我可以将案例 class 映射到灵活的数据库 table:-

case class SomeTimeStamp(id: Option[Long], timestamp: java.sql.Timestamp )

class TimeStampTable(tag: Tag) extends Table[SomeTimeStamp](tag, "TSTAMP_TABLE") {
  def id    = column[Long]("ID", O.AutoInc, O.PrimaryKey)
  def time  = column[java.sql.Timestamp]("TIME")

  def * = (id.?, time) <> (SomeTimeStamp.tupled, SomeTimeStamp.unapply)
}

案例class有slick默认转换为数据库类型的字段,所以一切都很好。

但这不起作用,如果我的字段 class 不是默认数据库类型。 Slick 允许我使用 MappedColumnType 提供与有效数据库类型之间的隐式转换,所以我尝试了这个,请注意我现在使用 Java LocalDatetime 而不是 sql 时间戳。

case class SomeLocalDate(id: Option[Long], timestamp: java.time.LocalDateTime )

class LocalDateTable(tag: Tag) extends Table[SomeLocalDate](tag, "TLDATE_TABLE") {
import LocalDateTable._
  def id    = column[Long]("ID", O.AutoInc, O.PrimaryKey)
  def time  = column[java.time.LocalDateTime]("TIME")

  def * = (id.?, time) <> (SomeLocalDate.tupled, SomeLocalDate.unapply)
}

object LocalDateTable {
  implicit val localDateTimeToTimestamp = MappedColumnType.base[LocalDateTime, Timestamp](
    { Timestamp.valueOf(_) } ,
    { ts => ts.toLocalDateTime }
  )
}

我已经添加和隐式映射列我仍然在投影和隐式解析中遇到编译错误 编译错误为:-

[info] Compiling 1 Scala source to ...target/scala-2.11/classes...
[error] db/Tables.scala:92: could not find implicit value for parameter tt: slick.ast.TypedType[java.time.LocalDateTime]
[error]   def time: Rep[LocalDateTime]  = column[java.time.LocalDateTime]("TIME")

如果我修改 def 时间并添加类型归属,我会得到不同的错误,请参见下文。

case class SomeLocalDate(id: Option[Long], timestamp: java.time.LocalDateTime )

class LocalDateTable(tag: Tag) extends Table[SomeLocalDate](tag, "TLDATE_TABLE") {
import LocalDateTable._
  def id    = column[Long]("ID", O.AutoInc, O.PrimaryKey)
  def time: Rep[LocalDateTime]  = column[java.time.LocalDateTime]("TIME")

  def * = (id.?, time) <> (SomeLocalDate.tupled, SomeLocalDate.unapply)
}

object LocalDateTable {
  implicit val localDateTimeToTimestamp = MappedColumnType.base[LocalDateTime, Timestamp](
    { Timestamp.valueOf(_) } ,
    { ts => ts.toLocalDateTime }
  )
}

给出这个错误:-

[info] Compiling 1 Scala source to ...target/scala-2.11/classes...
[error] db/Tables.scala:92: could not find implicit value for parameter tt: slick.ast.TypedType[java.time.LocalDateTime]
[error]   def time: Rep[LocalDateTime]  = column[java.time.LocalDateTime]("TIME")
[error]                                                                  ^
[error]db/Tables.scala:94: No matching Shape found.
[error] Slick does not know how to map the given types.
[error] Possible causes: T in Table[T] does not match your * projection. Or you use an unsupported type in a Query (e.g. scala List).
[error]   Required level: slick.lifted.FlatShapeLevel
[error]      Source type: (slick.lifted.Rep[Option[Long]], slick.lifted.Rep[java.time.LocalDateTime])
[error]    Unpacked type: (Option[Long], java.time.LocalDateTime)
[error]      Packed type: Any
[error]   def * = (id.?, time) <> (SomeLocalDate.tupled, SomeLocalDate.unapply)
[error]                        ^
[error] two errors found

如果案例 class 没有默认支持的数据库类型(例如 LocalDateTime),我需要做什么才能将案例 class 映射到 table?

干杯

经过几天的努力,我找到了答案:-/

问题是在 Table class 之后声明了隐式 MappedColumnType。出于某种原因,即使我将其导入到 table.

上方,这也会打乱隐式分辨率

当我删除大小写 class 并简单地使用元组时,我找到了答案。当我这样做时,出现了以下错误

[info] Compiling 1 Scala source to target/scala-2.11/classes...
[error] /db/Tables.scala:100: could not find implicit value for parameter tt: slick.ast.TypedType[java.time.LocalDateTime]
[error]   def time: Rep[LocalDateTime]  = column[java.time.LocalDateTime]("TIME")
[error]                                                                  ^
[error] /db/Tables.scala:102: type mismatch;
[error]  found   : (slick.lifted.Rep[Option[Long]], slick.driver.H2Driver.api.Rep[java.time.LocalDateTime])
[error]     (which expands to)  (slick.lifted.Rep[Option[Long]], slick.lifted.Rep[java.time.LocalDateTime])
[error]  required: slick.lifted.ProvenShape[(Option[Long], java.time.LocalDateTime)]
[error]  Note: implicit value localDateTimeToTimestamp is not applicable here because it comes after the application point and it lacks an explicit result type
[error]   def * : ProvenShape[(Option[Long], LocalDateTime)] = (id.?, time)
[error]                                                        ^

所以要明确解决方案是将隐式转换放在 table 定义之上(如果在同一个文件中)或将其放在另一个文件中并导入它,请参阅下面的工作解决方案。谢谢

case class SomeLocalDate(id: Option[Long], timestamp: java.time.LocalDateTime )

object LocalDateTableConversions {
  implicit val localDateTimeToTimestamp = MappedColumnType.base[LocalDateTime, Timestamp](
    { Timestamp.valueOf(_) } ,
    { ts => ts.toLocalDateTime }
  )
}
import LocalDateTableConversions._

class LocalDateTable(tag: Tag) extends Table[SomeLocalDate](tag, "TLDATE_TABLE") {
  def id    = column[Long]("ID", O.AutoInc, O.PrimaryKey)
  def time: Rep[LocalDateTime]  = column[java.time.LocalDateTime]("TIME")

  def * = (id.?, time) <> (SomeLocalDate.tupled, SomeLocalDate.unapply)
}