【问题标题】:Play Scala Anorm dynamic SQL for UPDATE query为 UPDATE 查询播放 Scala Anorm 动态 SQL
【发布时间】:2015-04-20 14:12:49
【问题描述】:

我的 Google-fu 让我失望了,所以我希望你能帮忙

我正在构建一些 web 服务是使用 scala 和 anorm 进行数据库访问的播放框架

我的一个电话是更新数据库中的现有行 - 即运行类似的查询

UPDATE [Clerks]
   SET [firstName] = {firstName}
  ,[lastName] = {lastName}
  ,[login] = {login}
  ,[password] = {password}
 WHERE [id] = {id}

我的方法接收一个职员对象,但所有参数都是可选的(当然除了 id),因为他们可能只希望像这样更新行的单个列

UPDATE [Clerks]
   SET [firstName] = {firstName}
 WHERE [id] = {id}

所以我希望该方法检查定义了哪些职员参数并相应地构建更新语句的“SET”部分

似乎应该有更好的方法,而不是遍历 clerk 对象的每个参数,检查它是否已定义并构建查询字符串 - 但到目前为止我一直无法找到有关该主题的任何内容。

有没有人对如何最好地处理有任何建议

【问题讨论】:

  • 我不确定是否有,因为对于批量更新/插入,人们往往还会构建查询字符串..
  • 您可以根据可用值动态准备SQL语句的字符串,将该语句传递给SQL(...),然后传递参数。看文档。

标签: scala playframework anorm


【解决方案1】:

正如评论者所说,这似乎是不可能的 - 您必须自己构建查询字符串。

我发现围绕这个缺乏的例子,解决这个问题比它应该花费的时间更多(我是 scala 和 play 框架的新手,所以这是一个共同的主题)

最后这是我实现的:

override def updateClerk(clerk: Clerk) = {
  var setString: String = "[modified] = {modified}"
  var params: collection.mutable.Seq[NamedParameter] = 
     collection.mutable.Seq(
            NamedParameter("modified", toParameterValue(System.currentTimeMillis / 1000)), 
            NamedParameter("id", toParameterValue(clerk.id.get)))

  if (clerk.firstName.isDefined) {
    setString += ", [firstName] = {firstName}"
    params = params :+ NamedParameter("firstName", toParameterValue(clerk.firstName.getOrElse("")))
  }
  if (clerk.lastName.isDefined) {
    setString += ", [lastName] = {lastName}"
    params = params :+ NamedParameter("lastName", toParameterValue(clerk.lastName.getOrElse("")))
  }
  if (clerk.login.isDefined) {
    setString += ", [login] = {login}"
    params = params :+ NamedParameter("login", toParameterValue(clerk.login.getOrElse("")))
  }
  if (clerk.password.isDefined) {
    setString += ", [password] = {password}"
    params = params :+ NamedParameter("password", toParameterValue(clerk.password.getOrElse("")))
  }
  if (clerk.supervisor.isDefined) {
    setString += ", [isSupervisor] = {supervisor}"
    params = params :+ NamedParameter("supervisor", toParameterValue(clerk.supervisor.getOrElse(false)))
  }

  val result = DB.withConnection { implicit c =>
    SQL("UPDATE [Clerks] SET " + setString + " WHERE [id] = {id}").on(params:_*).executeUpdate()
  }
}

这可能不是最好的方法,但是我发现它非常易读,并且在准备好的语句中正确处理了参数。

希望这可以使遇到类似问题的人受益

如果有人想提供改进,我们将不胜感激

【讨论】:

  • 你有没有发现更好的东西?你有机会分享toParameterValue的定义吗?
  • 嗨@Brad,从来没有找到更好的东西 - 将更新代码以包含 toParameterValue
  • 嘿@BradUrani - 实际上我不需要添加代码 - toParameterValue 是 anorm 包的一部分,所以你只需要'import anorm.ParameterValue._' :-)
【解决方案2】:

从大约 2.6.0 开始,这可以直接使用 anorm 使用它们的宏,http://playframework.github.io/anorm/#generated-parameter-conversions

这是我的例子:

case class UpdateLeagueFormInput(transferLimit: Option[Int], transferWildcard: Option[Boolean], transferOpen: Option[Boolean])
val input = UpdateLeagueFormInput(None, None, Some(true))

val toParams: ToParameterList[UpdateLeagueFormInput] = Macro.toParameters[UpdateLeagueFormInput]
val params = toParams(input)
val dynamicUpdates = params.map(p => {
  val snakeName = camelToSnake(p.name)
  s"$snakeName = CASE WHEN {${p.name}} IS NULL THEN l.$snakeName ELSE {${p.name}} END"
})
val generatedStmt = s"""UPDATE league l set ${dynamicUpdates.mkString(", ")} where league_id = ${league.leagueId}"""
SQL(generatedStmt).on(params: _*).executeUpdate()

制作:

UPDATE league l set transfer_limit = CASE WHEN null IS NULL THEN l.transfer_limit ELSE null END, transfer_wildcard = CASE WHEN null IS NULL THEN l.transfer_wildcard ELSE null END, transfer_open = CASE WHEN true IS NULL THEN l.transfer_open ELSE true END where league_id = 26;

注意事项:

  • camelToSnake 函数只是我自己的(有一个明显的 ColumnNaming.SnakeCase 可用于解析器行,但我找不到类似的参数解析)

  • 我的示例字符串插入 {league.leagueId},它也可以将其视为参数

  • 最好避免空字段的冗余集,但我认为这是不可能的,我认为clean code/messy auto-generated sql > messy code/clean auto-generated sql

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多