scala slick query return value

Doug Anderson picture Doug Anderson · Oct 27, 2012 · Viewed 10.8k times · Source

I am rather new at Scala and have been struggling with slick and can't see how to return the results of a query to the calling method

I have a simple UserDto

case class UserDto(val firstName:String,
  val lastName:String,
  val userName:String,
  val isAdmin:Boolean) {}

a User table object

object User extends Table[(String, String, String, Boolean)]("USER") {

  def firstName = column[String]("FIRST_NAME")
  def lastName = column[String]("LAST_NAME")
  def userName = column[String]("USER_NAME")
  def admin = column[Boolean]("IS_ADMIN")

  def * = firstName ~ lastName ~ userName ~ admin

}

and a query class

class UserQuerySlickImpl(dataSource:DataSource) {

  def getResults(userName:String):Option[UserDto] = {
    var resultDto:Option[UserDto] = None

    Database.forDataSource(dataSource) withSession {
      val q = for {u <- User if u.userName is userName}
      yield (u.firstName, u.lastName, u.userName, u.admin)

      for (t <- q) {
        t match {
          case (f:String, l:String, u:String, a:Boolean) => 
            resultDto = Some(new UserDto(f, l, u, a))
        }
      }
    }
    resultDto
  }
}

I can query the database and get the user that matches the username, but the only way I could figure out how to return that user is by creating a var outside of the Database.forDataSource....{}.

Is there a better way that does not use the var but returns the resultDto directly.

also is there a way to construct the UserDto directly from the first for comprehension rather than needing the second for (t <- q) ...

I am using slick_2.10.0-M7, version 0.11.1.

Answer

Aaron Novstrup picture Aaron Novstrup · Oct 27, 2012

I haven't toyed with Slick yet but if it's reasonable (by which I mean consistent with Scala conventions) you should be able to do something like

def getResults(userName:String):Option[UserDto] =
  Database.forDataSource(dataSource) withSession {
    val q = for {u <- User if u.userName is userName}
      yield (u.firstName, u.lastName, u.userName, u.admin)

    q.firstOption map { case (f, l, u, a) => UserDto(f, l, u, a) }
  }

This is exactly what you would do if q was a List[(String, String, String, Boolean)].

Cleaning this up a bit, you can write

def getResults(userName:String):Option[UserDto] =
  Database.forDataSource(dataSource) withSession {
    (for (u <- User if u.userName is userName)
      yield UserDto(u.firstName, u.lastName, u.userName, u.admin)).firstOption
  }

Otherwise, you should be able to use

q foreach { 
   case (f, l, u, a) => return Some(UserDto(f, l, u, a))
}
return None

Generally, return statements like this one should be avoided, so hopefully q's type gives you something more functional to work with.