29

Slick を使用して MySQL をクエリする方法を学習しようとしています。単一の Visit オブジェクトを取得するために、次のタイプのクエリが機能しています。

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で可能ですか?

4

4 に答える 4

31

他の回答が示唆するように、これは静的クエリで行うのは面倒です。静的クエリ インターフェイスでは、バインド パラメータを .xml として記述する必要がありますProduct(Int, Int, String*) は有効なスカラではなく、使用(Int,Int,List[String])にはいくつかのクラッジも必要です。さらに、それlocationCodes.sizeが常にクエリの数と等しいことを確認する(?, ?...)必要があるのは脆弱です。

実際には、代わりに query モナドを使用したいので、これはそれほど問題ではありません。これはタイプセーフであり、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
于 2012-12-28T11:28:25.250 に答える
6

StaticQuery object( Q) は、メソッドの型パラメーターを使用してquery(型の) セッター オブジェクトの並べ替えを作成し、クエリ文字列にパラメーターを暗黙的に設定することを期待しているため、機能しません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]

これをスコープに入れれば、コードを実行できるはずです

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


最後に、シーケンス タイプを一般化するために、マクロを使用してよりクリーンな回避策を作成できると思います。しかし、シーケンスサイズの動的な性質に関する前述の問題を考えると、フレームワークにとって賢明な選択になるかどうかはわかりません。

于 2012-12-28T10:32:17.870 に答える
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)
  }

pagoda_5b が言うように、暗黙の SetParameter を使用します

  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]
于 2013-03-01T04:18:02.700 に答える