Web Development Business

Pseudo-Static Row Mappers, a Healthy Alternative to Static Row Mapping

Ryan McCullough Java, Spring Leave a Comment

Attention: The following article was published over 6 years ago, and the information provided may be aged or outdated. Please keep that in mind as you read the post.

If you know Spring, chances are pretty good that you’ve also worked with RowMappers and everyone’s #1 favorite BeanPropertyRowMapper. Okay, maybe not EVERYONE. But EVERYONE will acknowledge BPRM’s power potential and how wonderfully easy it is to use!

While BeanPropertyRowMapper may be the smartest and most beautiful on the RowMapper block, many in the industry refuse to give it the time of day, and for perfectly justified reasons.

Sometimes, when we can’t have beauty and wisdom, we’re forced to settle for loud and predictable. Yes, I’m talking about hardcoded, unchanging, tell-it-like-it-is, static RowMapper. Hate on them all you like, Static RowMappers are fast, easy to understand, and they seem to replicate like tribbles.

But, as many of you know, an application can grow into a swamp of one-off RowMappers. ESPECIALLY if you are working with a lot of high-throughput batch operations that need to run strictly optimized queries for performance as to avoid any unnecessary marshaling of data.

Recently, I’ve tried a mildly clever alternative to RowMapping I like to call Pseudo-Static Row Mappers. In this post, I introduce the basics of Pseudo-Static Row Mappers. We show how they give the tough rigid optimization and control of hard-coded naming and data typing while retaining BeanPropertyRowMapper’s spirit of freedom.

Pseudo-Static Behavior

Note: Check out my blog on vacuole encapsulation so you won’t be angry with me for not making sense in the next paragraphs.

(Okay fine! The gist of vacuoles: collections of meaty key-like bits that could be partially consumed, transported, shared, exchanged, etc. in an amoeba-like form, for singular or shared objectives of data binding.)

To accomplish this pseudo-static behavior, I use a collection of vacuole classes; one for each column, using the data source metadata to initialize & eliminate unused vacuoles on the first read.

After the initialization occurs, the remaining subset of vacuoles is iterated over and used to access the resultset data. (See the bottom source snippet for more information)

Here is an example.

public abstract class Vacuole<T> {
    public Vacuole(String key){this.KEY = key}
    private final String KEY;
    private Mapping(String name) {KEY = name;}    
    private abstract void translate(T instance, ResultSet rs) throws SQLException;
    // A lambda would have been REALLY cool, if it weren't for the blasted SQLException :(
}

… And applied, it looks like this:

public class YetAnotherRowMapper extends PseudoStaticRowMapper<Pojo> {
    public YetAnotherRowMapper(){
   	 super(PanelCheckHeader::new); 
// Yay lambda constructors!
   
	 add(new Vacuole<Pojo>("FKey_That_Looks_Exactly_the_same_in_1000_places") {
		 // The vacuole pattern enables you to reuse in all 1000 unchanging places
   		 public void translate(Pojo item, ResultSet rs) throws SQLException {
   			 item.setColumnA(rs.getInt(KEY)); 
   		 }
   	 });

	 add(new Vacuole<Pojo>("Column_B") {
   		 public void translate(Pojo item, ResultSet rs) throws SQLException {
   			 item.setColumnB(rs.getString(KEY));
   		 }
   	 });
	 add(new Vacuole<Pojo>("YetAbotherColumn”) {
   		 public void translate(Pojo item, ResultSet rs) throws SQLException {
   			 item.setColumnC(rs.getTimestamp(KEY));
   		 }
   	 });

As you can see, the end result isn’t as brutally honest as the familiar static mapper. However, because these pseudo-static row mappers so effortlessly replace static mappers, they make fast work in drying up a swamp of one-off mappers.

Unfortunately, I can’t share all of the secret sauce from the PseudoStaticRowMapper itself as it was used on a recent client (proprietary) project. But I will share a generic snippet to entertain your curiosity.


public void init(ResultSet rs){
  if(rs.isFirst(){
    for(Vacuole<T> v : vacuoles) {   		 
      for(int i = 0; i <= rs.getMetaData().getColumnCount();) {
        if(!v.KEY.equals(rs.getMetaData().getColumnName(++i))) {
          eject(v);  
          // you might also have validation to ensure all colums were handled ;)
        }
      }
    }
  }
}

Final Thoughts

I dare you: implement this approach just one time. You’ll never go back.

Demonstrate it to someone else who understands this pain, and they’ll love you for it.

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments