The Dark Side of Frameworks April 25
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?