Подтвердить что ты не робот

В разделе "В" в анореме?

Кажется, нет простого способа использовать предложение "in" в анормах:

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

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

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

4b9b3361

Ответ 2

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

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

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

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

В разделе с Anorm

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

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

Ответ 3

Возможно, поздно, но я добавляю это для других, которые ищут то же самое. Чтобы преодолеть это, вы можете использовать некоторые встроенные функции базы данных. Это одно из преимуществ, которое 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)

Ответ 4

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

То, что я закончил делать, было его дезинфекцией, превратив его в список ints и обратно:

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(","))

Ответ 5

Возможно, это слишком поздно, но вот подсказка для использования пользовательской интерполяции строк, которая также работает для решения проблемы предложения 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 *)

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

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

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

}

Ответ 6

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

Ответ 7

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