In a previous post I discussed making JDBC’s query and ResultSet into a Java 8 Stream. Having used that for a while, it occurred to me I ought to be able to make the row extraction more functional.
Traditionally to implement this the code you end up writing is a series of statements that call a method on ResultSet and copy the value into a bean. You ought to be able to do that as a series of chained functions right? Well you can.
What are the basics of copying one value from the ResultSet to a instance variable? You have a “bean” with a setter, a ResultSet with it’s getter, and the index of the column. Let’s start with the “getter”. We need a functional wrapper for a ResultSet getter:
That’s a BiFunction wrapping ResultSet’s getString and dealing with the Exception. That’s our getter. The setter is simple, so before we go there let’s look at how we’d use the getter.
So that will copy the value from a getter into a setter. How do we define the setter? That’s trivial with Java 8.
As you can see on line 12, the setter is just a matter of using an unbound method reference, i.e. Class::method.
The last ingredient needed is how to collect a set of these to use because you’re obviously going to pull a number of columns from the row. As a proof of concept I collected a set of these, and then did a forEach on the collection for each row. That worked but quickly I realized I could create one function out of the numerous ones by using andThen to compose a single function.
Pulling It All Together
So, to recap, the goal here is to use functional tactics to improve writing Extractors. I went with a factory approach, where the factory would gather everything you needed and return a usable Extractor. Here’s the public interface illustrated.
To use it you would:
Lines 12 through 15 above are where I use the factory to create an Extractor that pulls column one, as a string and calls Bean::setStr with it. The add method of the factory can be called repeatedly to add more extractions to the Extractor.
Here is the factories actual implementation:
In addition to the factory, I implemented all the basic getters for ResultSet as static final functions to avoid rewriting those. To see the entire implementation take a look at the project in github. There is room for cleaning and refactoring here, but my basic goal was achieved. You can easily create a straight forward Extractor with the factory, and the Extractor is fairly clean and efficient.
So having worked to keep the generated extractors efficient, a bit of testing was in order. I created a performance test that pitted a hand coded extractor against a generated one. The test was low tech, I simply created a mock ResultSet and then looped the two extractors against it and noted the times. What did I find? Well generally the two extractors where on par with one another, with the coded one performing slightly better on average. About as would have expected.