简体   繁体   English

在Anorm 2.4中使用具有可空列的Parser API

[英]Using the Parser API with nullable columns in Anorm 2.4

I'm really struggling to get rid of deprecation warnings now that I've upgraded to Anorm 2.4. 现在我已经升级到Anorm 2.4,我真的很难摆脱弃用警告。 I've had a look at How to handle null in Anorm but it didn't help me enough. 我已经看过如何在Anorm中处理null但它对我没有帮助。

Let's take a simple example: the account database table: 我们举一个简单的例子: account数据库表:

  • id (bigint not null) id (bigint not null)
  • email_address (varchar not null) email_address (varchar not null)
  • first_name (varchar) first_name (varchar)
  • last_name (varchar) last_name (varchar)

I could have 2 functions in my Scala code: getAccountOfId and getAccountsOfLastName . 我的Scala代码中可以有两个函数: getAccountOfIdgetAccountsOfLastName

  • getAccountOfId returns 0 or 1 account, therefore Option[(Long, String, Option[String], Option[String])] to keep our example simple getAccountOfId返回0或1个帐户,因此Option[(Long, String, Option[String], Option[String])]使我们的示例变得简单
  • getAccountsOfLastName returns a list of accounts (which could potentially have a size of 0), therefore List[(Long, String, Option[String], String)] to keep our example simple getAccountsOfLastName返回一个帐户列表(可能大小为0),因此List[(Long, String, Option[String], String)]使我们的示例变得简单

Part of the code of these 2 functions: 这两个函数的部分代码:

def getAccountOfId(id: Long): Option[(Long, String, Option[String], Option[String])] = {
  DB.withConnection { implicit c =>
    val query = """select email_address, first_name, last_name
        from account
        where id = {id};"""

    /* Rest of the code that I struggle with unless I use deprecated functions */
  }
}

def getAccountsOfLastName(lastName: String): List[(Long, String, Option[String], String)] = {
  DB.withConnection { implicit c =>
    val query = """select id, email_address, first_name
        from account
        where last_name = {lastName};"""

    /* Rest of the code that I struggle with unless I use deprecated functions */
  }
}

I want the "rest of the code" in these 2 functions to be based on Anorm's Parser API . 我希望这两个函数中的“其余代码”基于Anorm的Parser API

Not sure if this helps, but using Anorm 2.4, I have a case class which looks like this: 不确定这是否有帮助,但使用Anorm 2.4,我有一个如下所示的案例类:

 final case class Role(id: Int,
                      label: String,
                      roletype: Int,
                      lid: Option[Int],
                      aid: Option[Int],
                      created: DateTime,
                      modified: DateTime)

and then just have parser combinator for it which looks like this: 然后只有它的解析器组合器,它看起来像这样:

 val roleOptionRowParser = int("id") ~ str("label") ~ int("roletype") ~ (int("lid")?) ~ (int("vid")?) ~ get[DateTime]("created") ~
    get[DateTime]("modified") map {
    case id~label~roletype~lid~vid~created~modified ⇒ Some(Role(id, label, roletype, lid, vid, created, modified))
    case _ ⇒ None
}

so you basically just parse out using the ? 所以你基本上只是解析使用? combinator for optional fields, then match based on what you extract from the SQL result row. 用于可选字段的组合器,然后根据从SQL结果行中提取的内容进行匹配。 You can then apply this to queries in the following way: 然后,您可以通过以下方式将此应用于查询:

SQL(s"""
            | select * from $source
            |    where $clause
            """.stripMargin).on(params : _*).as(rowParser.single).get

where 'rowParser' in this case is just a reference to the roleOptionRowParser defined in the last lump of code. 其中'rowParser'在这种情况下只是对最后一批代码中定义的roleOptionRowParser的引用。

If you have multiple rows returned from your query (or expect to have multiple rows) then you can apply the same combinators (such as ? or *) before passing them through to the 'as' function like this: 如果从查询中返回多行(或者期望有多行),那么可以在将它们传递给'as'函数之前应用相同的组合器(例如?或*),如下所示:

SQL(s"""
            | select * from $source
            |    where $clause
            """.stripMargin).on(params : _*).as(rowParser *).flatten

or 要么

SQL(s"""
            | select * from $source
            |    where $clause
            """.stripMargin).on(params : _*).as(rowParser ?).flatten

Ah - forgot to mention that the 'flatten' on the end is there because my parser in this example returns an Option[Role], depending on whether all the necessary column values are present in the returned row (this bit): 啊 - 忘了提到最后的'flatten'是因为我的解析器在这个例子中返回一个Option [Role],这取决于返回的行中是否存在所有必要的列值(这一位):

case id~label~roletype~lid~vid~created~modified ⇒ Some(Role(id, label, roletype, lid, vid, created, modified))

So, when multiple rows are returned, I just apply 'flatten' to uplift out of the Option type so that I end up with a list of actual 'Role' instances. 因此,当返回多行时,我只是应用'flatten'来提升Option类型,这样我最终会得到一个实际的'Role'实例列表。

Cheers, 干杯,

HTH. HTH。

Turns out it was easy: 结果很简单:

  def getAccountOfId(id: Long): Option[(Long, String, Option[String], Option[String])] = {
    DB.withConnection { implicit c =>
      val query = """select email_address, first_name, last_name
        from account
        where id = {id};"""

      val rowParser = str("email_address") ~ (str("first_name") ?) ~ (str("last_name") ?) map {
        case emailAddress ~ firstNameOpt ~ lastNameOpt => (id, emailAddress, firstNameOpt, lastNameOpt)
      }

      SQL(query).on("id" -> id).as(rowParser.singleOpt)
    }
  }

  def getAccountsOfLastName(lastName: String): List[(Long, String, Option[String], String)] = {
    DB.withConnection { implicit c =>
      val query = """select id, email_address, first_name
        from account
        where last_name = {lastName};"""

      val rowParser = long("id") ~ str("email_address") ~ (str("first_name") ?) map {
        case id ~ emailAddress ~ firstNameOpt => (id, emailAddress, firstNameOpt, lastName)
      }

      SQL(query).on("lastName" -> lastName).as(rowParser.*)
    }
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM