斯卡拉光滑查询其中列表(Scala slick query where in list)

2019-08-04 20:53发布

我试图要学会用油滑查询的MySQL。 我有查询的以下类型的工作,以得到一个访问对象:

Q.query[(Int,Int), Visit]("""
    select * from visit where vistor = ? and location_code = ?
""").firstOption(visitorId,locationCode)

我想知道的是我如何改变上述查询得到一个列表[观看]对于位置的集合......是这样的:

val locationCodes = List("loc1","loc2","loc3"...)
Q.query[(Int,Int,List[String]), Visit]("""
    select * from visit where vistor = ? and location_code in (?,?,?...)
""").list(visitorId,locationCodes)

这可能与油滑?

Answer 1:

由于对方的回答表明,这是很麻烦的静态查询做。 静态查询界面,需要你来描述绑定参数的Product(Int, Int, String*)是无效的阶,以及使用(Int,Int,List[String])需要一些组装机为好。 此外,必须确保locationCodes.size总是等于数量(?, ?...)在查询中有脆。

在实践中,因为要使用查询单子,而不是,这是用油滑的类型安全的,推荐的方式,这不是太大的问题。

val visitorId: Int = // whatever
val locationCodes = List("loc1","loc2","loc3"...)
// your query, with bind params.
val q = for {
    v <- Visits 
    if v.visitor is visitorId.bind
    if v.location_code inSetBind locationCodes
  } yield v
// have a look at the generated query.
println(q.selectStatement)
// run the query
q.list

这是假设你有你的表格设置是这样的:

case class Visitor(visitor: Int, ... location_code: String)

object Visitors extends Table[Visitor]("visitor") {
  def visitor = column[Int]("visitor")
  def location_code = column[String]("location_code")
  // .. etc
  def * = visitor ~ .. ~ location_code <> (Visitor, Visitor.unapply _)
}

请注意,您可以随时换您的查询的方法。

def byIdAndLocations(visitorId: Int, locationCodes: List[String]) = 
  for {
    v <- Visits 
    if v.visitor is visitorId.bind
    if v.location_code inSetBind locationCodes
  } yield v
}

byIdAndLocations(visitorId, List("loc1", "loc2", ..)) list


Answer 2:

它不工作,因为StaticQuery objectQ )期望隐式设置在查询字符串参数,使用的类型参数query的方法来创建一种设置器对象(类型的scala.slick.jdbc.SetParameter[T] )。
的作用SetParameter[T]是于查询参数设置为类型的值T ,其中所需要的类型从所拍摄的query[...]类型参数。

从我看到有一个为定义没有这样的对象T = List[A]一个通用的A ,它似乎是一个明智的选择,因为你不能实际编写与该参数的动态列表的SQL查询IN (?, ?, ?,...)子句


我通过下面的代码提供这样一个隐含的价值做了一个实验

import scala.slick.jdbc.{SetParameter, StaticQuery => Q}

def seqParam[A](implicit pconv: SetParameter[A]): SetParameter[Seq[A]] = SetParameter {  
    case (seq, pp) =>
        for (a <- seq) {
            pconv.apply(a, pp)
        }
}

implicit val listSP: SetParameter[List[String]] = seqParam[String]

这个范围,你应该能够执行代码

val locationCodes = List("loc1","loc2","loc3"...)
Q.query[(Int,Int,List[String]), Visit]("""
    select * from visit where vistor = ? and location_code in (?,?,?...)
""").list(visitorId,locationCodes)

但是,你必须手动保证locationCodes大小相同的数量? 在你的IN子句


最后,我认为,一个更清洁的解决方法可以使用宏,概括的序列类型来创建。 但我不知道这将是一个框架一个明智的选择,因为与序列大小的动态性质的上述问题。



Answer 3:

您可以在第全自动这样产生:

  def find(id: List[Long])(implicit options: QueryOptions) = {
    val in = ("?," * id.size).dropRight(1)
    Q.query[List[Long], FullCard](s"""
        select 
            o.id, o.name 
        from 
            organization o
        where
            o.id in ($in)
        limit
            ?
        offset
            ?
            """).list(id ::: options.limits)
  }

而使用隐式的setParameter作为pagoda_5b说

  def seqParam[A](implicit pconv: SetParameter[A]): SetParameter[Seq[A]] = SetParameter {
    case (seq, pp) =>
      for (a <- seq) {
        pconv.apply(a, pp)
      }
  }

  implicit def setLongList = seqParam[Long]


Answer 4:

如果你有一个复杂的查询和上面提到的理解是不是一种选择,你可以这样做在油滑3.下列但是,你需要确保你在你的列表查询自己的参数来防止SQL注入验证数据:

val locationCodes = "'" + List("loc1","loc2","loc3").mkString("','") + "'"
sql"""
  select * from visit where visitor = $visitor 
    and location_code in (#$locationCodes)
"""

在可变参考前面的#禁用类型验证并允许您解决这个不为列表查询参数的隐式转换供给的功能。



文章来源: Scala slick query where in list