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.
Background
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.
Existing resources
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
to the 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 theorg.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 likejava.lang.NoClassDefFoundError: com/google/inject/multibindings/MapBinder
. -
javaJdbc
- is a needed dependency to add the Play Database support and to access the Connection Pool / DataSource - namely theplay.db.DBModule
which provides access to theDatabase
class we need.
Note that there should be the following line present in your build.sbt
,
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).
Step 2: Configure MyBatis
MyBatis Guice offers us a base module to inherit from: org.mybatis.guice.MyBatisModule
.
so we define a module
Note that we don’t override configure
as usual in Module
s
because it is already finally implemented in org.mybatis.guice.AbstractMyBatisModule
,
which delegates to initialize
instead.
- 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.
- The
mybatis.configuration.failfast
is aBoolean
property, which causes the configuration to eagerly load some configurations. This will cause myBatis to provide us earlier with error messages. - The
PlayDataSourceProvider
is a link to the class below. TheDatabase
class 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 theEnvironment
and theConfiguration
classes, see the example from the documentation (additional note: with exactly this signature!).
With declaring a separate class (thePlayDataSourceProvider
) which is dependent on theDatabase
, 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@Singleton
annotation. 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. - The
TransactionFactory
creates, well,Transaction
s for us.JdbcTransactionFactory
is the proper choice for us here. -
addMapperClass(UserService.class)
registersUserService
as 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 app/service/UserService.java
.
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 UserMapper.java
file.
It is important that the file is under app/service/UserMapper.xml
(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 service.UserMapper
and could look like Mapped SQL Statement out of the Getting started,
however the namespace
attribute should match the fully qualified name of the Interface
and 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:
- The
UserMapper
which 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
users
andprofile
with the corresponding parameters.
For more enhanced examples it might necessary to have a look at the @Transactional
annotation.
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 compile
scope)
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 target
directory.
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
<PrjRoot>/target/scala-2.11/classes/UserMapper.xml
.
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 UserMapper
under services
.
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 UserMapper.xml
instead of services/UserMapper.xml
(derived from the package name).
This is the reason why we need to add the app
root directory to the unmanagedResourceDirectories
.
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 java
and 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.
- Benefit:
- The XML file lies next to the source file in the source tree.
- Disadvantage:
- 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 app
and conf
(Tip: you can check that yourself by inspecting the property, remember?).
What if we include the file to conf/services
to begin with?
- Benefit:
- No mixture of source and resource path and we don't need to filter the resources to not contain java files.
- Disadvantage:
- 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 conf
route.
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 app/services
folder.
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 service.UserMapper
just add the following snippet to the default conf/logback.xml
:
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
(remove the TRACE_LEVEL_FILE=0
part from the JDBC Url in the application.conf
file).
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/service
but 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.