Database-Driven Web Apps with Play!

Last Updated: 20 March 2014

play

Table of Contents

This guide will get you going with database-driven Play! Framework apps on the Cedar stack.

If you have questions about Java on Heroku, consider discussing them in the Java on Heroku forums.

Prerequisites

  • Java, Play! Framework version 1.2.3, Git, and the Heroku client (as described in the basic Play! quickstart)
  • An installed version of Postgres to test locally

Write your app

Follow the steps in the Play! quickstart to build a basic Play! application with process definition in Procfile.

Now continuing on the same application, create a model class called User:

app/models/User.java

package models;

import java.util.*;
import javax.persistence.*;

import play.db.jpa.*;

@Entity
@Table(name="my_user")
public class User extends Model {

    public String email;
    public String fullname;
    public boolean isAdmin;

    public User(String email, String fullname, boolean isAdmin) {
        this.email = email;
        this.fullname = fullname;
        this.isAdmin = isAdmin;
    }

}

Create a data file containing some initial test data for the UserProfile entity

conf/initial-data.yml

User(bob):
  email: bob@gmail.com
  fullname: Bob Clark
  isAdmin: false
User(joe):
  email: joe@gmail.com
  fullname: Joe Smith
  isAdmin: false

Create a bootstrap class that loads the initial data set

app/Bootstrap.java

import play.*;
import play.jobs.*;
import play.test.*;

import models.*;

@OnApplicationStart
public class Bootstrap extends Job {

    public void doJob() {
        // Check if the database is empty
        if(User.count() == 0) {
            Fixtures.loadModels("initial-data.yml");
        }
    }

}

Now let’s update the controller to let us access a list of users with a REST JSON call. Add a users() method to the controller:

app/controllers/Application.java

package controllers;

import play.*;
import play.mvc.*;

import java.util.*;

import models.*;

public class Application extends Controller {

    public static void index() {
        render();
    }

    public static void users() {
        List<User> users = User.findAll();
        renderJSON(users);
    }

}

Add a route from /users to this new method in the routes file

conf/routes

# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

# Home page
GET     /                                       Application.index

# New route to users method
GET     /users                                  Application.users

# Ignore favicon requests
GET     /favicon.ico                            404

# Map static resources from the /app/public folder to the /public path
GET     /public/                                staticDir:public

# Catch all
*       /{controller}/{action}                  {controller}.{action}

Set database configuration to use DATABASE_URL for connection URL and set the Hibernate dialect to Postgres by adding these lines to the bottom of conf/application.conf

conf/application.conf

db=${DATABASE_URL}
jpa.dialect=org.hibernate.dialect.PostgreSQLDialect
jpa.ddl=update

(we don’t recommend setting jpa.ddl to update for a real world production app. Use Play!’s database evolutions instead.)

Run your app locally

Ensure you have the DATABASE_URL local environment variable pointed to your local Postgres database. For example:

$ export DATABASE_URL=postgres://scott:tiger@localhost/play

Run your app:

$ play run
~        _            _
~  _ __ | | __ _ _  _| |
~ | '_ \| |/ _' | || |_|
~ |  __/|_|\____|\__ (_)
~ |_|            |__/
~
~ play! 1.2.4, http://www.playframework.org
~
~ Ctrl+C to stop
~
Listening for transport dt_socket at address: 8000
21:07:28,080 INFO  ~ Starting /Users/test/dev/helloworld
21:07:28,603 WARN  ~ You're running Play! in DEV mode
21:07:28,715 INFO  ~ Listening for HTTP on port 9000 (Waiting a first request to start) ...

Now, in another terminal window, fetch the list of users as JSON using curl:

$ curl -i http://localhost:5000/users
HTTP/1.1 200 OK
Server: Play! Framework;1.2.x-bfb715e;dev
Content-Type: application/json; charset=utf-8
Set-Cookie: PLAY_FLASH=;Expires=Sun, 21-Aug-11 21:46:03 GMT;Path=/
Set-Cookie: PLAY_ERRORS=;Expires=Sun, 21-Aug-11 21:46:03 GMT;Path=/
Set-Cookie: PLAY_SESSION=;Expires=Sun, 21-Aug-11 21:46:03 GMT;Path=/
Cache-Control: no-cache
Content-Length: 145

[{"email":"bob@gmail.com","fullname":"Bob Clark","isAdmin":false,"id":2},{"email":"joe@gmail.com","fullname":"Joe Smith","isAdmin":false,"id":3}]

Looks good.

Check in your changes

$ git add .
$ git commit -m "added user profile entity and configured database"

Deploy to Heroku/Cedar

Deploy your code:

$ git push heroku master
Counting objects: 50, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (40/40), done.
Writing objects: 100% (50/50), 38.07 KiB, done.
Total 50 (delta 9), reused 0 (delta 0)

-----> Heroku receiving push
-----> play app detected
-----> Installing Play!..... done
-----> Building Play! application...
       ~        _            _
       ~  _ __ | | __ _ _  _| |
       ~ | '_ \| |/ _' | || |_|
       ~ |  __/|_|\____|\__ (_)
       ~ |_|            |__/
       ~
       ~ play! 1.2.x-bfb715e, http://www.playframework.org
       ~
       1.2.x-bfb715e
       Resolving dependencies: .play/play dependencies ./ --forceCopy --sync --silent -Duser.home=/tmp/build_j1fa3o1m0jj2 2>&1
       ~ Resolving dependencies using /tmp/build_j1fa3o1m0jj2/conf/dependencies.yml,
       ~
       ~
       ~ No dependencies to install
       ~
       ~ Done!
       ~
       Precompiling: .play/play precompile ./ --silent 2>&1
       Listening for transport dt_socket at address: 8000
       21:50:44,177 INFO  ~ Starting /tmp/build_j1fa3o1m0jj2
       21:50:44,781 INFO  ~ Precompiling ...
       21:50:48,135 INFO  ~ Done.

-----> Built 1 Play! configuration(s).
-----> Discovering process types
       Procfile declares types -> web
-----> Compiled slug size is 26.2MB
-----> Launching... done, v6
       http://growing-snow-502.herokuapp.com deployed to Heroku

Test it with

$ curl -i http://growing-snow-502.herokuapp.com/users
HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Type: application/json; charset=utf-8
Server: Play! Framework;1.2.x-bfb715e;prod
Set-Cookie: PLAY_FLASH=;Expires=Sun, 21-Aug-11 21:57:49 GMT;Path=/
Set-Cookie: PLAY_ERRORS=;Expires=Sun, 21-Aug-11 21:57:49 GMT;Path=/
Set-Cookie: PLAY_SESSION=;Expires=Sun, 21-Aug-11 21:57:49 GMT;Path=/
Content-Length: 145
Connection: keep-alive

[{"email":"bob@gmail.com","fullname":"Bob Clark","isAdmin":false,"id":1},{"email":"joe@gmail.com","fullname":"Joe Smith","isAdmin":false,"id":2}]

Looks good.

Further reading

Play! makes it easy to build more complex models and to add Web UIs. The best place to go from here is the very comprehensive step-by-step tutorial to creating a real-world app in Play!.