通过anorm生成sql查询,除了一个以外的所有空值

时间:2015-07-08 18:06:51

标签: sql-server scala playframework playframework-2.3 anorm

我使用play framework 2.3.8和scala开发web应用程序,在后端和前端都有复杂的架构。作为后端,我们使用MS SQL和许多存储过程,并通过anorm调用它。这里有一个问题。

我需要更新数据库中的某些字段。前端调用play框架,并返回字段名称和值。然后我解析,字段名称,然后我需要为更新字段生成SQL查询。我需要为所有参数赋值null,但recived参数除外。我试着这样做:

def updateCensusPaperXX(name: String, value: String, user: User) = {
  DB.withConnection { implicit c =>
    try {
        var sqlstring = "Execute [ScXX].[updateCensusPaperXX] {login}, {domain}"
        val params = List(
          "fieldName1",
          "fieldName2",
          ...,
          "fieldNameXX"
        )
        for (p <- params){
          sqlstring += ", "
          if (name.endsWith(p))
            sqlstring += value
          else
            sqlstring += "null"

        }
        SQL(sqlstring)
          .on(
            "login" -> user.login,
            "domain" -> user.domain,
          ).execute()
    } catch {
      case e: Throwable => Logger.error("update CensusPaper04 error", e)
    }
  }
}

但实际上并非在所有情况下都有效。例如,当我尝试保存字符串时,它会给我一个错误,如:

com.microsoft.sqlserver.jdbc.SQLServerException: Incorrect syntax near 'some phrase'

使用除了一个之外的所有空值的anorm生成sql查询的最佳方法是什么?

1 个答案:

答案 0 :(得分:3)

发生这种情况的原因是因为当您将字符串值直接写入SQL语句时,需要引用它。解决此问题的一种方法是确定哪些字段是字符串并添加条件逻辑以确定是否引用该值。这可能不是最好的方法。作为一般规则,您应该使用命名参数,而不是使用参数值构建字符串。这有一些好处:

  1. 您可能更容易诊断问题,因为您将在运行时收到更明智的错误消息。
  2. 它可以防止SQL注入的可能性。
  3. 您可以获得重用预准备语句的通常性能优势,尽管在存储过程调用的情况下这可能不会太多。
  4. 这意味着您应该像处理用户和域一样将字段列表视为命名参数。这可以通过对上面代码的一些细微更改来完成。首先,您可以按如下方式构建SQL语句:

        val params = List(
          "fieldName1",
          "fieldName2",
          ...,
          "fieldNameXX"
        )
    
        val sqlString = "Execute [ScXX].[updateCensusPaperXX] {login}, {domain}," +
          params.map("{" + _ + "}").mkString{","}
    

    上面发生的是您不需要直接插入值,因此您可以通过将参数列表添加到查询字符串的末尾来构建字符串。

    然后您可以继续开始构建参数列表。请注意,on SQL方法的参数是NamedParameter的vararg列表。基本上,我们需要创建Seq of NamedParameters,其中包含“login”,“domain”以及要填充的字段列表。以下内容应该有效:

        val userDomainParams: Seq[NamedParameter] = (("login",user.login),("domain",user.domain))
        val additionalParams = params.map(p => 
          if (name.endsWith(p)) 
            NamedParameter(p, value)
          else 
            NamedParameter(p, None)
        ).toSeq
        val fullParams = userDomainParams ++ additionalParams
        // At this point you can execute as follows
        SQL(sqlString).on(fullParams:_*).execute()
    

    这里发生的是你构建参数列表然后使用splat运算符:_*将序列扩展为所需的varargs作为on方法的参数。请注意,上面的NamedParameter中使用的None由Anorm转换为jdbc NULL

    这会处理与字符串相关的问题,因为您不再将字符串直接写入查询,并且它具有额外的好处,可以消除与编写SQL字符串相关的其他问题,而不是使用参数。