【问题标题】:anorm dynamic filters异常动态过滤器
【发布时间】:2012-11-11 01:58:54
【问题描述】:

我正在研究一点 anorm 文档(来自 play 框架),不清楚它是否支持常见的查询用例:动态过滤器,即用户在 10 个字段的搜索表单上填写 2 或 3 个搜索条件.

在这种情况下,如何在没有经典字符串操作的情况下动态构造查询?

【问题讨论】:

  • 在我看来,here 有一个答案,但我不明白 whereString 应该是什么,所以我不完全理解这个问题。
  • 同样,动态“order by”列表呢?
  • @Robin Green 所以 whereString 将类似于我在下面发布的内容:({countryCode} is null or c.code = {countryCode}) and ({capital} is null or c.capital = {capital})

标签: scala playframework playframework-2.0 anorm


【解决方案1】:

是的,我认为 Robin Green 提到的问题包含了答案。只需使用占位符(例如{criterion1})使用所有可能的条件定义您的查询,然后在查询上调用on() 方法,传递实际的SeqOption 参数,如接受的答案中所述。


来自 Anorm 文档的修改示例,假设您有两个条件,但只希望您的查询过滤国家代码而不是首都:

SQL(
"""
select * from Country c 
join CountryLanguage l on l.CountryCode = c.Code 
where ({countryCode} is null or c.code = {countryCode})
  and ({capital} is null or c.capital = {capital});
"""
).on("countryCode" -> Some("FRA"), "capital" -> None)

这应该可以解决问题。

【讨论】:

  • 这很好,但似乎对一些更“异国情调”的类型有限制,如 ZonedDateTime 或 LocalDateTime(来自 java.time),即使这些类型在 Anorm 上工作得很好。尝试将其与可选的 ZonedDateTime 一起使用,我得到“错误:无法确定参数 $1 的数据类型”。如果有人想试一试,请尝试将此方法应用于my use case
  • @Jonik 没试过,但如果你通过 Option.empty[ZonedDateTime] 而不是 None 可能会起作用?
  • 事情是,它总是在运行时失败(“无法确定参数的数据类型”)是否存在可选的 ZonedDateTime(无论是 Some(2016-01-09T17:19:25Z[UTC])NoneOption.empty[ZonedDateTime])。如果你想看看,我有一个complete test case at GitHub
  • @Jonik 这似乎是带有 Postgresql 驱动程序的issue。这真的很难看,但如果你使用{created}::timestamptz 而不是{created} 可能会起作用。
  • 谢谢,({created}::timestamptz is null or created >= {created}) 确实可以正常工作!
【解决方案2】:

先简短回答。假设您在数据库中有一个包含 3 列的表:nameemailpass。但是从用户那里,你只得到了namepassword 而不是email。所以选择所有 3 个选项

val name:Option[String] = Some("alice")
val email:Option[String] = None
val pass:Option[String] = Some("password")

//For db insertion have this:

  DB.withConnection { implicit conn =>
  SQL("INSERT INTO USERS (name,email,pass) VALUES ({n},{e},{p})").on(
 'n -> name, 'e -> email,'p -> pass).executeInsert()
 }

执行上述操作,因为emailNone,它将在您的数据库中插入null。因此,在您的情况下,对于所有 10 列,您可以在上面的 SQL 语句中定义它们,并在 on() 中传递 Option。如果其中任何一个是None,那么它将在数据库中将其作为null

虽然如果在架构中的列上存在NOT NULL 的约束,则可能会出现问题。在这种情况下,您可以将getOrElse 用于以下列:

DB.withConnection { implicit conn =>
  SQL("INSERT INTO USERS (name,email,pass) VALUES ({n},{e},{p})").on(
 'n -> name, 'e -> email.getOrElse("Default Email"),'p -> pass).executeInsert()

以下是关于 play 如何将类型转换为数据库类型的综合列表。它可以在对象anorm.ToStatement中找到:

        case Some(bd: java.math.BigDecimal) => stmt.setBigDecimal(index, bd)
        case Some(o) => stmt.setObject(index, o)
        case None => stmt.setObject(index, null)
        case bd: java.math.BigDecimal => stmt.setBigDecimal(index, bd)
        case date: java.util.Date => stmt.setTimestamp(index, new java.sql.Timestamp(date.getTime()))
        case o => stmt.setObject(index, o)

如您所见,None 将其视为 null。


如果是SELECT hmm,我不知道有什么异常特性可以在这里有所帮助,但我想简单的字符串操作可能就足够了:

def getColumns(xs:List[Option[_]]):String = {
    val notNone = xs.collect{
       case Some(x) => x.toString
    }
    notNone.mkString(",")
}

然后是SQL("SELECT %s from table".format(getColumns(List(nameColumn,emailColumn,passColumn)))

虽然这不是你想要的。 Anorm 只是一个 sql 构建库。为了做你想做的事,它还必须记住你的表模式(即至少列名..)。我不认为 anorm 是用来做所有这些的

【讨论】:

  • 这个问题是关于搜索(即 SELECT 查询)而不是插入。也许我遗漏了一些东西,但我看不出它对 SELECT 的作用。
  • @RobinGreen 嗯,那么我不知道可以做到这一点的异常特性。无论如何编辑它。
【解决方案3】:

Anorm 似乎在您插入普通的旧 SQL 的前提下明确运行,没有任何功能,例如类型安全或查询构建。它可能不是动态查询的正确工具。构建查询最终必须是字符串操作,因此我建议使用为您执行此操作的库。应该很容易将任何用于生成 SQL 语句的库与 Anorm 集成。

您可以使用jOOQ 之类的库动态构造一个查询并将该查询字符串传递给Anorm。作为奖励,您将获得对许多数据库的支持。 jOOQ 似乎很受欢迎,但您可能可以使用许多其他库来代替。或者如果 Anorm 不适合您的项目,则完全替换它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-28
    • 2013-04-05
    • 2014-05-28
    • 2013-07-08
    • 2013-09-04
    • 2011-05-15
    • 1970-01-01
    相关资源
    最近更新 更多