2012-03-02 6 views
23

Это кажется не легкий способ использовать «в» пункта в anorm:В разделе «В» в анормальном режиме?

val ids = List("111", "222", "333") 
val users = SQL("select * from users where id in ({ids})").on('ids-> ???).as(parser *) 

Как заменить ??? часть?

Я пробовал:

on('ids -> ids) 
on('ids -> ids.mkString("'","','","'")) 
on('ids -> ids.mkString("','") 

Но никто не работает.

Я вижу в обсуждении с точно такой же проблемой: https://groups.google.com/d/topic/play-framework/qls6dhhdayc/discussion, автор имеет сложное решение:

val params = List(1, 2, 3) 

val paramsList = for (i <- 0 until params.size) yield ("userId" + i) 

// ---> results in List("userId0", "userId1", "userId2") 

User.find("id in ({%s})" 

    // produces "id in ({userId0},{userId1},{userId2})" 
    .format(paramsList.mkString("},{")) 

    // produces Map("userId0" -> 1, "userId1" -> 2, ...) 
    .on(paramsList.zip(params)) 
    .list() 

Это слишком сложное.

Есть ли более простой способ? Или нужно играть, чтобы сделать что-то проще?

ответ

-1
User.find("id in (%s)" 
    .format(params.map("'%s'".format(_)).mkString(",")) 
    .list() 
+3

Это решение фактически совпадает с предложенным в вопросе, за тем исключением, что оно уязвимо для SQL-инъекции, потому что вы вставляете параметры непосредственно в строку запроса (без экранирования!), Вместо того, чтобы использовать bind параметры. Не стоит этого ради спасения 14 персонажей! –

+1

Разрывная дыра безопасности не стоит 14 символов ?! –

-1
val ids = List("111", "222", "333") 
val users = SQL("select * from users 
       where id in 
       (" + ids.reduceLeft((acc, s) => acc + "," + s) + ")").as(parser *) 
+3

Было бы более идиоматично писать 'reduceLeft' как просто' ids.mkString (",") '. И согласно другому ответу, это уязвимо для SQL Injection из-за помещения параметров непосредственно в строку запроса. –

0

У меня была та же проблема в последнее время. К сожалению, похоже, что нет возможности использовать интерполяцию строк и, следовательно, уязвим для SQL-инъекции.

То, что я в конечном итоге делает был своего рода дезинфицирующим его, превращая его в список Интс и обратно:

val input = "1,2,3,4,5" 

// here there will be an exception if someone is trying to sql-inject you 
val list = (_ids.split(",") map Integer.parseInt).toList 

// re-create the "in" string 
SQL("select * from foo where foo.id in (%s)" format list.mkString(",")) 
8

прибил его! В этом потоке не было никаких обновлений, но, похоже, все еще актуально. Из-за этого, и потому что нет ответа, я подумал, что заброшу его на рассмотрение.

Anorm не поддерживает предложения 'IN'. Я сомневаюсь, что они когда-нибудь будут. Нет ничего, что вы могли бы сделать, чтобы заставить их работать, я даже прочитал сообщение, в котором анорм специально отобрал эти предложения, потому что они заставили Anorm почувствовать себя «как ORM».

Однако довольно просто обернуть SqlQuery в короткий класс, который поддерживает предложение IN, а затем преобразовать этот класс в SqlQuery, когда это необходимо.

Вместо вставки кода здесь, потому что он немного длинный, вот ссылка на мой блог, где я разместил код и как его использовать.

In clause with Anorm

В принципе, если у вас есть код из моего блога, ваши заявления выглядеть следующим образом:

RichSQL(""" SELECT * FROM users WHERE id IN ({userIds}) """).onList("userIds" -> userIds).toSQL.as(userParser *)(connection) 
0

Может быть, это слишком поздно, но вот подсказка для использования пользовательской строки интерполяции, которая также работает для решения проблемы предложения IN.

Я применил вспомогательный класс для определения интерполяции строк. Вы можете видеть это ниже, и вы можете просто скопировать и вставить, но сначала давайте посмотрим, как вы можете его использовать.

Вместо написать что-то

SQL("select * from car where brand = {brand} and color = {color} and year = {year} order by name").on("brand" -> brand, "color" -> color, "year" -> year).as(Car.simple *) 

Вы можете просто написать:

SQL"select * from car where brand = $brand and color = $color and year = $year order by name".as(Car.simple *) 

Таким образом, используя интерполяцию строки это более кратким и легким для чтения.

И в случае с помощью предложения, вы можете написать:

val carIds = List(1, 3, 5) 
SQLin"select * from car where id in ($carIds)".as(Car.simple *) 

Или для примера:

val ids = List("111", "222", "333") 
val users = SQLin"select * from users where id in ($ids)".as(parser *) 

Для получения дополнительной информации об интерполяции строк, проверить это link

Код для этого неявного класса:

package utils 

object AnormHelpers { 

    def wild (str: String) = "%" + str + "%" 

    implicit class AnormHelper (val sc: StringContext) extends AnyVal { 

    // SQL raw -> it simply create an anorm.Sql using string interpolation 
    def SQLr (args: Any*) = { 
     // Matches every argument to an arbitrary name -> ("p0", value0), ("p1", value1), ... 
     val params = args.zipWithIndex.map(p => ("p"+p._2, p._1)) 
     // Regenerates the original query substituting each argument by its name with the brackets -> "select * from user where id = {p0}" 
     val query = (sc.parts zip params).map{ case (s, p) => s + "{"+p._1+"}" }.mkString("") + sc.parts.last 
     // Creates the anorm.Sql 
     anorm.SQL(query).on(params.map(p => (p._1, anorm.toParameterValue(p._2))) :_*) 
    } 

    // SQL -> similar to SQLr but trimming any string value 
    def SQL (args: Any*) = { 
     val params = args.zipWithIndex.map { 
     case (arg: String, index) => ("p"+index, arg.trim.replaceAll("\\s{2,}", " ")) 
     case (arg, index) => ("p"+index, arg) 
     } 
     val query = (sc.parts zip params).map { case (s, p) => s + "{"+ p._1 + "}" }.mkString("") + sc.parts.last 
     anorm.SQL(query).on(params.map(p => (p._1, anorm.toParameterValue(p._2))) :_*) 
    } 

    // SQL in clause -> similar to SQL but expanding Seq[Any] values separated by commas 
    def SQLin (args: Any*) = { 
     // Matches every argument to an arbitrary name -> ("p0", value0), ("p1", value1), ... 
     val params = args.zipWithIndex.map { 
     case (arg: String, index) => ("p"+index, arg.trim.replaceAll("\\s{2,}", " ")) 
     case (arg, index) => ("p"+index, arg) 
     } 
     // Expands the Seq[Any] values with their names -> ("p0", v0), ("p1_0", v1_item0), ("p1_1", v1_item1), ... 
     val onParams = params.flatMap { 
     case (name, values: Seq[Any]) => values.zipWithIndex.map(v => (name+"_"+v._2, anorm.toParameterValue(v._1))) 
     case (name, value) => List((name, anorm.toParameterValue(value))) 
     } 
     // Regenerates the original query substituting each argument by its name expanding Seq[Any] values separated by commas 
     val query = (sc.parts zip params).map { 
     case (s, (name, values: Seq[Any])) => s + values.indices.map(name+"_"+_).mkString("{", "},{", "}") 
     case (s, (name, value)) => s + "{"+name+"}" 
     }.mkString("") + sc.parts.last 
     // Creates the anorm.Sql 
     anorm.SQL(query).on(onParams:_*) 
    } 
    } 

} 
1

Возможно, это было поздно, но я добавляю это для других, которые ищут то же самое. Чтобы преодолеть это, вы можете использовать некоторые встроенные функции базы данных. Это одно из преимуществ, которое Anorm имеет над ORM. Например, если вы используете PostgreSQL, вы можете передать свой список в виде массива и отключить массив в своем запросе:

Я предполагаю, что идентификаторы являются целыми.

val ids = List(1, 2, 3) 

val idsPgArray = "{%s}".format(ids.mkString(",")) //Outputs {1, 2, 3} 

val users = SQL(
    """select * from users where id in (select unnest({idsPgArray}::integer[]))""" 
).on('ids-> ???).as(parser *) 

Выполненный запрос будет

select * from users where id in (select unnest('{1, 2, 3}'::integer[])) 

которая равна

select * from users where id in (1, 2, 3) 
12

Anorm теперь поддерживает такой случай (и более), так как 2,3: "Using multi-value parameter"

Возврат к исходному Например, он дает:

val ids = Seq("111", "222", "333") 
val users = SQL("select * from users where id in ({ids})").on('ids-> ids).as(parser *) 
+0

Можно также использовать 'SQL 'select * от пользователей, где id in ($ ids)". As (parser. *) ' – cchantep

Смежные вопросы