Search code examples
scalaslickslick-3.0

Create custom column mapping for java.time.LocalDate with Slick


I'm using Slick 3.1.0-M2 and I wish to use the java.time.LocalDate and java.time.LocalTime in my tables. I do it like this:

import java.sql.{Date, Time, Timestamp}
import java.time.{LocalDate, LocalTime, LocalDateTime, ZoneOffset}

trait DateTimeColumns {

  import slick.driver.PostgresDriver.api._

  implicit val localDateTimeColumnType = MappedColumnType.base[LocalDateTime, Timestamp](
    d => Timestamp.from(d.toInstant(ZoneOffset.ofHours(0))),
    d => d.toLocalDateTime
  )

  implicit val dateColumnType = MappedColumnType.base[LocalDate, Date](
    d => Date.valueOf(d),
    d => d.toLocalDate
  )

  implicit val timeColumnType = MappedColumnType.base[LocalTime, Time](
    localTime => Time.valueOf(localTime),
    time => time.toLocalTime
  )
}

So I have 3 implicit mappings but only the first one compiles. The ones with java.sql.Date and java.sql.Time fails the compilation with:

could not find implicit value for evidence parameter of type slick.driver.PostgresDriver.BaseColumnType[java.sql.Date]

When I do a implicit parameter inspection in IntelliJ I can see that the first mapping finds TimestampJdbcType in the file JdbcTypesComponent.scala. Right next to that I see the TimeJdbcType and DateJdbcType. So how come the first one is found but the others aren't?


Solution

  • If you check the slick.driver.JdbcTypesComponent, you will find a trait ImplicitColumnTypes contains lots of implicits for column type, including:

    implicit def timeColumnType = columnTypes.timeJdbcType
    implicit def dateColumnType = columnTypes.dateJdbcType
    

    The latter two you defined actually have the same name with the default ones.

    Changing the name of them or renaming the default ones when importing works for me.

    import slick.driver.PostgresDriver.api._
    import slick.driver.PostgresDriver.api.{ timeColumnType => DefaultTimeColumnType }
    
    implicit val myDateColumnType = MappedColumnType.base[LocalDate, Date](
        ld => Date.valueOf(ld),
        d => d.toLocalDate
    )
    
    implicit val timeColumnType = MappedColumnType.base[LocalTime, Time](
        localTime => Time.valueOf(localTime),
        time => time.toLocalTime
    )