Dropwizard provides a very slick UnitOfWork
annotation that provides a declarative method of scoping transactional boundaries across requests.
It provides a very convenient no frills way for the developers to simply focus on the logic
and rely on Dropwizard that the work performed will be done as a unit. Anything goes wrong (more on that later) in this transaction
and it will be rolled back
else it commits
. Fine and dandy.
However, this support is only available for Hibernate
. Diving in the source code explains why, as the logic is tied extensively to Hibernate only features.
Jdbi
on the other hand is a lightweight wrapper over JDBC
API. It doesn’t nearly provide as many features as Hibernate
but is very maintainable.
Directly writing SQL
is much more convenient. Also there’s the question of ORM hate
to which I am very much aligned. However that’s not the topic of our discussion. In fact Dropwizard itself provides support for integration with Jdbi
in an easy to use dropwizard-jdbi
module. However there’s no explicit support for Unit of Work.
What we’re looking to achieve is something like this.
Notice the JdbiUnitOfWork
This is exactly what we intend to create. The following blog posts will shed light on how we achieve this.
Before proceeding further, I’d like to mention that the entire source is available on Github
and the artifact is available on Maven Central
In order to achieve this, we need to be familiar with the Jdbi
API. Let’s look at its core abstraction, a Handle
Handle
- A
handle
represents a connection to the database system represented by adatasource
. Sort of a wrapper around aJDBC
connection object that provides more features as compared to a vanillaJDBC
connection. The underlying database could be anything. Handle
interface defines a lot of methods particularly for
data queriescreate()
,select()
,batch()
,
connection queriesisOpen()
,close()
.. and importantly
transaction queriesbegin()
,commit()
,rollback()
So now, we know that a handle
instance is capable of managing transactions per connection. There’s only one implementation of handle in JDBI, the BasicHandle
class BasicHandle implements Handle {
}
Handle contract exposes a particularly interesting trait inTransaction
Handle also has the ability to attach SQL objects
to itself. The attached SQL objects
have the same lifecycle as the handle. When the handle is closed, the SQL object
becomes unusable.
Any state changes to the handle, or the SQL object
, such as transaction status, closing it, etc, will apply to both the object and the handle.
TheSql objects
are nothing but an interface that declare the query. Jdbi
provides an implementation for our object automatically using the SqlObjectBuilder
which builds the real object.
Armed with the above knowledge, we can now see the forest for the trees. To achieve transactionality, we set the following
- Initialise a
handle
- Attach
dao's
to thehandle
, thus tying their lifecycle with thehandle
- Begin a transaction context and perform work in a
unit
- Close the
handle
Although the above code works, it still is far away from the ideal @JdbiUnitOfWork
that we imagined. Here are a few problems
with the current approach:
Handle
is exposed to the resource layer. A core persistence layer construct has to be propagated upwards.- Every resource method would need to specify a
inTransaction()
which would wrap mark the explicitstart
andend
of a transaction boundary.
The above example is a bit contrived, but the situation will definitely not ameliorate when multiple dao’s and resource methods are involved.
The plan
- Since we now know that objects can be
attached
to ahandle
, we have to ensure that whenever we call any method on anyDAO
(in query path), it is already attached to ahandle
. We plan to achieve this withJava proxies
in the upcoming sections. - Once our
Dao's
have the ability to be attached to a commonhandle
, we need to ensure that weinitialise
andterminate
this handle for every request served by Dropwizard. We plan to do this usingJersey Event Monitoring
andapplication listeners.
- Once we receive a result from the business layer, which is where our logic resides, we need to
commit
orrollback
the transaction marked by our handle accordingly.
It’s going to be an exciting road ahead building these pieces and finally seeing our @JdbiUnitOfWork
coming into life.
We will also discuss, how we can spawn multiple threads off of a single request thread currently being served and preserve the handle across all of them. Stay tuned for future parts. Also do critique and provide your valuable feedback :)