JDBC code is one area of Java coding that leads to an amazing amount of repetitive code being written. On top of that, silly mistakes almost always plague JDBC code. Writing good JDBC code is not difficult yet can be quite painful.
Pro Jakarta Commons, by Harshad Oak
The DbUtils component is a nice, simple component that does nothing complex but just makes many JDBC tasks a shade easier for the developer. Although many persistence frameworks and packages are available these days that aim at making data persistence a little easier, JDBC still is very much bread and butter for most Java and Java 2 Enterprise Edition (J2EE) developers. So anything that makes working with JDBC easier is good news.
DbUtils is available as a free download. It does not depend on any other Commons component and only expects the following:
- Java Development Kit (JDK) 1.2 (or later)
- JDBC 2.0 (or later)
The DbUtils documentation isn’t the best around but is enough to get you going. In the next section, you'll see the most useful classes in DbUtils and some examples of their usage. You should be able to quite easily use the code and examples here and start using DbUtils on your project right away. I'll focus on two classes (org.apache.commons.dbutils.DbUtils and org.apache.commons.dbutils.QueryRunner) and one interface (org.apache.commons.dbutils.ResultSetHandler). Before I show you examples of their usage, we'll zoom in a little closer and check out what they offer.
DbUtils is a class that provides utility methods to do routine tasks such as closing connections and loading JDBC drivers. All the methods are static.
The important methods in this class are:
- close: The DbUtils class provides three overloaded close methods. These methods check if the parameter provided is null, and if it is not, they close a Connection, Statement, and ResultSet.
- closeQuietly: The closeQuietly method not only avoids closing if the Connection, Statement, or ResultSet is null but also hides any SQLException that is thrown in the process. This is useful if you do not intend to handle the exception. Of the overloaded closeQuietly methods, a particularly useful one is closeQuietly(Connection conn, Statement stmt, ResultSet rs) because in most cases Connection, Statement, and ResultSet are the three things you use and have to close in the final block. Using this method, your final block can have just this one method invocation.
- commitAndCloseQuietly(Connection conn): This method commits the Connection and then closes it without escalating any SQLException that might occur in the process of closing.
- loadDriver(String driverClassName): This method loads and registers the JDBC driver and returns true if it succeeds. Using this method, you do not need to handle a ClassNotFoundException. Using the loadDriver method, the code gets easier to understand, and you also get a nice Boolean return value that tells you whether the driver class was loaded.
As the name suggests, implementations of this interface handle a java.sql.ResultSet and can convert and manipulate data into any form you want that is useful for the application and easier to use. The component provides the ArrayHandler, ArrayListHandler, BeanHandler, BeanListHandler, MapHandler, MapListHandler, and ScalarHandler implementations.
The ResultSetHandler interface provides a single method: Object handle(java.sql.ResultSet rs). So any ResultSetHandler implementation takes a ResultSet as input, processes it, and returns an object. Because the return type is java.lang.Object, apart from not being able to return Java primitives, there is no restriction on what can be returned. If you find that none of the seven implementations provided serves your purpose, you can always write and use your own implementation.
This class simplifies executing SQL queries. It takes care of many of the mundane tasks involved and, in tandem with ResultSetHandler, can drastically cut down on the code that you need to write. The QueryRunner class provides two constructors. One is an empty constructor, and the other takes a javax.sql.DataSource as parameter. So, in cases where you do not provide a database connection as a parameter to a method, the DataSource provided to the constructor is used to fetch a new connection and proceed.
The important methods in this class are as follows:
- query(Connection conn, String sql, Object params, ResultSetHandler rsh): This method executes a select query where the values in the Object array are used as replacement parameters for the query. The method internally handles the creation and closure of a PreparedStatement and the ResultSet. The ResultSetHandler is responsible for converting the data from the ResultSet into an easier or application-specific format to use.
- query(String sql, Object params, ResultSetHandler rsh): This is almost the same as the first method; the only difference is that the connection is not provided to the method and is retrieved from the DataSource provided to the constructor or set using the setDataSource method.
- query(Connection conn, String sql, ResultSetHandler rsh): This executes a select query that expects no parameters.
- update(Connection conn, String sql, Object params): This method is used to execute an Insert, Update, or Delete statement. The Object array holds the replacement parameters for the statement.
Now look at an example where you'll fetch some data from a database. For the example, I'm using a MySQL database. You'll also need to download the MySQL JDBC driver. The MySQL database I'm using is running on localhost port 3306. The database name is test. The Student table you'll be using has the following structure:
In Listing A, you'll fetch data from the Student table and modify it to suit your needs. Although you're using JDBC, note that you hardly write any JDBC code. (You might have to change the username and password stated in the example based on your specific database configuration.)
This code takes the following steps:
- Load the JDBC Driver class and get a database connection using the DriverManager.
- Instantiate the QueryRunner class.
- Use the query method that takes the connection, SQL query, parameters, and the ResultSetHandler as input. You use an org.apache.commons.dbutils.handlers.MapListHandler. A MapListHandler takes a ResultSet and returns a java.util.List of java.util.Map instances. So every row in the ResultSet becomes a java.util.Map, and all these java.util.Map instances are held together in a java.util.List.
- Iterate through the List while picking values from each Map in the List.
- Use the QueryRunner to execute a method that takes no parameters. Here you use the BeanListHandler, which is a particularly useful ResultSetHandler because you can convert the ResultSet into a List of a specific bean. Here you specify the bean class to be StudentBean, as shown in Listing B.
- You iterate through the List of beans retrieved and pick values from each instance of StudentBean.
NOTE: In Listing B, the StudId in the StudentBean class had to be int because the type of the StudId column in the Student table is int. Adhering to this type matching is the only rule that needs to be followed.
Because in this case the properties of the StudentBean class and the fields of the table Student mapped perfectly, just specifying the StudentBean class as a parameter did the trick. The field values got inserted into properties with the same name as the field names. However, if you want more control over the creation of the bean, the BeanListHandler class provides a second constructor: BeanListHandler(java.lang.Class type, RowProcessor convert). Implementations of the RowProcessor interface convert rows in the ResultSet into objects. In the StudentBean case, the BasicRowProcessor implementation of RowProcessor was used and was capable of handling the task. However, you can write a new implementation and provide that to the BeanListHandler constructor.
The output upon executing the code is, of course, dependent on what data you have in the Student table. In my case, I got the following output:
Apart from the classes you have seen so far, some other classes you need to study are:
- org.apache.commons.dbutils.QueryLoader: QueryLoaderis a simple class that loads queries from a file into a Map. You then pick queries from the Map as and when required. Having queries in a file also makes changes easily possible without having to touch the code.
- org.apache.commons.dbutils.wrappers.SqlNullCheckedResultSet: This class can be useful to have a systematic way of tackling null values. Wrap a normal ResultSet with an instance of SqlNullCheckedResultSet, and then specify what should be done in case of null values.
- org.apache.commons.dbutils.wrappers.StringTrimmedResultSet: Wrap a ResultSet with StringTrimmedResultSet so you can trim all strings returned by the getString() and getObject() methods.
Although the DbUtils component is nice and small, it does pack quite a punch and is well worth adopting on all projects where you use JDBC.