Building Unit Of Work Support for JDBI in Dropwizard

Aman Garg
5 min readApr 27, 2020

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.

Dropwizard’s Unit Of Work

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.

Our own Jdbi Unit Of Work

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

Release version of the library

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 a datasource. Sort of a wrapper around a JDBC connection object that provides more features as compared to a vanilla JDBC connection. The underlying database could be anything.
  • Handle interface defines a lot of methods particularly for
    data queries create(), select(), batch(),
    connection queries isOpen(), close().. and importantly
    transaction queries begin(), commit(), rollback()
Some of the many Handle API Offerings

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.

Dao: A Sql 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 the handle, thus tying their lifecycle with the handle
  • Begin a transaction context and perform work in a unit
  • Close the handle
Transaction with multiple SQL objects

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 explicit start and end 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 a handle, we have to ensure that whenever we call any method on any DAO (in query path), it is already attached to a handle. We plan to achieve this with Java proxies in the upcoming sections.
  • Once our Dao's have the ability to be attached to a common handle, we need to ensure that we initialise and terminate this handle for every request served by Dropwizard. We plan to do this using Jersey Event Monitoring and application listeners.
The plan ahead
  • Once we receive a result from the business layer, which is where our logic resides, we need to commit or rollback 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 :)

Part II: https://medium.com/@aman_garg/building-unit-of-work-support-for-jdbi-in-dropwizard-part-ii-4857ca6c48cf

--

--