scala - Anorm:WHERE条件,有条件的

标签 scala playframework playframework-2.4 anorm

考虑像这样的存储库/DAO 方法,效果很好:

def countReports(customerId: Long, createdSince: ZonedDateTime) =
  DB.withConnection {
    implicit c =>
      SQL"""SELECT COUNT(*)
            FROM report
            WHERE customer_id = $customerId
            AND created >= $createdSince
         """.as(scalar[Int].single)
  }

但是如果方法是用可选参数定义的呢:

def countReports(customerId: Option[Long], createdSince: Option[ZonedDateTime])

要点是,如果存在任一可选参数,则使用它来过滤结果(如上所示),否则(如果它是 None),只需省略相应的 WHERE 条件。

使用可选的 WHERE 条件编写此方法的最简单方法是什么?作为 Anorm 新手,我一直在努力寻找这样的示例,但我想肯定有一些明智的做法(即,不为存在/缺失参数的每个组合复制 SQL)。

请注意,当在 Anorm SQL 调用中使用时,java.time.ZonedDateTime 实例会完美且自动地映射到 Postgres timestamptz。 (尝试将 WHERE 条件提取为字符串,在 SQL 之外,使用普通字符串插值创建的字符串无效;toString 生成数据库无法理解的表示。)

Play 2.4.4

最佳答案

一种方法是设置过滤子句,例如

val customerClause =
  if (customerId.isEmpty) ""
  else " and customer_id={customerId}"

然后将这些代入你的SQL:

SQL(s"""
  select count(*)
    from report
    where true
      $customerClause
      $createdClause
""")
.on('customerId -> customerId, 
  'createdSince -> createdSince)
.as(scalar[Int].singleOpt).getOrElse(0)

我认为使用 {variable} 而不是 $variable 更可取,因为它可以降低 SQL 注入(inject)攻击的风险,在这种情况下,有人可能会使用恶意字符串调用您的方法。 Anorm 不介意您是否有未在 SQL 中引用的其他符号(即如果子句字符串为空)。最后,根据数据库(?),计数可能不返回任何行,因此我使用 singleOpt 而不是 single。

我很好奇您还收到了什么其他答案。

编辑:异常插值(即 SQL"...",一种超越 Scala 的 s"..."、f"..."和 raw"..."的插值实现)was introduced允许使用 $variable 等同于 {variable} with .on。从 Play 2.4 开始,Scala 和 Anorm 插值可以混合使用 $ 用于 Anorm(SQL 参数/变量)和 #$ 用于 Scala(纯字符串)。事实上,只要 Scala 内插字符串不包含对 SQL 参数的引用,它就可以正常工作。在 2.4.4 中,我发现在使用 Anorm 插值时在 Scala 插值字符串中使用变量的唯一方法是:

val limitClause = if (nameFilter="") "" else s"where name>'$nameFilter'"
SQL"select * from tab #$limitClause order by name"

但这很容易受到 SQL 注入(inject)的攻击(例如像 it's 这样的字符串会导致运行时语法异常)。因此,对于内插字符串中的变量,似乎有必要使用“传统”.on 方法,仅使用 Scala 插值:

val limitClause = if (nameFilter="") "" else "where name>{nameFilter}"
SQL(s"select * from tab $limitClause order by name").on('limitClause -> limitClause)

也许将来可以扩展 Anorm 插值以解析变量的插值字符串?

Edit2:我发现有些表中可能包含或可能不包含在查询中的属性数量不时发生变化。对于这些情况,我定义了一个上下文类,例如客户上下文。在这个案例类中,有 lazy val 用于影响 sql 的不同子句。 sql 方法的调用者必须提供 CustomerContext,然后 sql 将包含 ${context.createdClause} 等内容。这有助于提供一致性,因为我最终在其他地方使用上下文(例如分页的总记录数等)。

关于scala - Anorm:WHERE条件,有条件的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34078955/

相关文章:

java - Scala RegEx 字符串提取器的行为不一致

scala - Play WS - 检查压缩头

css - 将静态文件内容包含到 Play 2.4 模板中

java - 如何添加用户库作为播放框架的依赖项?

scala - 如何重试失败的 akka-http 请求流解码?

scala - 如何在scala中构建无限不可变树

scala - 使用 sbt 构建凿子时,如何关闭进度条等以便输出干净?

java - Rythm Template Engine 相对于 Japid for Play Framework 1.x 的优势?

scala - 如何模拟 Scala 单例对象?

scala - Play 框架解析查询参数中的分号