Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The "right" way to use write Slick 3.0 Scala queries in Play Framework

I'm using Slick 3.0 and (of course) almost all the examples out there cover Slick 2.x. Things have changed and frankly seem more complicated, not less.

Here's an example: I want to get an object (a GPPerson) by id. This is what I have right now, and it seems very verbose... more so than Slick 2.x:

def get(id: GPID): Option[GPPerson] = Await.result(
    db.run(
        people.filter(_.id === id).result), Duration.Inf
    ).headOption

In Slick 2.x things were easier because of the implicits, among other things. But the above seems to be the most concise expression I've come up with.

It also doesn't really address exception handling, which I would need to add.

like image 289
Zaphod Avatar asked May 20 '15 03:05

Zaphod


2 Answers

I started to use Slick 3.0 in a new project a few months ago and I had the same questions. This is what I understood:

Slick 3.0 was designed for non-blocking asynchronous (reactive) applications. Obviously it means Akka + Play / Spray nowadays. In this world you mostly interact with Futures, that's why Slick's db.run returns Future. There is no point in using Await.result - if you need blocking calls it's better to return to 2.x.

But if you use reactive stack you'll get benefits immediately. For example, Spray is completely non-blocking library that works with Futures nicely using onComplete directive. You can call a method that returns Future with a result from Slick in a Spray route and then use that result together with onComplete. In this case the whole response-reply pipeline is non-blocking.

You also mentioned exception handling, so this is exactly how you do it - using Futures.

So based on my experience I would write your method in a following way:

def get(id: GPID): Future[Option[GPPerson]] = db.run(
  people.filter(_.id === id).result.map(_.headOption)
)

and then work with a Future.

like image 88
sap1ens Avatar answered Nov 05 '22 09:11

sap1ens


you can do this.

def syncResult[R](action:slick.dbio.DBIOAction[R, slick.dbio.NoStream, scala.Nothing]):R = {
    import scala.concurrent.duration.Duration

    val db = Database.forConfig("db")
    try {
      Await.result(db.run(action), Duration.Inf)
    } finally db.close
  }

def get(id: GPID): Option[GPPerson] = syncResult { people.filter(_.id === id).result.headOption }
like image 34
Notice Avatar answered Nov 05 '22 11:11

Notice