A better example of Play Framework 2.0 with Mybatis For Scala beta

This is a better example of using mybatis-scala with playframework 2.0

Note: If you are not familiar with play framework 2.0, please read this tutorial first: http://www.playframework.org/documentation/2.0/ScalaTodoList

This is a simple CRUD application:

  • Searchable List of Contacts
  • Create new Contact
  • Edit existing Contact
  • Delete existing Contact

Screens

Home Screen

Create / Edit Form

File layout

File layout

Mybatis related files

app/persistence/PersistenceContext.scala: Mybatis configuration. It uses the play datasource.

package persistence

import org.mybatis.scala.config._
import org.mybatis.scala.session.Session
import play.api.Play.current
import play.api.db.DB._

object PersistenceContext {

  //== Define mybatis session configuration ==//
  val conf = 
    Configuration(
      Environment(
        "default", 
        new ManagedTransactionFactory(),
        getDataSource()
      )
  )

  //== Register managed DAOs ==//
  conf ++= ContactStore

  //== Init mybatis context ==//
  val mybatis = conf.createPersistenceContext

  //== Define a few methods to use in your Play code ==//
  
  def withConnection[A] (block: Session => A) : A 
    = mybatis.readOnly(block)

  def withTransaction[A] (block: Session => A) : A 
    = mybatis.transaction(block)

}

app/persistence/ContactStore.scala: Contact CRUD DAO.

package persistence

import org.mybatis.scala.mapping._
import models._

object ContactStore {

  val search = new SelectListBy[String,Contact] {
    def xsql = <xsql>
      SELECT * 
      FROM contact
      WHERE 
        lower(firstname) LIKE lower(#{{value}})
        OR lower(lastname) LIKE lower(#{{value}})
      ORDER BY lastname
    </xsql>
  }

  val findById = new SelectOneBy[Long,Contact] {
    def xsql = <xsql>
      SELECT *
      FROM contact
      WHERE id = #{{id}}
    </xsql>
  }

  val update = new Update[Contact] {
    def xsql = <xsql>
      UPDATE contact 
      SET firstname = #{{firstname}}, lastname = #{{lastname}}, phone = #{{phone}}, address = #{{address}}
      WHERE id = #{{id}}
    </xsql>
  }

  val insert = new Insert[Contact] {
    def xsql = <xsql>
      INSERT INTO contact (firstname, lastname, phone, address)
      VALUES (#{{firstname}}, #{{lastname}}, #{{phone}}, #{{address}})
    </xsql>
  }

  val delete = new Delete[Long] {
    def xsql = <xsql>DELETE FROM contact WHERE id = #{{id}}</xsql>
  }

  def bind = Seq(search, findById, update, insert, delete)

}

Domain models

app/models/Contact.scala: Contact Bean (VO)

The Contact object is optional, but it is useful for pattern matching

package models

class Contact {
  var id : Long = _
  var firstname : String = _
  var lastname : String = _
  var phone : String = _
  var address : String = _
}

object Contact {

  def apply(id: Option[Long], firstname: String, lastname: String, phone: String, address: String) = {
    val c = new Contact
    c.id = id.getOrElse(0)
    c.firstname = firstname
    c.lastname = lastname
    c.phone = phone
    c.address = address
    c
  }

  def unapply(c : Contact) = Some((Option(c.id), c.firstname, c.lastname, c.phone, c.address))

}

Main application controller

app/controllers/Contacts.scala: The Play Controller

package controllers

import play.api._
import play.api.mvc._
import play.api.data._
import play.api.data.Forms._
import persistence.PersistenceContext._
import persistence._
import models._
import play.api.data.format.Formats._

object Contacts extends Controller {

  val searchForm = Form[String] (
    "filter" -> text
  )

  val editForm = Form[Contact] (
    mapping(
      "id" -> optional(of[Long]),
      "firstname" -> nonEmptyText,
      "lastname" -> nonEmptyText,
      "phone" -> nonEmptyText,
      "address" -> nonEmptyText
    )(Contact.apply)(Contact.unapply)
  )

  def home(filter : String = "")  = Action {
    withConnection { implicit s =>
      val contacts = ContactStore search "%" + filter + "%"
      Ok(views.html.contacts.home("Contacts", contacts, searchForm))
    }
  }
 
  def search = Action { implicit req =>
    searchForm.bindFromRequest.fold(
      formWithErrors => Redirect(routes.Contacts.home("")),
      filter => Redirect(routes.Contacts.home(filter))
    )
  }

  def create = Action {
    Ok(views.html.contacts.form("New Contact", editForm))
  }

  def edit(id : Long) = Action {
    withConnection { implicit s =>
      ContactStore.findById(id) match {
        case None => BadRequest(<h1>Contact {id} does not extists!</h1>)
        case Some(contact) => 
          val f = editForm.fill(contact)
          Ok(views.html.contacts.form("Edit Contact", f))
      }
    }
  }

  def delete(id : Long) = Action {
    withTransaction { implicit s =>
      ContactStore delete id
      Redirect(routes.Contacts.home(""))
    }
  }

  def save = Action { implicit req =>
    withConnection { implicit s =>
      editForm.bindFromRequest.fold(
        formWithErrors => BadRequest(views.html.contacts.form("Edit Contact", formWithErrors)),
        contact  => {
          if (contact.id == 0) ContactStore.insert(contact) else ContactStore.update(contact)
          Redirect(routes.Contacts.home(""))
        }
      )
    }
  }
      

}

Views

app/views/contact/home.scala.html : Contact list view

@(message: String, contacts : List[Contact], searchForm: Form[String])

@import helper._

@main("Welcome to Play 2.0") {
    
    <h1>@message</h1>

    <form action="@routes.Contacts.search" method="GET">
        <input type="text" name="filter" id="filter" />
        <input type="submit" value="Search"/>
    </form>

    <table class="datatable">
        <tr>
            <th>Id</th>
            <th>Name</th>
            <th>Phone Number</th>
            <th>Address</th>
        </tr>
        @contacts.map { c =>
            <tr>
                <td>
                    @helper.form(routes.Contacts.edit(c.id), 'style -> "float: left;") {
                        <input type="submit" value="Edit"/>
                    }
                    @helper.form(routes.Contacts.delete(c.id), 'style -> "float: left;") {
                        <input type="submit" value="Delete"/>
                    }
                </td>
                <td>@c.lastname, @c.firstname</td>
                <td>@c.phone</td>
                <td>@c.address</td>
            </tr>
        }
    </table>

    <a href="@routes.Contacts.create" style="margin-top: 10px; display: block;">Create new Contact</a>
}

app/views/contact/form.scala.html : Contact form view

@(message: String, f : Form[Contact])

@import helper._

@implicitFieldConstructor = @{ FieldConstructor(simpleInput.f) } 

@main(message) {

  <h1>@message</h1>
  <fieldset style="width: 500px;">
    @helper.form(routes.Contacts.save) {
      @inputText(f("id"), '_label -> "Id", 'readonly -> "readonly")
      @inputText(f("firstname"), '_label -> "First name")
      @inputText(f("lastname"), '_label -> "Last name")
      @inputText(f("phone"), '_label -> "Phone number")
      @inputText(f("address"), '_label -> "Address")
      <fieldset style="margin-top: 10px;">
          <input type="submit" value="Save" />
          <a href="@routes.Contacts.home("")">Cancel and go home</a>
      </fieldset>
    }        
  </fieldset>
  
}

Complete Source Code at GitHub

https://github.com/mnesarco/play2-mybatis-scala-better-sample

Update 2013-05-23

This article is translated to Serbo-Croatian language by Anja Skrba from Webhostinggeeks.com.

5 Comments

  1. [...] 2.0 primarily supports EBean and JPA. There are hacks to support iBatis and other lightweight ORMs but it is not officially supported. Share this:TwitterFacebookLike this:LikeBe the first to like this. This entry was posted in [...]

  2. This looks great, Frank. I can’t wait to try this. I discovered it while looking for the right way to case match on findById, which was just what I needed. I’ll be back! :grin:

  3. [...] A better example of Play Framework 2.0 with Mybatis For Scala beta « FDM Tech. [...]

  4. I just wrote a simple howto for play 2.1 (managed controllers) + guice + mybatis integration: http://inoio.de/blog/2013/02/07/integrating-mybatis-guice-play2/

  5. chirag says:

    Hi nice article so helpful me and can You post this type of article in Java … So helpful for Java beginner in playfrmawork