2012-03-02 57 views
23

似乎沒有簡單的方法在ANORM使用「在」條款:anorm中的「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() 

這太複雜了。

有沒有更簡單的方法?或者應該玩提供的東西,使其更容易?

回答

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

該解決方案與問題中提出的解決方案實際上是相同的,只有一個例外是它容易受SQL注入的影響,因爲您直接將參數粘貼到查詢字符串中(無需轉義!),而不是使用綁定參數。爲了節省14個字符,不值得! – 2012-08-08 15:50:07

+1

空洞的安全漏洞不值得14個字符?! – 2013-10-15 09:12:21

-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注入的影響。 – 2012-08-08 15:52:22

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特意將這些子句取出,因爲他們讓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 *) 

因此,使用字符串插值它更簡潔,更易於閱讀。

而對於使用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:_*) 
    } 
    } 

} 
1

它可能已經晚了,但我爲其他尋找相同的東西添加了這個。 你可以使用一些內置的數據庫功能來克服這一點。這是Anorm對ORM的優勢之一。例如,如果您使用的是PostgreSQL,則可以將您的列表作爲數組傳遞給查詢中的數組,並將其排列爲非常簡單:

我假設id是整數。

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 * from(where id in($ ids))。as。(parser。*)' – cchantep 2016-01-08 16:44:29