How to Integrate MyBatis With Play Framework 2.1 (and Guice)

As this has been asked on the play mailinglist this post shall guide you through the setup of MyBatis with Play2. For the integration of MyBatis and Play we’re using the MyBatis-Guice subproject, so we can inject MyBatis mappers into managed play controllers (currently only documented in Play 2.1 Highlights) - or mappers into repositories into services into controllers if you like ;-)

Check out the following steps to Play with MyBatis.

Update 2013/02/12: The source code is now also available on github.

Create new Play project

At first we create a new Play 2 java project, following the documentation - Creating a new application.

$ play new playMyBatisSample
    _            _
_ __ | | __ _ _  _| |
| '_ \| |/ _' | || |_|
|  __/|_|\____|\__ (_)
|_|            |__/

play! 2.1.0 (using Java 1.7.0_09-icedtea and Scala 2.10.0), http://www.playframework.org

The new application will be created in /home/magro/proj/inoio/playMyBatisSample

What is the application name? [playMyBatisSample]
> play-mybatis-sample

Which template do you want to use for this new application? 

1             - Create a simple Scala application
2             - Create a simple Java application

> 2
OK, application play-mybatis-sample is created.

Have fun!

After cd‘ing to playMyBatisSample we start the play console and check if everything’s fine so far:

$ cd playMyBatisSample/
$ play
[play-mybatis-sample] $ run
... [downloading some artifacts left out here]
--- (Running the application from SBT, auto-reloading is enabled) ---

[info] play - Listening for HTTP on /0:0:0:0:0:0:0:0:9000

(Server started, use Ctrl+D to stop and go back to the console...)

In the browser we can load http://localhost:9000/ and see s.th. like Your new application is ready. on a starting page.

Add MyBatis Guice Dependency

Next we add the mybatis and mybatis-guice dependencies to project/Build.scala, so that appDependencies looks like this:

val appDependencies = Seq(
  "org.mybatis" % "mybatis" % "3.1.1",
  "org.mybatis" % "mybatis-guice" % "3.3",
  javaCore, javaJdbc
)

>>> Update 2013/05/03:

Add mapper xml files to classpath

If we’re keeping mapper xml files besides the mapper java classes we also need to add the xml files to the classpath. To achieve this we have to adjust the settings in project/Build.scala:

val main = play.Project(appName, appVersion, appDependencies).settings(
  // Add app folder as resource directory so that mapper xml files are in the classpath
  unmanagedResourceDirectories in Compile <+= baseDirectory( _ / "app" ),
  // but filter out java and html files that would then also be copied to the classpath
  excludeFilter in Compile in unmanagedResources := "*.java" || "*.html"
)

<<< END Update 2013/05/03

To let play/sbt know about the changes to the project configuration we have to reload it:

[play-mybatis-sample] $ reload

Setup Guice + MyBatis in Global.java

The guice and mybatis setup will happen in the Global.java. The Global object allows to hook into application start and is responsible for retrieving managed controllers, so this is the right place for the guice injector.

import java.util.Properties;

import javax.sql.DataSource;

import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
import org.mybatis.guice.MyBatisModule;

import play.Application;
import play.GlobalSettings;
import play.db.DB;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Provider;
import com.google.inject.name.Names;

public class Global extends GlobalSettings {

  private Injector injector;

  @Override
  public void onStart(Application app) {
    injector = Guice.createInjector(new MyBatisModule() {
      @Override
      protected void initialize() {
        bindDataSourceProvider(new Provider<DataSource>() {
          @Override
          public DataSource get() {
            // use db as configured in conf/application.conf
            return DB.getDataSource();
          }
        });
        bindTransactionFactoryType(JdbcTransactionFactory.class);

        final Properties myBatisProperties = new Properties();
        myBatisProperties.setProperty("mybatis.environment.id", "default");
        Names.bindProperties(binder(), myBatisProperties);
      }
    });
  }

  @Override
  public <A> A getControllerInstance(Class<A> clazz) throws Exception {
    return injector.getInstance(clazz);
  }

}

As DataSource for MyBatis we use the one provided by play, so we have to remove comments from db.default.* in conf/application.conf.

Notice that we don’t have setup any mappers or other MyBatis artifacts in our MyBatisModule, as we want to keep it as simple as possible. To see how to do this check out MyBatis Guice Documentation.

Inject MyBatis class into managed controller

Now that we have configured MyBatis we can use it in our managed controllers. To use the Application controller as managed controller we have to add an @ to the relevant controller mapping in conf/routes

# Home page
GET     /                           @controllers.Application.index

and also change the index() method to an instance method (see below).

To use MyBatis in the Application controller we inject some arbitrary MyBatis object and print some info on the start page - just to see that the integration is working. Normally we would inject some mapper/repository/service here. This is the very simple Application controller:

package controllers;

import javax.inject.Inject;

import org.apache.ibatis.mapping.Environment;

import play.mvc.Controller;
import play.mvc.Result;
import views.html.index;

public class Application extends Controller {

  @Inject
  private Environment myBatisEnv;

  public Result index() {
    return ok(index.render("Your app is running with ds " + myBatisEnv.getDataSource()));
  }

}

We’re done: Now we can reload the start page in the browser and see that MyBatis is integrated with Play.

Comments