Package safejdbc

This is the main package of the SafeJdbc library which provides a safe and simple object oriented wrapper around the JDBC interface.

See:
          Description

Interface Summary
ConnectionProvider A ConnectionProvider is used by the SQLExecuter class to obtain a connection.
FillingCommand This interface serves to set the parameter values of a PreparedStatement SQL string which is passed to the query or update method of SQL.
LogListener A LogListener is used by a LoggingWrapper.
ResultSetProcessor ResultSetProcessors are used to specify how a ResultSet should be handled.
SQL This is the central abstraction of the JDBC library, it provides methods to execute SQL statements.
TXCodeBlock All code in a TXCodeBlock is performed within a single transaction.
 

Class Summary
DataSourceAdapter This class is a Adapter for DataSources to provide the ConnectionProvider interface.
LoggingWrapper If you want to log your SQL-Statements you just have to wrap your ConnectionProvider with a LoggingWrapper.
ResultSetIterator An Implementation of the ResultSetIterator interface.
SimpleConnectionProvider This class provides JDBC connection via the JDBC 1 Driver.
SimpleConProvider Deprecated. Don't use this class anymore.
SQLExecuter This is probably the most important class within the SafeJDBC framework, because this class provides methods to actually access the database.
TxDataSourceAdapter Some J2EE environments (e.g.
VersionInfo  
 

Package safejdbc Description

This is the main package of the SafeJdbc library which provides a safe and simple object oriented wrapper around the JDBC interface. The library was designed and written with the primary goal of preventing resource leaks, and care was taken not to restrict the flexibility of JDBC usage.

The most important class is SQLExecuter. This class provides methods to acutally access the database. The usual control is however inverted: Instead of directly operating on a JDBC connection, clients pass operations as objects to the SQLExecuter, and the SQLExecuter executes them. This is done to ensure that all ressources are cleanly deallocated even if an exception is thrown.

Performing an UPDATE operation is simple; a SQL string is passed in as a parameter, and no additional code is needed. The following code snippet contains an example:

  sqlExecuter.update ("INSERT INTO customer (id, name) VALUES (1, 'John Smith')");

Executing a SELECT statement needs a little more effort because the ResultSet needs to be handled in some way. This is where the ResultSetProcessor interface comes in ( ResultSetIterator is a convenience implementation of this interface). Whenever a SELECT string is sent to the database, an implementation of this interface must be provided, and after the actual database operation is finished the ResultSet is passed to this implementation so that it can process the results. Anonymous local classes are very handy for this sort of implementation.

The following is an example of a SELECT statement which is executed using SQLExecuter:

  sqlExecuter.query ("SELECT name FROM customer WHERE id=1",
    new ResultSetIterator () {
      public void forEachRow (ResultSet rs) {
        System.out.println (rs.getString ("name"));
      }
    });

The update and query methods of SQLExecuter are each executed in their own database transaction. Several database operations can be grouped together in a bigger transaciton by calling the executeTX method of SQLExecuter. This method takes an implementation of the TXCodeBlock class as a parameter; the principle is the same as for ResultSetIterator: The library initializes the transaction, then calls the TXCodeBlock so that it can do its operations, and finally the library commits the transaction - or rolls it back if an exception occurred.

The following code snippet contains a simple example:

  sqlexecuter.executeTX (new TXCodeBlock() {
    public void doTransaction (SQL sql) throws SQLException {
      sql.update ("INSERT INTO customer (id, name) VALUES (1, 'Arno Haase')");
      sql.update ("INSERT INTO customer (id, name) VALUES (2, 'Jan Hermanns')");
    }
  });

In order to actually write an implementation of TXCodeBlock, it is necessary to understand the SQL interface - because an instance of SQL is passed to the doTransaction method of TXCodeBlock. The SQL interface contains all methods that actually access the database, and SQLExecuter implements this interface. But while SQLExecuter executes each database operation in its own transaction, the SQL instance which is passed to a TXCodeBlock performs all operations in a common transaction. Since in both cases the SQL interface is used, the method calls in a transactional context are the same as in a non-transactional context.

Sound complicated? Look at the examples provided in the javadoc for SQLExecuter.

Two essential interfaces remain to be explained. Firstly, there is the ConnectionProvider interface. This interface serves as an abstraction for the way a SQLExecuter gets a JDBC connection if it needs one. DriverManager, DataSource, proprietary connection pool - there are a lot of possibilities to get a connection, and it would be foolish to make the SQLExecuter explicitly rely on one or several of these. So the SQLExecuter expects an implementation of ConnectionProvider in its constructor and asks this whenever it needs a database connection.

Secondly, there is the FillingCommand interface. SQLExecuter expects implementations of this interface whenever a SQL operation is executed using a PreparedStatement. A SQL string for a PreparedStatement contains a number of '?' which represent parameters and need to be filled with actual values before the PreparedStatement can be sent to the database; this setting of parameters is what FillingCommands are there for.

In practice, that looks something like the following:

  sql.update ("INSERT INTO customer (id, name) VLAUES (?, ?)",
    new FillingCommand () {
      public void fill (PreparedStatement stmt) throws SQLException {
        stmt.setInt (1, 1);
        stmt.setString (2, "John Smith");
      }
    });

Then there are a couple of convenience implementations of the interfaces: DataSourceAdapter turns a DataSource into a ConnectionProvider, ResultSetIterator is a ResultSetProcessor which iterates over all rows of a ResultSet, and SimpleConnectionProvider is a very simple JDBC 1 ConnectionProvider without connection pooling or any other bells and whistles.

Finally, there is the safejdbc.holder package which contains a number of "holder" classes which help passing data into and especially out of anonymous local classes; for details see the package overview.



Copyright (c) 2001, 2002 by Jan Hermanns and Arno Haase. All Rights Reserved.