2012-12-28 23 views
27

Sto tentando di imparare a usare Slick per interrogare MySQL. Ho la seguente tipo di query al lavoro per ottenere una sola visita oggetto:Query slick Scala dove nella lista

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

Quello che vorrei sapere è come posso cambiare il sopra per interrogare per ottenere un elenco di [Visita] per una raccolta di posizioni. ..something come questo:

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) 

questo è possibile con Slick?

+0

Non funziona? Dovrebbe funzionare. –

+0

Non riesci ad attenersi a Tuples di valori? Garantire che il numero di parametri che si passa alla query sia corretto. –

risposta

28

Come suggerisce l'altra risposta, questo è scomodo da fare con le query statiche. L'interfaccia di query statica richiede di descrivere i parametri di binding come Product. (Int, Int, String*) non è valido scala, e l'utilizzo di (Int,Int,List[String]) richiede anche alcuni kludges. Inoltre, dovendo garantire che locationCodes.size sia sempre uguale al numero di (?, ?...) che hai nella tua query è fragile.

In pratica, questo non è un problema, perché invece si desidera utilizzare la query monad, che è il tipo sicuro e consigliato per utilizzare 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 

Ciò presuppone che avete i vostri tavoli sono allestiti in questo modo:

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 _) 
} 

Nota che si può sempre avvolgere la query in un metodo.

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 
+0

Sembra promettente. Non tornerò al lavoro fino a mercoledì, ma cercherò di trovare un po 'di tempo per testarlo prima di allora e riferirò. – ShatyUT

+0

È anche possibile utilizzare s.th. come Parameters [List [Int]] a e quindi sostituire "def byIdAndLocations" con QueryTemplate "val byIdAndLocations"? – longliveenduro

+0

Non puoi usare 'val ids = List (1,2,3) | val result: DBIO [Seq [T]] = query.filter (_. id inSet id) 'per http://stackoverflow.com/questions/17408444/is-it-possibile-per-utilizzare-in-clause-in -plain-sql-slick # comment52439626_17422901? –

5

Non funziona perché il StaticQuery object (Q) prevede di impostare i parametri implicitamente nella stringa di query, utilizzando i parametri di tipo del metodo query per creare una sorta di oggetto setter (di tipo scala.slick.jdbc.SetParameter[T]).
Il ruolo di SetParameter[T] consiste nel impostare un parametro di query su un valore di tipo T, in cui i tipi richiesti sono presi dai parametri di tipo query[...].

Da quello che vedo non c'è tale oggetto definito per T = List[A] per un generico A, e mi sembra una scelta sensata, dal momento che non si può effettivamente dare una query SQL con un elenco dinamico di parametri per la IN (?, ?, ?,...) clausola di


ho fatto un esperimento, fornendo un valore così implicitamente attraverso il seguente codice

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] 

con questa portata, si dovrebbe essere in grado di eseguire il codice

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) 

Ma è necessario garantire sempre manualmente che la dimensione locationCodes è lo stesso come il numero di ? nel vostro IN clausola


Alla fine credo che una soluzione più pulito potrebbe essere creato utilizzando le macro, generalizzare sul tipo di sequenza. Ma non sono sicuro che sarebbe una scelta saggia per il framework, dati i suddetti problemi con la natura dinamica della dimensione della sequenza.

3

È possibile generare nella clausola automaticly come questo:

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) 
    } 

E utilizzare setParameter implicita come 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] 
2

Se si dispone di una query complessa e per la comprensione di cui sopra non è disponibile un'opzione, è possibile fare qualcosa di simile a quanto segue in Slick 3. Ma è necessario assicurarsi di convalidare i dati nel parametro della query elenco da soli per evitare l'iniezione SQL:

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

Il # davanti al riferimento variabile disabilita la convalida del tipo e ti permette di risolvere questo senza fornire una funzione per la conversione implicita del parametro di query lista.