【问题标题】:Plain SQL in Slick - How do I map a postgres array column to a List?Slick 中的普通 SQL - 如何将 postgres 数组列映射到列表?
【发布时间】:2020-01-11 12:52:24
【问题描述】:

我需要使用纯 sql 在 Slick 中进行查询,然后将结果行转换为 db 模型。我无法弄清楚如何将数组 postgres 列(在本例中为字符串数组)转换为 scala 列表。

查询示例

sql"""SELECT n.id, n.created_at, n.values
      FROM names n
      WHERE n.id = $id""".as[NamesDB])

其中 values 是一个字符串数组。

数据库模型:

case class NamesDB(
  id: Long
  createdAt: Timestamp,
  values: List[String]
)

和表定义:

class Names(tag: Tag) extends Table[NamesDB](tag, "names"){

  def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
  def createdAt = column[Timestamp]("created_at")
  def values = column[List[String]]("values")

  override def * = (
    id,
    createdAt,
    values
  ) <> ((NamesDB.apply _).tupled, NamesDB.unapply)
}

这行得通,但感觉必须有更好的方法:

implicit val NamesDBRecord = GetResult(r => {
    val id = r.nextLong()
    val createdAt = r.nextTimestamp()
    val values = r.nextString() // "{name1, name2}"
                .drop(1).dropRight(1)
                .split(",").toList // 

    NamesDB(id, createdAt, values)
  })

我一直在尝试阅读 slick 的源代码,以了解他们如何将 pg 列类型转换为 scala/java 类型,但不清楚在哪里可以找到它。

【问题讨论】:

    标签: sql postgresql scala slick implicit


    【解决方案1】:

    尝试使用https://github.com/tminglei/slick-pg

    import java.sql.Timestamp
    import com.github.tminglei.slickpg._
    
    trait MyPostgresProfile extends ExPostgresProfile with PgArraySupport {
    
      override val api = MyAPI
    
      object MyAPI extends API with ArrayImplicits 
    }
    
    object MyPostgresProfile extends MyPostgresProfile
    
    import MyPostgresProfile.api._
    
    case class NamesDB(
                        id: Long,
                        createdAt: Timestamp,
                        values: List[String]
                      )
    
    class Names(tag: Tag) extends Table[NamesDB](tag, "names"){
    
      def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
      def createdAt = column[Timestamp]("created_at")
      def values = column[List[String]]("values")
    
      override def * = (
        id,
        createdAt,
        values
      ) <> ((NamesDB.apply _).tupled, NamesDB.unapply)
    }
    

    libraryDependencies += "com.github.tminglei" %% "slick-pg" % "0.18.0"

    对于普通的 sql 试试

    implicit val strList = GetResult[List[String]] (
      r => (1 to r.numColumns).map(_ => r.nextString()).toList
    )
    implicit val NamesDBRecord = GetResult(r => NamesDB(r.<<, r.<<, r.<<))
    
    sql"""SELECT n.id, n.created_at, n.values
        FROM names n
        WHERE n.id = $id""".as[NamesDB]
    

    【讨论】:

    • 这实际上已经在我们的项目中了,并且在使用 slick-generated sql 时它就像一个魅力。但是,有没有办法使用此设置来转换从普通 sql 查询返回的 PositionedResult?
    • 知道了,这导致了一个有效的答案,我将发布。谢谢。
    【解决方案2】:

    解决方案:

    检查 pg_slick 库,我发现:

    private def simpleNextArray[T](r: PositionedResult): Option[Seq[T]] = {
      val value = r.rs.getArray(r.skip.currentPos)
      if (r.rs.wasNull) None else Some(
        value.getArray.asInstanceOf[Array[Any]].map(_.asInstanceOf[T]))
    }
    

    导致这个实现:

    implicit val strList = GetResult[List[String]] (r =>
      r.rs.getArray(r.skip.currentPos)
       .getArray
       .asInstanceOf[Array[Any]]
       .toList
       .map(_.toString())
    )
    
    implicit val getNamesDB = GetResult(r =>
      NamesDB(r.<<, r.<<, r.<<)
    )
    

    【讨论】:

      猜你喜欢
      • 2021-05-20
      • 1970-01-01
      • 2017-10-02
      • 1970-01-01
      • 1970-01-01
      • 2021-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多