scala - filtros dinámicos anorm
playframework playframework-2.0 (3)
Estoy estudiando un poco la documentación del archivo (desde el marco de juego) y no está claro si es compatible con un caso de uso de consulta común: filtros dinámicos, es decir, el usuario completa 2 o 3 criterios de búsqueda en un formulario de búsqueda de 10 campos.
En este caso, ¿cómo puedo construir dinámicamente la consulta sin la manipulación clásica de cadenas?
Parece que Anorm funciona con la premisa de que se inserta un SQL simple y antiguo, sin ninguna característica como typesafety o creación de consultas. Probablemente no sea la herramienta adecuada para consultas dinámicas. La construcción de consultas tiene que ser eventualmente manipulación de cadenas, por lo que recomendaría usar una biblioteca que haga esto por usted. Debería ser fácil integrar cualquier biblioteca para generar sentencias de SQL con Anorm.
Puede construir dinámicamente una consulta y pasar esa cadena de consulta a Anorm con una biblioteca como jOOQ . Como beneficio adicional obtendrás soporte para muchas bases de datos. jOOQ parece ser popular, pero probablemente haya muchas otras bibliotecas que podrías usar en su lugar. O reemplace Anorm por completo si no es adecuado para su proyecto.
Respuesta corta primero Supongamos que dice que tiene una tabla en la base de datos que contiene 3 columnas: name
, email
, pass
. Pero del usuario, solo recibiste el name
y la password
pero no el email
. Así que haga Opciones de todos 3
val name:Option[String] = Some("alice")
val email:Option[String] = None
val pass:Option[String] = Some("password")
//For db insertion have this:
DB.withConnection { implicit conn =>
SQL("INSERT INTO USERS (name,email,pass) VALUES ({n},{e},{p})").on(
''n -> name, ''e -> email,''p -> pass).executeInsert()
}
Al hacer lo anterior, como el email
es None
, insertará null
en su base de datos. Entonces, en su caso, para todas sus 10 columnas, puede definirlas en la declaración SQL
anterior y pasar Option
en on()
. Si alguno de ellos es None
, entonces lo tomará como null
en la base de datos.
Aunque puede haber un problema si hay una restricción en una columna en su esquema como NOT NULL
. En ese caso, puede usar getOrElse
para las columnas siguientes:
DB.withConnection { implicit conn =>
SQL("INSERT INTO USERS (name,email,pass) VALUES ({n},{e},{p})").on(
''n -> name, ''e -> email.getOrElse("Default Email"),''p -> pass).executeInsert()
La siguiente es una lista de comprensión sobre cómo el juego convierte el tipo a tipo de base de datos. Se puede encontrar en el objeto anorm.ToStatement
:
case Some(bd: java.math.BigDecimal) => stmt.setBigDecimal(index, bd)
case Some(o) => stmt.setObject(index, o)
case None => stmt.setObject(index, null)
case bd: java.math.BigDecimal => stmt.setBigDecimal(index, bd)
case date: java.util.Date => stmt.setTimestamp(index, new java.sql.Timestamp(date.getTime()))
case o => stmt.setObject(index, o)
Encima de lo que ves, para None
lo toma como nulo.
En el caso de SELECT
hmm, no estoy al tanto de ninguna característica de anorm que ayude aquí, pero supongo que la simple manipulación de String podría ser suficiente:
def getColumns(xs:List[Option[_]]):String = {
val notNone = xs.collect{
case Some(x) => x.toString
}
notNone.mkString(",")
}
Y luego SQL("SELECT %s from table".format(getColumns(List(nameColumn,emailColumn,passColumn)))
.
Aunque esto no es lo que quieres. Anorm es solo una biblioteca de construcción sql. Para hacer lo que quiera, también tendrá que recordar el esquema de su tabla (es decir, al menos los nombres de las columnas ...). No creo que Anorm esté hecha para hacer todo eso
Sí, creo que la pregunta a la que hace referencia Robin Green contiene la respuesta. Simplemente defina su consulta con todos los criterios posibles utilizando marcadores de posición (por ejemplo, {criterion1}
) y llame al método on on()
en la consulta, pasando los parámetros de la Seq
de la Option
real tal como se describe en la respuesta aceptada.
Ejemplo modificado del documento de Anorm, suponiendo que tiene dos criterios, pero solo desea que su consulta filtre en el código de país y no en la capital:
SQL(
"""
select * from Country c
join CountryLanguage l on l.CountryCode = c.Code
where ({countryCode} is null or c.code = {countryCode})
and ({capital} is null or c.capital = {capital});
"""
).on("countryCode" -> Some("FRA"), "capital" -> None)
Eso debería hacer el truco.