Play! Framework 2 (2.1) Scala with Slick made easy (With example)

posted in: Play! Framework, Scala | 19

Hi everyone!

Today I’ve seen some videos about Slick and Play! Framework 2.1 and I got excited, so I thought, let’s code an app with Slick. What did I realize? It wasn’t that simple. Play! isn’t yet thought to work with Slick very well.

First of all, if you want to see this example complete you can go to

https://github.com/mgonto/slick-play2-example

In Slick, you create table definitions for the class you want to use. However, all of the imports for the Table, the columns, etc. are driver dependant. So, when you change the driver, you don’t want to change the import in the code. You just want to change a configuration in the application.conf from Play! Framework. That’s exactly what I did.

So, let’s see first how it’s implemented. Every Driver (H2, Postgres, etc) extends from a trait called ExtendedProfile. So, I needed my table classes to use some profile that was injected by me. Once I realized that I needed to make some DI I thought about Cake Pattern. For more information about the Cake Pattern please viisit Cake pattern

So, the first thing I implemented was a Trait Profile with an abstract Extended Profile value:

package models

import slick.driver.ExtendedProfile

trait Profile {
  val profile: ExtendedProfile
}

After this, I needed to create the Users table. That Users table needed the profile, so I’d createa UserModule which would need the Profile trait. Then, as I’d mix with a Profile trait, I’d import the things from the abstract ExtendedProfile field and everything would compile. Then I create the users table with an ID and a Name (pretty simple)

package models

case class User(id: Option[Int], name : String)

trait UserComponent {
  this: Profile =>

  import profile.simple._

  object Users extends Table[User]("users") {
    def id = column[Int]("id", O.PrimaryKey)
    def name =  column[String]("name", O.NotNull)
    def * = id.? ~ name <> (User, User.unapply _)

    def add(user: User)(implicit session: Session) = {
      this.insert(user)
    }

    def countByName(name: String)(implicit session: Session) = {
      (for {
        user <- Users
        if (user.name === name)
      } yield(user)).list.size
    }

  }
}

After this, I create a DataAccessLayer class (DAL) which would receive the ExtendedProfile as a constructor parameter and then would include the other traits (Profile and UserModule)

package models

import slick.driver.ExtendedProfile

class DAL(override val profile: ExtendedProfile) extends UserComponent with Profile {

  import profile.simple._

  def create(implicit session: Session): Unit = {
    Users.ddl.create //helper method to create all tables
  }
}

After this, I have everything to create a DAL from a given profile. However, now I needed a way to configure the profile, so I added a new property in the application.conf.

slick.db.driver=scala.slick.driver.H2Driver

Then, I created a trait that would import this driver from the conf and create the database to do the queries and the dal with the corresponding profile. It has two methods, getDb and getDal which will be then used by two other classes.

package models

import slick.session.Database
import play.api.db.DB
import play.api.Application
import slick.driver.ExtendedProfile

trait DBeable {

  val SLICK_DRIVER = "slick.db.driver"
  val DEFAULT_SLICK_DRIVER = "scala.slick.driver.H2Driver"

  def getDal(implicit app : Application) : DAL = {
    val driverClass = app.configuration.getString(SLICK_DRIVER).getOrElse(DEFAULT_SLICK_DRIVER)
    val driver = singleton[ExtendedProfile](driverClass)
    new DAL(driver)
  }

  def getDb(implicit app : Application) = {
    Database.forDataSource(DB.getDataSource())
  }

  private def singleton[T](name : String)(implicit man: Manifest[T]) : T =
    Class.forName(name + "$").getField("MODULE$").get(man.runtimeClass).asInstanceOf[T]

}

The only things left are creating the DB once the app starts and having a singleton object to be able to ask for the dal and the database to do the queries from a controller. This is done as following:

import models.{User, DBeable, AppDB}
import play.api.db.DB
import play.api.GlobalSettings
import play.api.Application
import slick.session.Session

object Global extends GlobalSettings with DBeable{

  override def onStart(app: Application) {
    implicit val application = app
    lazy val database = getDb
    lazy val dal = getDal
    database.withSession {
      implicit session: Session =>
        dal.create
    }
  }
}
package models
import play.api.Play.current
object AppDB extends DBeable {

  lazy val database = getDb
  lazy val dal = getDal

}

And that’s it :). Now you have everything configured to start coding. I’ve tried if everything was working with a test as following.

class UserSpec extends Specification {

  "User" should {

    "be saved" in {
      running(FakeApplication(additionalConfiguration = inMemoryDatabase())) {
        AppDB.database.withSession {
          implicit session: Session =>
            AppDB.dal.Users.add(User(Some(2), "hola"))
            AppDB.dal.Users.countByName("pepe") must beEqualTo(0)
            AppDB.dal.Users.countByName("hola") must beEqualTo(1)

        }
      }
    }
  }
}

And that’s it :) Now you have Play! configured for running with Slick.

Cya people!

Share!Share on FacebookTweet about this on TwitterShare on RedditShare on Google+Share on LinkedInBuffer this pageEmail this to someoneFlattr the author
  • joergviola

    Hey man, you really save my day!!!

    I tried Play 2 head with this dependency:

    “com.typesafe” % “slick_2.10.0-M7″ % “0.11.1″

    On the first insert, an exception occurred (some implicit conversion was not found due to the wrong scala version), but swallowed and I was stuck.

    Thanks!

  • daniel

    Great – that saved me much time fixing my own DAL! Many thx!
    (You can find my repo here: https://github.com/danieldietrich/slick-integration)
    Greetz
    Daniel

    • mgonto

      Hey,

      That’s awesome. I think that’s going to help a lot of Play! Developers to work with Slick.

      If you want (or need), I can give you a hand with this integration.

      However, hopefully it will become depracated once Direct Embedding is fully implemented in Slick. I’m waiting for that!

      • mgonto

        I think that Slick is much much better than Anorm, so a nice way to include this into Play! is awesome :) Congrats on the FWK

      • daniel

        hi,
        direct embedding would be cool!
        since then your help is greatly appreciated!
        just added you to the github repo.
        - daniek

  • joergviola

    Sorry again for not mentioning this article – now added it.

  • ajith

    Thanks for the great article. I am trying to make the sample work with MySQL instead of H2. Could someone tell how would I initialize the driver?

    • Martin Gontovnikas

      Hi,

      This should be pretty easy. If you take a loot at the GitHub project, you’ll see that in the application.conf of the application I’ve set H2 driver.

      Look in https://github.com/mgonto/slick-play2-example/blob/master/conf/application.conf for Database configuration

      There, you’ll need to change both the DB driver, url, user and password for your MySQL db and the slick.db.driver to point to slick’s MySQL driver.

      Thanks to CakePattern implemented in this example, by doing that, you’ve got all set up, as the Profile dependency is injected given the driver you’ve chosen in the application.conf

  • Chris Hluchan

    Thanks a lot for the article! I’m new to play so maybe I’m thinking about this wrong, but is there any way to tie in the db.create to evolutions? It seems like you wouldn’t always want to create tables, sometimes you’d want to use an existing schema when the app starts up.

    • Martin Gontovnikas

      I agree that you don’t want to create table every time. You can actually do some checking about this in the Global to say if the tables exist, don’t create them. That can be done.

      Other option may be to hook with a PLugin to Evolutions but I don’t know if it can be done and documentation for Modules in Play 2 sucks. But that’s the best option. I just don’ know if it can be done. But, this is “the first step”.

  • Rai Por

    Hi Gonto, I cannot thank you enough for the post, its really an amazing tutorial. Unfortunately, I am a newbie in Scala (and Play!) and trying to pass the list of users to a Play! action. But I failed to do that. From where should I return the list, the Users object of UserComponent trait? How can I return the list and invoke the list from a Play! actino?

    • Martin Gontovnikas

      Check out the test. You should do exactly the same as in the test but in your action. Check out https://github.com/mgonto/slick-play2-example/blob/master/test/UserSpec.scala

      • Rai Por

        Hi Gonto, thanks again. I have added following code in UserComponent —

        def getAll(implicit session: Session) = {
        (for {
        user
        AppDB.dal.Users.getAll
        }

        It works, but seem to be a little bit ‘bad-code’. Any suggestion?

  • Rai Por

    Hello Gonto, sorry to bother you again. I want to extend your tutorial
    for multiple tables/models. But it is taking me a lot of boilerplate in
    DAL, AppDB and DBeable. Can you please extend the tutorial for (at
    least) two tables/models. Please, pardon my approach for asking help in
    this way, I am a newbie in Scala.

  • Karl

    how would you implement multiple tables? Also, what does a controller look like to access a model? Great walkthrough, keep ‘em going.

  • finestglasses j.

    t Knowing how my brothers’ practical experience ahead of, he seemed to be too shy to wear his solid eyeglasses. But now as a consequence of FG mens glasses, he or she is no more bashful yet it can help him or her to possess a beneficial self-esteem.

  • http://mebelminimalis.net/ vernanda tata
  • http://daftaragenherbalterbaik.wordpress.com/ Sara Onser

    Daftar Agen Herbal

    wow this nice carp, thanks for the share