The Play Framework is getting more popular and Version 2.4 finally includes Dependency Injection as the default mechanism to access central resources instead of singletons.
I personally loathe the singleton pattern, perhaps more than it deserves. However this made me gladly jump on the train for version 2.4.
This will be a tutorial how to use MyBatis with Guice and the Play Framework. Why this tech stack?
- I really like the approach taken by the Play Framework. This potentially means I would like Ruby on Rails as well, but I prefer statically compiled languages, so well, I use Play.
- Guice is the default dependency injection framework in Play, but on top of that it stores its configuration in Java (instead of e.g. XML).
- MyBatis is traditionally configured in XML but offers an integration with Guice as well. It is a persistence framework (non-JPA) which is SQL oriented.
There is a code sample accompanying this blog post available at GitHub.
Working on my main project from time to time the need to investigate certain incidents arised. For this I needed to some reports taken from the database, which are not normally correlated by the application. However for reasons not discussed here, this could not be part of the main application.
What I did instead is to create a maintenance application with Play and Java, which worked on the same DB schema and read the same data as my main project, however it was primarly focussed on analyzing these incidents. For this I needed a persistence layer which has non-commercial support for Oracle Databases in Java (which removes Slick or jOOQ from my list), is highly customizable and can be integrated to an already existing database. That left me with with the following options (as far that I know):
- eBean which was the primary persistence framework in Play before Play-1518.
- MyBatis which I had prior experience with
- JPA as a default fit on Java.
As you have guessed by now I have chosen MyBatis for its adaptability (You can adapt JPA to some degree but I needed more - I know because I tried…). I decided against eBean mainly because of next to no experience with it and the reason it was deprecated in Play scared me away of it.
If you look for how to set up this tech stage you will come across Inoio’s nice tutorial for Play 2.1 which has an updated version done by Alex Klibisz which is nearly up to date (Play 2.3.6).
They do a great job in showing a possible way of doing things, however they don’t go into detail about the reasoning why they do it the way they do. This is helpful information in case something goes wrong or if you want to deviate from the choices they took. I personally always want certain decisions a bit differently, which is why nearly all resources almost never fully match the setup I want.
To quote something I learned from a Videogame (Sid Meier’s Civilization which claims the origin is a man named Lao Tzu):
Give a man a fish and you feed him for a day. Teach a man to fish and you feed him for a lifetime
I want this post to be different, instead of showing you a possible implementation and let you start from there, I want to take you on a journey on why I did it the way I did
- so that you can better understand my choices and deviate from them in case you don’t.
Step 1: Configure build.sbt
We need to add the needed libraries, so we add
build.sbt file. Let’s see what we’ve just added here:
org.mybatis.mybatis:3.3.0- the MyBatis dependency is straight forward. That is the persistence framework we wan’t to use.
org.mybatis.mybatis-guice- MyBatis also offers a Guice integration, which plays nicely into the setup Play already has (beginning from version 2.4). This seems to be a good fit. It brings us the
org.mybatis.guice.configuration.ConfigurationProvider, which is basically where we inject deviating configuration into. It can also serve as a place to look for possible configurations, but be aware that if you miss an option there, be sure to check the MyBatis XML Configuration as well, as it seems this is the primary maintained source.
com.google.inject.extensions:guice-multibindings- the Guice-Multibinder extension is actually a dependency for MyBatis-Guice. If you miss to add it, you might be greeted with something like
javaJdbc- is a needed dependency to add the Play Database support and to access the Connection Pool / DataSource - namely the
play.db.DBModulewhich provides access to the
Databaseclass we need.
Note that there should be the following line present in your
which means that we want to use the new dependency injecting router.
(This is the default when creating new projects, e.g. by
activator new - but if you migrate you need to add it manually).
routesGenerator := InjectedRoutesGenerator
Step 2: Configure MyBatis
MyBatis Guice offers us a base module to inherit from:
so we define a module
Note that we don’t override
configure as usual in
because it is already finally implemented in
which delegates to
- The environment ID is a mandatory configuration for MyBatis, but can be useful where we need different environments. This is basically the equivalent to the XML Configuration of the environment.
Booleanproperty, which causes the configuration to eagerly load some configurations. This will cause myBatis to provide us earlier with error messages.
PlayDataSourceProvideris a link to the class below. The
Databaseclass will be provided by play at runtime, but it is not available at the time the configuration is wired so it is not possible to inject it directly into the module. The only things that can be injected directly into modules are the
Configurationclasses, see the example from the documentation (additional note: with exactly this signature!).
With declaring a separate class (the
PlayDataSourceProvider) which is dependent on the
Database, we can delay this dependency and then Guice will take care of the dependency for us.
Also even if I loathe the Singleton pattern, I am fine with something being a Singleton, that is in this example I am fine with the
@Singletonannotation. That is because this one is easily exchangeable and the Injection Framework guarantees its uniqueness. A choice which can be easily revoked which is in contrast to the use of the pattern. There are also additional reasons, but well, that’s another topic.
Transactions for us.
JdbcTransactionFactoryis the proper choice for us here.
UserServiceas a mapper, which directly leads us to the next chapter.
Step 3: Add Services / Mapper to the project
Let’s say we want to add our service to
We define an interface like in the MyBatis Getting Started
However this time I don’t go for the Annotation based approach (e.g.
@Select("SELECT * FROM Users ORDER BY id"))
which would also be possible, like in the tutorial, but elaborate on the
[…] a MyBatis mapper XML file could also be used
part of the Getting Started Guide for multiple reasons:
- The annotation based approach is already covered by the other tutorials.
- I thought that for my running example I needed more power and MyBatis is still primarly focussed on XML configuration (even though I am using the MyBatis-Guice integration and not the XML configuration).
In hindsight I didn’t use the more powerful features, so the annotation approach might have been sufficient as well.
For that to work we add a
UserMapper.xml file right next to the
It is important that the file is under
(Note: this decision will be revoked later in the post but is the very first approach I took).
app is the default source root in play applications,
so the full qualified name of the
UserMapper should be
and could look like Mapped SQL Statement out of the Getting started,
namespace attribute should match the fully qualified name of the
Id of the
Select statement should be
getUserById to match the method from the interface.
However this would still fail, because Play won’t copy the XML file to the classpath. The answer is already provided by the previous tutorials:
Wire it together in the controller
Now lets reap the fruits of our previous labour: Use MyBatis in conjunction with Guice in Play 2.4 depency injected:
This is a basic example how to use it. It requires:
UserMapperwhich is an instance of the previously defined interface and which will be injected by Guice into the controller.
For details on how to use the XML configuration please see the documentation for Mapped SQL Statement or the corresponding working example in the GitHub Repository.
- Some view templates
profilewith the corresponding parameters.
For more enhanced examples it might necessary to have a look at the
This is a feature of MyBatis-Guice which automatically wraps the call in a transaction.
For these simple
select statements it seems not to be necessary.
Step 3.5: Inspect the Build or What are we doing here?
Now we have a fully working example, but let’s take a step back and review what we have done to achieve this. The lines which caught my attention were those:
A good approach to get to know what happens here is that SBT
(or Activator or the Play CLI which are all basically SBT) allows us to inspect the build.
So let’s do this by entering
sbt and hopefully we find out why this all works together.
So basically we determined that the
copyResources task (in the
transitively depends on
unmanagedResources which is why this advice is useful.
Even better, we can now show the value of the property
(Sadly the indentation is not that nice if you do it yourself)
So we can now even validate if the resource is copied to the
However what I dislike is that the we now have merged the resource directory with the source directory,
which was previously not the case.
To make things worse it is done for the whole tree!
Can we be more precise?
So let’s assume we didn’t follow the advice given from the previous tutorial and would like it
to be more specific (don’t include the whole
app directory, just the
app/services directory) like this:
Which would result in the
UserMapper.xml to reside under
Starting the application, sadly we get to know that this does not work.
Based from our
Configuration class, where the mapper is added the the framework,
we can trace the call down to
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#loadXmlResource where we can see that it looks for the
In case we added
app/services as another root it would flatten the directory path
and this lookup would fail, because then it should look for
services/UserMapper.xml (derived from the package name).
This is the reason why we need to add the
app root directory to the
However as a side effect this will add all other classes and directories to the path as well
- so this is why we filter them out again with:
That left me with a bad feeling. Instead of white listing the files we want to add,
we now added all and blacklisted (filtered) all
html files instead.
Step 4: Separating the resource tree from the source tree again
This works for SBT, however it overlaps the source directory with the resources directory.
The addition of the
excludeFilter is an indicator for this.
What’s worse the IDE I am currently working with (IntelliJ) gets confused with it
and takes all Java files as resources.
However we now have the knowledge to do this better.
What we ultimatevily want is that the
UserMapper.xml should reside under
services in the runtime classpath.
What we have done so far is to ‘fix’ SBT to include the XML file from the source directory.
- The XML file lies next to the source file in the source tree.
- This mixes up source and resource directories.
The answer to that is an easy one.
We added the
app directory to the
unmanagedResourceDirectories, so now it consists of
conf (Tip: you can check that yourself by inspecting the property, remember?).
What if we include the file to
conf/services to begin with?
- No mixture of source and resource path and we don't need to filter the resources to not contain java files.
- The XML file is now not lying right next to the interface source file.
I prefer the separation of the directory structure and not to mess with the build system,
so I take the
However I think I could also live with the other approach if I could limit the effect to mix both trees
(resource and source) only to the
This would make the build more complicated (because we then need to apply a custom rule for the resources).
I am pretty confident that this would be possible as well with SBT, but I didn’t follow that path because I was
satisfied with the
conf folder solution.
Other useful things: Logging
As MyBatis is just a short wrapper around SQL it could be useful to see the final generated SQL scripts. This is particular useful if you have a highly dynamical script where the script is constructed with multiple conditions. The only thing needed to see the final SQL statement is to configure the logger accordingly.
To enable logging for the
UserMapper, which fully qualified name is
just add the following snippet to the default
This configures the statements of this mapper to be logged.
The GitHub Example doesn’t use an Oracle but an H2 Database,
where you can of course also enable the trace of the H2 database instead
TRACE_LEVEL_FILE=0 part from the JDBC Url in the
Where to go from here
Hopefully you are now confident how to configure Play 2.4 to work with MyBatis as a persistence layer. Also you should have a basic understanding how both frameworks interact with each other and what are the things we needed to configure in Play to support MyBatis and why.
There are multiple further routes you could take now:
- I am almost sure you are already familiar with the Play Framework and Guice, however in case you are not I highly recommend to read into those two!
- If you are interested how the build works and how you can interact with SBT further,
I would recommend the blog posts SBT - A Task Engine by James Roper.
Potentially you can take a look and see if you can merge the resource and source tree only for
app/servicebut still package the XML configuration under a path where it can be found at runtime.
- If you want to tune MyBatis more specifically to your needs, I would recommend to read the MyBatis documentation. Specifially the XML documentation. The Guice configuration worked well for me so far but MyBatis primarly supports the XML configuration, so potentially there are things that can only be done there.
Also I would recommend to validate if MyBatis is the persistence framework you want to use at all or if you prefer an Object Relational Mapper (e.g. Hibernate) which take a different approach.