How do I add an additional implicit extractor in Play 2.1.4 and actually use it?
Asked Answered
G

2

5

I am using Play 2.1.4 against a postgresql db. In the postgresql db, I am using uuid as my pk datatype, which correlates to java.util.UUID. The SqlParser.getT function in anorm doesn't have an implicit extractor for java.util.UUID. That makes sense, because I don't think many people use it; however, I can't seem to find instructions on how I can add one. Does anyone know how to add an additional implicit extractor to anorm.SqlParser in Play?

The error I am getting is below:

could not find implicit value for parameter extractor: anorm.Column[java.util.UUID]

I'm really new to Scala and Play, so if my approach is completely wrong, please let me know, but I'd really like to be able to do something like what you see below.

case class App(appId: UUID, appName: String, appServerName: String,
               appComponent: String, appDescription: String,
               appDateCreated: DateTime, appDateModified: DateTime,
               appValidated: Boolean)

val app = {
    get[UUID]("app_id") ~
    get[String]("app_name") ~
    get[String]("app_server_name") ~
    get[String]("app_component") ~
    get[String]("app_description") ~
    get[java.util.Date]("app_date_created") ~
    get[java.util.Date]("app_date_modified") ~
    get[Boolean]("app_validated") map {
      case id ~ name ~ serverName ~ component ~ description ~ dateCreated ~
        dateModified ~ validated  => App(id, name, serverName, component,
      description, new DateTime(dateCreated.getTime),
        new DateTime(dateModified.getTime), validated)
    }
  }

def all(): List[App] = DB.withConnection { implicit conn =>
  SQL("SELECT * FROM apps").as(app *)
}
Goldplate answered 16/9, 2013 at 22:20 Comment(1)
This answer should help you get started although it is about JodaTime conversion.Concessive
K
5

Maybe this post will be helpfull. (Used in my project. Working fine)

 /**
   * Attempt to convert a SQL value into a UUID
   *
   * @param value value to convert
   * @return UUID
   */
  private def valueToUUIDOption(value: Any): Option[UUID] = {
    try {
      valueToByteArrayOption(value) match {
        case Some(bytes) => Some(UUIDHelper.fromByteArray(bytes))
        case _ => None
      }
    }
    catch {
      case e: Exception => None
    }
  }
/**
   * Implicit conversion from UUID to anorm statement value
   */
  implicit def uuidToStatement = new ToStatement[UUID] {
    def set(s: java.sql.PreparedStatement, index: Int, aValue: UUID): Unit = s.setObject(index, aValue)
  }
 /**
   * Implicit converstion from anorm row to uuid
   */
  implicit def rowToUUID: Column[UUID] = {
    Column.nonNull[UUID] { (value, meta) =>
      val u = UUID.fromString(value.toString)
      val MetaDataItem(qualified, nullable, clazz) = meta
      valueToUUIDOption(value) match {
        case Some(uuid) => Right(uuid)
        case _ => Left(TypeDoesNotMatch("Cannot convert " + value + ":" + value.asInstanceOf[AnyRef].getClass + " to UUID for column " + qualified))
      }
    }
  }
Keare answered 17/9, 2013 at 10:52 Comment(2)
You're a gentleman and a scholar. This worked great. I had to modify it slightly because I am using it with postgresql which returns a UUID as a data type instead of a byte array; however, seeing your code gave me a HUGE leg up on figuring out how to do what I wanted to do, and now I can use either DB, which is fantastic. Thanks so much.Goldplate
@jlegler - SO is place for helping others ;)Keare
G
7

Here is a simplified variation of the @r.piesnikowski answer for a JDBC driver that returns java.util.UUID, like PostgreSQL does:

  /**
   * Implicit conversion from UUID to Anorm statement value
   */
  implicit def uuidToStatement = new ToStatement[UUID] {
    def set(s: java.sql.PreparedStatement, index: Int, aValue: UUID): Unit = s.setObject(index, aValue)
  }

  /**
   * Implicit conversion from Anorm row to UUID
   */
  implicit def rowToUUID: Column[UUID] = {
    Column.nonNull[UUID] { (value, meta) =>
      value match {
        case v: UUID => Right(v)
        case _ => Left(TypeDoesNotMatch(s"Cannot convert $value:${value.asInstanceOf[AnyRef].getClass} to UUID for column ${meta.column}"))
      }
    }
  }
Goodly answered 31/10, 2013 at 20:43 Comment(1)
Your version works well for me unlike @r.piesnikowski's. Is there any difference between them?Windle
K
5

Maybe this post will be helpfull. (Used in my project. Working fine)

 /**
   * Attempt to convert a SQL value into a UUID
   *
   * @param value value to convert
   * @return UUID
   */
  private def valueToUUIDOption(value: Any): Option[UUID] = {
    try {
      valueToByteArrayOption(value) match {
        case Some(bytes) => Some(UUIDHelper.fromByteArray(bytes))
        case _ => None
      }
    }
    catch {
      case e: Exception => None
    }
  }
/**
   * Implicit conversion from UUID to anorm statement value
   */
  implicit def uuidToStatement = new ToStatement[UUID] {
    def set(s: java.sql.PreparedStatement, index: Int, aValue: UUID): Unit = s.setObject(index, aValue)
  }
 /**
   * Implicit converstion from anorm row to uuid
   */
  implicit def rowToUUID: Column[UUID] = {
    Column.nonNull[UUID] { (value, meta) =>
      val u = UUID.fromString(value.toString)
      val MetaDataItem(qualified, nullable, clazz) = meta
      valueToUUIDOption(value) match {
        case Some(uuid) => Right(uuid)
        case _ => Left(TypeDoesNotMatch("Cannot convert " + value + ":" + value.asInstanceOf[AnyRef].getClass + " to UUID for column " + qualified))
      }
    }
  }
Keare answered 17/9, 2013 at 10:52 Comment(2)
You're a gentleman and a scholar. This worked great. I had to modify it slightly because I am using it with postgresql which returns a UUID as a data type instead of a byte array; however, seeing your code gave me a HUGE leg up on figuring out how to do what I wanted to do, and now I can use either DB, which is fantastic. Thanks so much.Goldplate
@jlegler - SO is place for helping others ;)Keare

© 2022 - 2024 — McMap. All rights reserved.