scala - pequeños - nombres de insectos comunes y cientificos
Scala consulta pulida donde en lista (4)
Como lo sugiere la otra respuesta, esto es complicado de hacer con las consultas estáticas. La interfaz de consulta estática requiere que describas los parámetros de enlace como un Product
. (Int, Int, String*)
no es válido scala, y el uso de (Int,Int,List[String])
necesita algunos kludges. Además, tener que asegurarse de que locationCodes.size
sea siempre igual al número de (?, ?...)
que tiene en su consulta es frágil.
En la práctica, esto no es un problema demasiado grande porque desea utilizar la mónada de consulta en su lugar, que es la forma segura y recomendada de usar 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
Esto es asumiendo que tienes tus tablas configuradas de esta manera:
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 _)
}
Tenga en cuenta que siempre puede ajustar su consulta en un método.
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
Estoy intentando aprender a usar Slick para consultar MySQL. Tengo el siguiente tipo de consulta trabajando para obtener un solo objeto Visit:
Q.query[(Int,Int), Visit]("""
select * from visit where vistor = ? and location_code = ?
""").firstOption(visitorId,locationCode)
Lo que me gustaría saber es cómo puedo cambiar lo anterior para consultar y obtener una [Visita] de una colección de ubicaciones ... algo como esto:
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)
¿Es esto posible con Slick?
No funciona porque el StaticQuery object
( Q
) espera establecer implícitamente los parámetros en la cadena de consulta, usando los parámetros de tipo del método de query
para crear una especie de objeto de establecimiento (de tipo scala.slick.jdbc.SetParameter[T]
).
La función de SetParameter[T]
es establecer un parámetro de consulta en un valor de tipo T
, donde los tipos requeridos se toman de los parámetros de tipo de consulta.
Por lo que veo, no hay tal objeto definido para T = List[A]
para una A
genérica, y parece una opción sensata, ya que no se puede escribir una consulta de SQL con una lista dinámica de parámetros para el IN (?, ?, ?,...)
cláusula
Hice un experimento proporcionando un valor implícito a través del siguiente código
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 esto en alcance, deberías poder ejecutar tu código
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)
¿Pero siempre debe garantizar manualmente que el tamaño de locationCodes
códigos de locationCodes
sea el mismo que el número de ?
en su cláusula IN
Al final, creo que se podría crear una solución más limpia utilizando macros, para generalizar en el tipo de secuencia. Pero no estoy seguro de que sea una elección acertada para el marco, dados los problemas mencionados anteriormente con la naturaleza dinámica del tamaño de la secuencia.
Puedes generar en cláusula automáticamente de esta manera:
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)
}
Y use SetParameter implícito como dice 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]
Si tiene una consulta compleja y para la comprensión mencionada anteriormente no es una opción, puede hacer algo como lo siguiente en Slick 3. Pero debe asegurarse de validar los datos en su parámetro de consulta de lista para evitar la inyección de SQL:
val locationCodes = "''" + List("loc1","loc2","loc3").mkString("'',''") + "''"
sql"""
select * from visit where visitor = $visitor
and location_code in (#$locationCodes)
"""
El # en frente de la referencia de variable desactiva la validación de tipo y le permite resolver esto sin proporcionar una función para la conversión implícita del parámetro de consulta de lista.