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
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>
}



[...] 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 [...]
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!
[...] A better example of Play Framework 2.0 with Mybatis For Scala beta « FDM Tech. [...]
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/