postgresqlscalaslickslick-pg

Scala Slick compose column type mappers


I'm using slick-pg which adds support(with implicits) for the List and DateTime types in slick.

Unfortunately I cannot use List[DateTime] - slick does not understand composition of those types, but I've checked that both work correctly on their own(for example List[Int] and DateTime).

Is there a way to easily compose those two implicits?


Solution

  • 1. Try to add

      implicit def dateTimeList =
        MappedColumnType.base[List[DateTime], List[Timestamp]](
          _.map(dt => new Timestamp(dt.getMillis)),
          _.map(ts => new DateTime(ts.getTime))
        )
    

    Just in case, the whole code that compiles:

    import java.sql.Timestamp
    import org.joda.time.DateTime
    import slick.jdbc.PostgresProfile.api._
    import slick.lifted.ProvenShape
    import slick.basic.Capability
    import slick.jdbc.JdbcCapabilities
    import com.github.tototoshi.slick.PostgresJodaSupport._
    import com.github.tminglei.slickpg._
    
    object App {
    
      trait MyPostgresProfile extends ExPostgresProfile
        with PgArraySupport
        with PgDate2Support
        with PgRangeSupport
        with PgHStoreSupport
    //    with PgPlayJsonSupport
        with PgSearchSupport
    //    with PgPostGISSupport
        with PgNetSupport
        with PgLTreeSupport {
        def pgjson = "jsonb" // jsonb support is in postgres 9.4.0 onward; for 9.3.x use "json"
    
        // Add back `capabilities.insertOrUpdate` to enable native `upsert` support; for postgres 9.5+
        override protected def computeCapabilities: Set[Capability] =
          super.computeCapabilities + /*JdbcProfile.capabilities.insertOrUpdate*/ JdbcCapabilities.insertOrUpdate
    
        override val api = MyAPI
    
        object MyAPI extends API with ArrayImplicits
          with DateTimeImplicits
    //      with JsonImplicits
          with NetImplicits
          with LTreeImplicits
          with RangeImplicits
          with HStoreImplicits
          with SearchImplicits
          with SearchAssistants {
          implicit val strListTypeMapper = new SimpleArrayJdbcType[String]("text").to(_.toList)
    //      implicit val playJsonArrayTypeMapper =
    //        new AdvancedArrayJdbcType[JsValue](pgjson,
    //          (s) => utils.SimpleArrayUtils.fromString[JsValue](Json.parse(_))(s).orNull,
    //          (v) => utils.SimpleArrayUtils.mkString[JsValue](_.toString())(v)
    //        ).to(_.toList)
        }
      }
    
      object MyPostgresProfile extends MyPostgresProfile
    
      import MyPostgresProfile.api._
    
       // This can be used instead of slick-joda-mapper library 
    //  implicit def dateTime =
    //    MappedColumnType.base[DateTime, Timestamp](
    //      dt => new Timestamp(dt.getMillis),
    //      ts => new DateTime(ts.getTime)
    //    )
    
      implicit def dateTimeList =
        MappedColumnType.base[List[DateTime], List[Timestamp]](
          _.map(dt => new Timestamp(dt.getMillis)),
          _.map(ts => new DateTime(ts.getTime))
        )
    
      case class Record(id: Int, name: String, friends: List[Int], registered: DateTime, visits: List[DateTime])
    
      class RecordTable(tag: Tag) extends Table[Record](tag, Some("public"), "records") {
        def id: Rep[Int] = column[Int]("id", O.PrimaryKey, O.AutoInc)
        def name: Rep[String] = column[String]("name")
        def friends: Rep[List[Int]] = column[List[Int]]("friends")
        def registered: Rep[DateTime] = column[DateTime]("registered")
        def visits: Rep[List[DateTime]] = column[List[DateTime]]("visits")
        def * : ProvenShape[Record] = (id, name, friends, registered, visits) <> (Record.tupled, Record.unapply)
      }
    
      val records: TableQuery[RecordTable] = TableQuery[RecordTable]
    }
    

    build.sbt

    name := "slickdemo"
    
    version := "0.1"
    
    scalaVersion := "2.12.3"
    
    libraryDependencies += "com.typesafe.slick" %% "slick" % "3.2.1"
    libraryDependencies += "org.slf4j" % "slf4j-nop" % "1.7.25"
    libraryDependencies += "com.typesafe.slick" %% "slick-hikaricp" % "3.2.1"
    
    libraryDependencies += "org.postgresql" % "postgresql" % "42.1.4"
    
    libraryDependencies += "com.github.tminglei" %% "slick-pg" % "0.15.3"
    
    libraryDependencies += "joda-time" % "joda-time" % "2.9.9"
    libraryDependencies += "org.joda" % "joda-convert" % "1.9.2"
    libraryDependencies += "com.github.tototoshi" % "slick-joda-mapper_2.12" % "2.3.0"
    

    Based on answer and documentation.


    2. Alternatively you can add

        implicit val dateTimeArrayTypeMapper =
            new AdvancedArrayJdbcType[DateTime]("timestamp",
              (s) => utils.SimpleArrayUtils.fromString[DateTime](DateTime.parse)(s).orNull,
              (v) => utils.SimpleArrayUtils.mkString[DateTime](_.toString)(v)
            ).to(_.toList)
    

    after strListTypeMapper and playJsonArrayTypeMapper.