Alex
Alex

Reputation: 469

Slick 3.0 transactions in Play 2.4

I have a controller that makes various calls to database model classes that perform various operations on a PostgreSQL database. I want to perform all of these operations inside a single transaction. In Play 2.3 I was able to wrap code in DB.withTransaction { ... } to get it to perform all actions inside a single transaction.

With Play 2.4 and Slick 3.0 it appears that Slick handles connection management, and DB.withTransaction no longer seems the correct way to handle this (it throws IllegalArgumentException with the message "Could not find database for default" anyway). What's the proper way to group Slick operations inside a transaction from within the controller?

Upvotes: 1

Views: 851

Answers (1)

Julien Lafont
Julien Lafont

Reputation: 7877

The solution of your problem is DBIO composition.

Firstly, all your atomic operations should be implemented by a DBIOAction (A DBIOAction represents an operation which will be executed on the DB).

Next, you can compose all your DBIOAction together, with andThen, flatMap, or using a for comprehension (see DBIOAction api)

When you have your composed DBIO, you can call .transactionally to make the DBIOAction transactional when it will be executed on a db.

Sample:

def updateUsername(id: Long, username: String) = Schemas.users.filter(_.id === id).map(_.username).update(username)
def updateUserAddress(id: Long, address: String) = Schemas.addresses.filter(_.id === id).map(_.address).update(address)
def getUser(id: Long) = Schemas.users.filter(_.id === id).head

val operations = for {
   _ <- updateUserName(1, "foo")
   _ <- updateUserAddress(1, "bar")
   user <- getUser(1)
} yield user

db.run(operations.transactionally)

The way to obtain the db object is explained in the Play documentation.

Upvotes: 2

Related Questions