The Dark Side of Frameworks

Framework seems to be a pretty hot buzzword these days. Not that it hasn’t been for years, but now more and more clients are asking for frameworks for all kinds of things. Frameworks upon frameworks actually.

The most recent example is a client who wanted a framework that would wrap the way you could call stored procedures via JDBC. I was originally against the idea, but being all about customer service, I know when to give up the fight and just roll with it. What one of my developers ended up creating is a framework that wraps JDBC and allows you to save a few lines of code per procedure call, not bad really, but not really enough to change my mind about the whole thing in general.

I could see why they liked the idea, it did hide some of the perceived complexity from the user of the framework, but the reality was it really didn’t do too much more than make sure the close statement was in a finally block and allow the user of the framework to bypass seting all the parameters in a callable statement.

One framework we looked at using instead of creating our own was the Spring framework’s wrapper for JDBC. I don’t want to start a flame war, but honestly, what’s the deal with that monstrosity? What does it actually simplify? From the examples in the documentation I looked at, it seemed simple enough, but that was only because it was doing simple things. When I took a look at called Stored Procedures it didn’t look so simple any more.

Take the example of calling the sysdate() function from Oracle. This is the code for JDBCTemplate:

import java.sql.Types;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.datasource.*;
import org.springframework.jdbc.object.StoredProcedure;

public class TestStoredProcedure {

    public static void main(String[] args)  {
        TestStoredProcedure t = new TestStoredProcedure();
        t.test();
        System.out.println("Done!");
    }

    void test() {
        DriverManagerDataSource ds = new DriverManagerDataSource();
        ds.setDriverClassName("oracle.jdbc.OracleDriver");
        ds.setUrl("jdbc:oracle:thin:@localhost:1521:mydb");
        ds.setUsername("scott");
        ds.setPassword("tiger");

        MyStoredProcedure sproc = new MyStoredProcedure(ds);
        Map results = sproc.execute();
        printMap(results);
    }

    private class MyStoredProcedure extends StoredProcedure {

        private static final String SQL = "sysdate";

        public MyStoredProcedure(DataSource ds) {
            setDataSource(ds);
            setFunction(true);
            setSql(SQL);
            declareParameter(new SqlOutParameter("date", Types.DATE));
            compile();
        }

        public Map execute() {
            // the ’sysdate’ sproc has no input parameters, so an empty Map is supplied…
            return execute(new HashMap());
        }
    }

    private static void printMap(Map results) {
        for (Iterator it = results.entrySet().iterator(); it.hasNext(); ) {
            System.out.println(it.next());
        }
    }
}

This is as excerpted from Chapter 11 of the Spring Framework Reference Documentation.

I am positive that if I showed that to the client as an implementation they would ask for a framework to wrap it to hide the complexity and what is the point in that?

The same thing is accomplished just using JDBC as follows:

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.SQLException;

public class TestStoredProcedure {

    public static void main(String[] args){
        TestStoredProcedure t = new TestStoredProcedure ();
        t.test();
       System.out.println("Done!");
    }

    public void test() {
        Connection connection = getConnection();
        String myDate = null;
        try {
            myDate = test(connection);
        } catch (SQLException e) {
            //Might want to do something more useful here.
           e.printStackTrace();
        } finally {
            try {
                connection.close();
            } catch (SQLException e) {
                //don't care right now if we can't close it.
            }
        }
        System.out.println("Date:"+myDate);
    }

    public String test(Connection conn) throws SQLException {
        String result = null;
        CallableStatement proc = conn.prepareCall("BEGIN ? := sysdate(); End;");
        proc.registerOutParameter(1, java.sql.Types.DATE);
        proc.execute();
        result = proc.getDate(1).toString();
        proc.close();
        return result;
    }

    public Connection getConnection() {
        Connection conn = null;
        try{
            Class.forName("oracle.jdbc.OracleDriver");
            conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe","scott","tiger");
        } catch (ClassNotFoundException e1) {
            e1.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }

}

So what was gained with the framework? To me it doesn’t seem like much. I’m not sure where the exceptions went to, do I get a Runtime exception if sysdate isn’t a function? I guess it means the user doesn’t have to access the CallableStatement or ResultSet directly and that *may* be a good thing to some people. IMHO saving yourself from learning JDBC just means that you won’t know what the problem may be when things start to go wrong and you just limited your skillset to a framework that not everyone is going to use.

Many frameworks are useful and I realize this part of the Spring framework is one small piece that probably isn’t used by very many people, but again, that’s kind of my point. Why bother with that piece at all?

5 comments

  1. Thomas Risberg Apr 25

    I agree that the StoredProcedure example is a bit verbose, especially when creating a DataSource. This is typically injected into your class. Even so, using the framework gives you the dependency injection, transaction management and unit test goodies for free. The framework will also manage all resources, allow you to run in a JTA or local JDBC transaction environment without any code changes, so there are other benefits than fewer lines of code.

    If you look at the new SimpleJdbcCall you’ll see a simplified approach where parameters don’t need to be declared if the framework can look them up in the database metadata.

    Here is the SYSDATE example using SimpleJdbcCall:

    SimpleJdbcCall callSysdate =
    new SimpleJdbcCall(dataSource)
    .withFunctionName(”sysdate”)
    .withoutProcedureColumnMetaDataAccess()
    .declareParameters(new SqlOutParameter(”return”, Types.DATE));
    Date sysDate = callSysdate.executeFunction(Date.class, Collections.EMPTY_MAP);
    System.out.println(”–> ” + sysDate);

    This is assuming that you already have the DataSource available. Since SYSDATE is a built in function there is no metadata to look up so we need to turn off the metadata lookup and explicitly configure the out parameter.

    Thomas

  2. Robert Nocera Apr 26

    Thomas: I appreciate the comment. SimpleJdbcCall may indeed be easier to use, it looks that way from your example. Still, in my opinion, making a simple call like that is extremely simple in JDBC, so why complicate matters by introducing another framework? A lot of times the complexity isn’t relieved, it’s just shifted somewhere else.

    If I was working on a project that used Spring already, I’d probably look harder into using the Spring JDBC Template or SimpleJdbcCall, but if I’m not, JDBC itself is simple enough for me.

  3. Thomas Risberg Apr 26

    Robert,

    Introducing a framework does bring a bit of a learning curve and isn’t something you should do without analyzing the benefits. If you have an application with extremely simple data access requirements, then yes, using a framework might not pay off immediately. However, looking at it long term and if you develop a number of applications then the JDBC framework investment quickly pays off.

    It’s more than just the code to make the call. Any data access operation is part of an application and, except for an extremely simple application, you will at some point move beyond the autocommit mode. Now you need to manage your transactions. If you write code both for a standalone and a managed J2EE environment, the transaction management is very different and you can’t easily re-use code in the different environments. Using a framework like Spring gives you this transaction management on top of the JDBC framework and the transactions become transparent to your application.

    Another issue with straight JDBC code is the resource management and try/finally blocks that tend to obscure the actual data access logic. Moving these concerns to the framework not only prevents the JDBC programmer from messing this part up, it also makes your code more readable and maintainable.

    Thomas

  4. Robert Nocera Apr 26

    Thomas,

    Exactly. Like I said, if I was working on a project using Spring already, then I’d probably make use of the Spring JDBC template.

    Sometime I’ll research how the JDBC Template is handling exceptions, that is one thing that struck me when looking at the example code, there aren’t any being thrown. I would assume that they aren’t actually buried, but the if they aren’t then the exception handling being done inside that framework is a little less than obvious. Of course, I didn’t actually try the example code, so perhaps it is ignoring the exception handling.

    My intention with this post wasn’t so much to pick on Spring at all, it’s a decent framework for what it does, it was to point out that not all attempts to abstract complexity work, a lot of times they just move that complexity around.

  5. Thomas Risberg Apr 28

    I guess where we differ is on how soon investing in a data access framework pays off. I can’t think of many applications where I would want to be without one. In my opinion an investment in Spring and/or JPA, Hibernate or Ibatis quickly provides benefits.

    The exceptions get translated to a subclass of DataAccessException which is a runtime exception. This is true for both JDBC and all other data access technologies supported by Spring. That way your application don’t need to worry about all the different exception hierarchies.

    Thomas

Leave a reply

You must be logged in to post a comment.