【问题标题】:Scala slick query where in listScala 光滑查询在列表中的位置
【发布时间】:2012-12-13 09:59:16
【问题描述】:

我正在尝试学习使用 Slick 来查询 MySQL。我有以下类型的查询来获取单个访问对象:

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

我想知道的是如何更改上述内容以查询以获取位置集合的 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)

Slick 可以做到这一点吗?

【问题讨论】:

  • 这不行吗?应该工作。
  • 你不能坚持使用元组值吗?它将保证您传递给查询的参数数量是固定的。

标签: scala typesafe-stack slick


【解决方案1】:

正如另一个答案所暗示的,这对于静态查询来说很麻烦。静态查询接口要求您将绑定参数描述为Product(Int, Int, String*) 不是有效的 scala,使用 (Int,Int,List[String]) 也需要一些麻烦。此外,必须确保 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

这是假设您的表格设置如下:

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

【讨论】:

  • 这看起来很有希望。我要到星期三才回来上班,但我会在此之前找点时间测试一下,然后再报告。
  • 是否也可以使用s.th。像 Parameters[List[Int]] 到然后用 QueryTemplate "val byIdAndLocations" 替换 "def byIdAndLocations" ?
  • 你不能使用val ids = List(1,2,3) | val result: DBIO[Seq[T]] = query.filter(_.id inSet ids) per stackoverflow.com/questions/17408444/…吗?
【解决方案2】:

它不起作用,因为StaticQuery object (Q) 期望隐式设置查询字符串中的参数,使用query 方法的类型参数来创建一种setter 对象(@ 类型987654321@).
SetParameter[T]的作用是将查询参数设置为T类型的值,其中所需的类型取自query[...]类型参数。

据我所见,对于通用A,没有为T = List[A] 定义这样的对象,这似乎是一个明智的选择,因为您实际上不能为@987654331 编写带有动态参数列表的sql 查询@子句


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

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 子句中? 的数量相同


最后,我相信可以使用宏创建更简洁的解决方法,以概括序列类型。但考虑到上述序列大小的动态性质问题,我不确定这是否是框架的明智选择。

【讨论】:

    【解决方案3】:

    你可以像这样自动生成in子句:

      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 says

      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]
    

    【讨论】:

      【解决方案4】:

      如果您有一个复杂的查询并且上面提到的 for 理解不是一个选项,您可以在 Slick 3 中执行以下操作。但是您需要确保自己验证列表查询参数中的数据以防止 SQL注入:

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

      变量引用前面的# 禁用类型验证,并允许您在不提供用于列表查询参数的隐式转换的函数的情况下解决此问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-07-09
        • 2018-02-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多