简体   繁体   中英

Using Anorm RowParser

I have been using Play framework 2.0 for about 6 months, I have been wondering why they use so many boilerplate code to parse from my SQL query returns, like below:

case class Journal_accountDetail(amount: Double, states: Boolean)

val Journal_AccountParser: RowParser[Journal_accountDetail] = {
    get[Double] ("amount") ~
    get[Boolean] ("states") map({
        case amount~states => Journal_accountDetail(amount,states)
    })
}

Is it something that boost Play framework performance ??

The parsing API can seem a bit tedious at first, but it's quite powerful when you start combining and re-using the parsers, and much less ugly than pattern matching in every function that returns an SQL result.

Imagine something like this:

case class User(id: Int, name: String, address: Address)
case class Address(id: Int, street: String, city: String, state: State, country: Country)
case class State(id: Int, abbrev: String, name: String)
case class Country(id: Int, code: String, name: String)

To construct the User you need to parse a result with multiple JOIN s. Rather than having one large parser, we construct one for each class in it's companion object:

object User {
    val parser: RowParser[User] = {
        get[Int]("users.id") ~
        get[String]("users.name") ~ 
        Address.parser map {
            case id~name~address => User(id, name, address)
        }
    }
}

object Address {
    val parser: RowParser[Address] = {
        get[Int]("address.id") ~
        get[String]("address.street) ~
        get[String]("address.city") ~ 
        State.parser ~
        Country.parser map {
            case id~street~city~state~country => Address(id, street, city, state, country)
        }
    }
}

object State {
    val parser: RowParser[State] = {
        get[Int]("states.id") ~
        get[String]("states.abbrev") ~ 
        get[String]("states.name") map {
            case id~abbrev~name => State(id, abbrev, name)
        }
    }
}

object Country {
    val parser: RowParser[Country] = {
        get[Int]("countries.id") ~
        get[String]("countries.code") ~ 
        get[String]("countries.name") map {
            case id~code~name => Country(id, code, name)
        }
    }
}

Note how I'm using the full table space in the parsers, in order to avoid column name collisions.

Altogether, this looks like a lot of code, but for each source file it's only a small footprint. And the largest benefit is that our User parser is quite clean despite it's complex structure. Let's say in User the address is actually Option[Address] . Then accounting for that change is as simple as changing Address.parser in the User parser to (Address.parser ?) .

For parsing simple queries, yes it does seem like a lot of boilerplate. But I'm quite thankful for the parsing API when it comes to parsing examples like the one above (and much more complex ones).

anorm.SqlParser also provides convinent parser functions, like .str , .int , .float , .double , ... instead of .get[String] , .get[Int] , .get[Float] , .get[Double] . Best regards.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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