6
votes

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.

2
Just to be clear, you're blocking your thread with Await.result. You're probably aware but it's just scary seeing it in code.goralph
I'm aware – and all of the Slick 3.0 examples from Typesafe do this...Zac

2 Answers

4
votes

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.

1
votes

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 }