Scala光滑查询列表中的位置

Sha*_*yUT 28 scala typesafe-stack slick

我试图学习使用Slick来查询MySQL.我有以下类型的查询工作来获取单个Visit对象:

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

我想知道的是如何更改以上内容以查询以获取Locations集合的List [Visit] ...类似这样的内容:

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)
Run Code Online (Sandbox Code Playgroud)

Slick有可能吗?

Fai*_*aiz 29

正如另一个答案所暗示的那样,这对于静态查询来说很麻烦.静态查询接口要求您将绑定参数描述为Product.(Int, Int, String*) 是无效的scala,并且使用也(Int,Int,List[String])需要一些kludges.此外,必须确保locationCodes.size始终等于查询中的数量(?, ?...)是脆弱的.

实际上,这并不是一个问题,因为您希望使用查询monad,这是使用Slick的类型安全和推荐的方法.

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
Run Code Online (Sandbox Code Playgroud)

这假设你的表设置如下:

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 _)
}
Run Code Online (Sandbox Code Playgroud)

请注意,您始终可以将查询包装在方法中.

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
Run Code Online (Sandbox Code Playgroud)


pag*_*_5b 6

它不起作用,因为StaticQuery object(Q)期望隐式设置查询字符串中的参数,使用方法的类型参数query来创建一种setter对象(类型scala.slick.jdbc.SetParameter[T]).
其作用SetParameter[T]是将查询参数设置为type的值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]
Run Code Online (Sandbox Code Playgroud)

在范围内,您应该能够执行您的代码

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)
Run Code Online (Sandbox Code Playgroud)

但是,你必须手动保证locationCodes大小相同数量?的中IN条款


最后,我相信可以使用宏创建更清晰的解决方法,以概括序列类型.但鉴于前面提到的序列大小的动态性问题,我不确定它是否是框架的明智选择.