Ouertani Slim was born in Tunisia in 1981. Now he is a software engineer since 2004 and he is Java 6 and Spring certified with 100% and 94% respectively. He is very interested in Java technology, Scala and open source projects. He believes that programming is one of the hardest jobs and most beautiful in the world. Slim has posted 32 posts at DZone. You can read more from them at their website. View Full User Profile

Getting Started with Play 2.1 , Scala 2.10 and Slick 0.11.1

10.01.2012
| 15099 views |
  • submit to reddit

Starting from scala 2.10 version , Slick will be by defaut the database manager. In addition,  Playframework also will be based on scala 2.10  since version 2.1.

In this tutorial, I will try to present an example using the last version of slick and scalatest based on scala 2.10.0-M7 and a developpment version of playframework

This post uses the famous tutorial !

Setup Slick in Play 2

If you don't already have a Play 2 project, then create a new one after installing Play 2:

play new mySlickApp

Choose Scala as the language for the project.

The Slick library needs to be added to the Play project. 

Edit the project/Build.scala file and update the dependencies:

val appDependencies = Seq(
   "com.typesafe" % "slick_2.10.0-M7" % "0.11.1"
)
Then if you are using an Eclipse or IntelliJ Play can automatically create the project for you using either:
play idea
or
play eclipsify
Note that we created the project files after updating the dependencies, so the projects would be configured with the required libraries. If the dependencies change in the future, just re-run the commands to create the projects.

You can now start the application from within your project's root directory:

play ~run

Verify that the server is running by opening the following URL in your browser: http://localhost:9000

For local testing we will use an in-memory "h2" database. To setup Play to use that database, edit the conf/application.conf file and uncomment or add the following lines:

db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"

The final setup step is to provide Slick a database connection. This is easily done by adding a Global class that can hook into the startup phase of the Play application lifecycle. Create a new file named app/Global.scala containing:

import play.api.db.DB
import play.api.GlobalSettings
// Use H2Driver to connect to an H2 database
import scala.slick.driver.H2Driver.simple._

// Use the implicit threadLocalSession
import Database.threadLocalSession

import play.api.Application
import play.api.Play.current


object Global extends GlobalSettings {

  override def onStart(app: Application) {

    lazy val database = Database.forDataSource(DB.getDataSource())
 
  }
}

On application startup the  configuration parameter will be used to determine which driver to use to setup the database connection.

If you reload the http://localhost:9000 webpage in your browser, everything should still be working and you should see the following message in the Play STDOUT log:

[info] play - database [default] connected at jdbc:h2:mem:play

Create an Entity

Now lets create a simple entity object that will be used to persist data into the database. Create a new file named app/models/Bar.scala containing:
package models

import scala.slick.driver.H2Driver.simple._


case class Bar(id: Option[Int] = None, name: String)

object Bars extends Table[Bar]("bar") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)

  // This is the primary key column
  def name = column[String]("name")

  // Every table needs a * projection with the same type as the table's type parameter
  def * = id.? ~ name <>(Bar, Bar.unapply _)
}
This is a very simple entity that will store a list of Bar objects. Each Bar has a name and an id property for the primary key. The case class in Scala is immutable and basically supercharges a class, adding a number of syntactic conveniences. It also allows it to be used for pattern matching, which can be quite handy when matching form values returned from the client. The Bars object is an instance of Table  Bar that Slick will map into the database. 

Begineer about slick, I will not use evolution to create database but let slick create it for me, let's modify Global.scala as bellow :
import play.api.db.DB
import play.api.GlobalSettings
// Use H2Driver to connect to an H2 database
import scala.slick.driver.H2Driver.simple._

// Use the implicit threadLocalSession
import Database.threadLocalSession

import play.api.Application
import play.api.Play.current
import models.Bars

object Global extends GlobalSettings {

  override def onStart(app: Application) {

    lazy val database = Database.forDataSource(DB.getDataSource())

    database .withSession {
     Bars.ddl.create
    }
  }
}
reload the http://localhost:9000 webpage to create bar table on your local in-memory database.
Test the Model

The testing support in Play 2 is very powerful and fits well with the Test Driven Development style. Play 2 with Scala uses specs2 as the default for testing but we prefer ScalaTest. Lets create a simple test for the Bar model object. Start by adding the ScalaTest dependency to the project and modifying the testOptions setting.  Since scala 2.10.0-M6 scala-actor is an independent jar and must be add as dependencies.
Update the project/Build.scala file to contain:
val appDependencies = Seq(
      "org.scalatest" % "scalatest_2.10.0-M7" % "2.0.M4-2.10.0-M7-B1" % "test",
      "org.scala-lang" % "scala-actors" % "2.10.0-M7"    % "test",
      "com.typesafe" % "slick_2.10.0-M7" % "0.11.1"
)

val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
  testOptions in Test := Nil
  // Add your own project settings here
)
Now create a new file named test/BarSpec.scala containing.
import models.{Bars, Bar}

import org.scalatest.FlatSpec
import org.scalatest.matchers.ShouldMatchers
// Use H2Driver to connect to an H2 database
import scala.slick.driver.H2Driver.simple._

// Use the implicit threadLocalSession
import Database.threadLocalSession

import play.api.test._
import play.api.test.Helpers._

class BarSpec extends FlatSpec with ShouldMatchers {

  "A Bar" should "be creatable" in {
    Database.forURL("jdbc:h2:mem:test1", driver = "org.h2.Driver") withSession {

      Bars.ddl.create
      Bars.insert(Bar(None, "foo"))
      val b = for(b <- Bars) yield b
      b.first.id.get     should  equal(1)
    }
  }
}

Setup Slick in Play 2

If you don't already have a Play 2 project, then create a new one after installing Play 2:

play new mySlickApp

Choose Scala as the language for the project.

The Slick library needs to be added to the Play project. 

Edit the project/Build.scala file and update the dependencies:
val appDependencies = Seq(
   "com.typesafe" % "slick_2.10.0-M7" % "0.11.1"
)
Then if you are using an Eclipse or IntelliJ Play can automatically create the project for you using either:
play idea
or
play eclipsify
Note that we created the project files after updating the dependencies, so the projects would be configured with the required libraries. If the dependencies change in the future, just re-run the commands to create the projects.

You can now start the application from within your project's root directory:
play ~run
Verify that the server is running by opening the following URL in your browser: http://localhost:9000

For local testing we will use an in-memory "h2" database. To setup Play to use that database, edit the conf/application.conf file and uncomment or add the following lines:
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
The final setup step is to provide Slick a database connection. This is easily done by adding a Global class that can hook into the startup phase of the Play application lifecycle. Create a new file named app/Global.scala containing:
import play.api.db.DB
import play.api.GlobalSettings
// Use H2Driver to connect to an H2 database
import scala.slick.driver.H2Driver.simple._

// Use the implicit threadLocalSession
import Database.threadLocalSession

import play.api.Application
import play.api.Play.current


object Global extends GlobalSettings {

  override def onStart(app: Application) {

    lazy val database = Database.forDataSource(DB.getDataSource())
 
  }
}
On application startup the  configuration parameter will be used to determine which driver to use to setup the database connection.

If you reload the http://localhost:9000 webpage in your browser, everything should still be working and you should see the following message in the Play STDOUT log:
[info] play - database [default] connected at jdbc:h2:mem:play
Create an Entity

Now lets create a simple entity object that will be used to persist data into the database. Create a new file named app/models/Bar.scala containing:
package models

import scala.slick.driver.H2Driver.simple._


case class Bar(id: Option[Int] = None, name: String)

object Bars extends Table[Bar]("bar") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)

  // This is the primary key column
  def name = column[String]("name")

  // Every table needs a * projection with the same type as the table's type parameter
  def * = id.? ~ name <>(Bar, Bar.unapply _)
}
This is a very simple entity that will store a list of Bar objects. Each Bar has a name and an id property for the primary key. The case class in Scala is immutable and basically supercharges a class, adding a number of syntactic conveniences. It also allows it to be used for pattern matching, which can be quite handy when matching form values returned from the client. The Bars object is an instance of Table  Bar that Slick will map into the database. 

Begineer about slick, I will not use evolution to create database but let slick create it for me, let's modify Global.scala as bellow :
import play.api.db.DB
import play.api.GlobalSettings
// Use H2Driver to connect to an H2 database
import scala.slick.driver.H2Driver.simple._

// Use the implicit threadLocalSession
import Database.threadLocalSession

import play.api.Application
import play.api.Play.current
import models.Bars

object Global extends GlobalSettings {

  override def onStart(app: Application) {

    lazy val database = Database.forDataSource(DB.getDataSource())

    database .withSession {
     Bars.ddl.create
    }
  }
}
reload the http://localhost:9000 webpage to create bar table on your local in-memory database.
Test the Model

The testing support in Play 2 is very powerful and fits well with the Test Driven Development style. Play 2 with Scala uses specs2 as the default for testing but we prefer ScalaTest. Lets create a simple test for the Bar model object. Start by adding the ScalaTest dependency to the project and modifying the testOptions setting.  Since scala 2.10.0-M6 scala-actor is an independent jar and must be add as dependencies.
Update the project/Build.scala file to contain:
val appDependencies = Seq(
      "org.scalatest" % "scalatest_2.10.0-M7" % "2.0.M4-2.10.0-M7-B1" % "test",
      "org.scala-lang" % "scala-actors" % "2.10.0-M7"    % "test",
      "com.typesafe" % "slick_2.10.0-M7" % "0.11.1"
)

val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
  testOptions in Test := Nil
  // Add your own project settings here
)

Now create a new file named test/BarSpec.scala containing.

import models.{Bars, Bar}

import org.scalatest.FlatSpec
import org.scalatest.matchers.ShouldMatchers
// Use H2Driver to connect to an H2 database
import scala.slick.driver.H2Driver.simple._

// Use the implicit threadLocalSession
import Database.threadLocalSession

import play.api.test._
import play.api.test.Helpers._

class BarSpec extends FlatSpec with ShouldMatchers {

  "A Bar" should "be creatable" in {
    Database.forURL("jdbc:h2:mem:test1", driver = "org.h2.Driver") withSession {

      Bars.ddl.create
      Bars.insert(Bar(None, "foo"))
      val b = for(b <- Bars) yield b
      b.first.id.get     should  equal(1)
    }
  }
}

This test uses a  an in-memory database to run the test.  The body of the test simply creates a new instance of Bar and tests that the id is equal to one.  Different from Play 1, test are run from the command line using:

play test

If the tests worked, then you should see the following message in the Play STDOUT log:

[mySlickApp] $ test
[info] BarSpec:
[info] A Bar
[info] - should be creatable
[info] Passed: : Total 1, Failed 0, Errors 0, Passed 1, Skipped 0
[success] Total time: 2 s, completed Oct 9, 2012 12:28:28 PM


If you'd like to have the tests run whenever the source changes then run:


play ~test

You can keep both the ~run and ~test commands running in the background. This allows you to quickly test the application from both programmatic unit / functional tests and from manual browser tests.
Creating Bars From a Web Form

Now lets add a basic web UI for creating new Bar objects. Note that this code will not compile until this entire section is completed.

First update the app/controllers/Application.scala file to contain:

package controllers

import play.api._
import play.api.mvc._


import play.api.data.Form

import play.api.data._
import play.api.data.Forms._

// Use H2Driver to connect to an H2 database

import scala.slick.driver.H2Driver.simple._

// Use the implicit threadLocalSession
//import Database.threadLocalSession

import scala.slick.session.Session
import play.api.libs.json._

import models.{Bar, Bars}
import play.api.db._
import play.api.Play.current

// Use H2Driver to connect to an H2 database

import scala.slick.driver.H2Driver.simple._

// Use the implicit threadLocalSession

import Database.threadLocalSession


object Application extends Controller {

  lazy val database = Database.forDataSource(DB.getDataSource())


  val barForm = Form(
    mapping(
      "name" -> text
    )
      ((name) => Bar(None, name))

      ((bar: Bar) => Some(bar.name))

  )

  def index = Action {
    Ok(views.html.index(barForm))
  }

  def addBar = Action {
    implicit request =>
      barForm.bindFromRequest.value map {
        bar =>
          database withSession {
            (Bars insert bar)
          }
          Redirect(routes.Application.index())
      } getOrElse BadRequest
  }
}
The barForm provides a mapping from a request parameter named name to the name property on the Bar case class (via it's constructor). The index method has been updated to pass an instance of the barForm into the index template. We will update that template next. The addBar method binds the request parameters into an object named bar then in a session the bar is inserted into the database.  Then the user is redirected back to the index page. If the request parameters could not be mapped to a Bar using the barForm then a BadRequest error is returned.

Now we need to update the app/views/index.scala.html template to contain:
@(form: play.api.data.Form[Bar])

@main("Welcome to Play 2.0") {
    @helper.form(action = routes.Application.addBar) {
        @helper.inputText(form("name"))
        <input type="submit"/>
    }
}

The template now takes a Form[Bar] parameter which is passed from the index method on the Application controller. Then in the body of the template a new HTML form is rendered using Play 2's form helper. The form contains an HTML field for the name and a submit button. Notice that the action of the form points from the route to the Application controller's addBar method.

If you look in the console window at this point you will see the error "value addBar is not a member of controllers.ReverseApplication". This is because the route file is compiled and the view is checked for a valid route. But we haven't created a route yet, so edit the conf/routes file and add a new line with the following:

POST    /bars                       controllers.Application.addBar

This creates a HTTP route that maps POST requests for the /bars URL to the addBar method.

Now refresh http://localhost:9000 in your browser and you should see the very basic form for adding new Bar objects. If successful, after adding a new Bar the browser should just redirect back to the index page.

Now lets add a RESTful service to the application that will return all of the Bar objects as JSON-serialized data. Start by adding a new method to the app/controllers/Application.scala file:

def getBars = Action {
    val json = database withSession {
      val bars = for (b <- Bars) yield b.name.toString
      Json.toJson(bars.list)
    }
    Ok(json).as(JSON)
  }

The getBars method fetches the Bar objects from the database using Slick and then creates a JSON representation of the list of Bar objects and returns the JSON data.

Now add a new route to the conf/routes file:

GET     /bars                       controllers.Application.getBars

This maps GET requests for /bars to the getBars method.

Try this out in your browser by loading: http://localhost:9000/bars

You should see a list of the Bar objects you've created serialized as JSON.

The syntax of the query demonstrates the power of Slick type-safe query language and the power of Scala to create DSLs.  The json val is then returned in an Ok (HTTP 200 status code response) with the content type set to application/json (the value of JSON).

References:

Slick : http://slick.typesafe.com
Playframework :  http://www.playframework.org
                            https://github.com/playframework/Play20
Scala : http://www.scala-lang.org/downloads
Squeryl + Playframework + Scala  : http://www.artima.com/articles/play2_scala_squeryl.html

Published at DZone with permission of its author, Slim Ouertani.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Jacobus Reyneke replied on Mon, 2012/11/19 - 7:21am

Hi there,

Nice tutorial (although some sections are duplicates ;-)

Note that the way you use the AutoInc primary key does not work with PostgreSQL. You will have to remove the id field from the insert statement for this to work.

Cheers

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.