Create your own type 3 JDBC driver, Part 1

Connect your Java applications to databases—the easy way

To access a database management system (DBMS) in Java, you need a JDBC (Java Database Connectivity) driver. You may write such drivers, which range from types 1 to 4, in pure Java or a combination of Java and Java Native Interface (JNI) methods. The industry trend is towards the more robust types 3 and 4 pure-Java drivers. Type 3 drivers shine when supporting Internet deployment in environments that connect to a variety of DBMS servers requiring numerous concurrently connected users where performance and scalability are major concerns. Therefore, to develop a high-performance, Internet-deployable application, you'll often find it useful to convert your existing type 1 or 2 drivers to type 3 drivers.

In this three-part series, we first introduce our own type 3 JDBC driver's architecture and design (Part 1), then show how to implement and deploy the driver (Part 2), and finish by explaining how you can add advanced features to the driver, like SQL logging or connection pooling (Part 3).

Note: Before you read this article, you may wish to read Nitin Nanda's "JDBC Drivers in the Wild" (JavaWorld, July 2000) to better understand JDBC drivers.

The JDBC driver architecture

JDBC provides a programming-level interface for uniformly communicating with databases. To use the JDBC API with a particular DBMS, you need a JDBC driver to mediate between JDBC technology and the database. JDBC drivers divide into four types or levels. Each type defines a JDBC driver implementation with increasingly higher levels of platform independence, performance, and deployment administration. The four types are:

  • Type 1: JDBC-ODBC (Open Database Connectivity) Bridge
  • Type 2: Native-API, partly Java driver
  • Type 3: Network-protocol, all-Java driver
  • Type 4: Native-protocol, all-Java driver

All JDBC drivers implement the four important JDBC classes: Driver, Connection, Statement, and ResultSet. The DriverManager class included with the java.sql package tracks the loaded JDBC drivers. The client application retrieves the desired database connections through the DriverManager class. The JDBC Driver class loads whenever a call comes to the driver:


The specified JDBC driver's static code block runs during the JDBC driver class's loading, which registers the driver with the DriverManager. Now, whenever a client program retrieves a database connection with the DriverManager.getConnection() method, the DriverManager in turn calls the Driver.connect() method. Every JDBC driver must implement the java.sql.Driver interface. So, the JDBC driver's connect() method checks whether the driver URL is correct, then returns the Connection within its connect() method.

Sample type 3 driver's architecture

To show you a type 3 driver's inner workings, we've constructed our own type 3 JDBC driver for this series. As Figure 1 shows, our JDBC type 3 driver—the network-protocol/all-Java driver—follows a three-tiered approach, whereby the JDBC database requests pass through the network to the middle-tier server. The middle-tier server then translates the requests (directly or indirectly) to the database-specific native-connectivity interface to further the request to the database server. The middle-tier server, written in Java, accesses the database server with a type 1 JDBC-ODBC Bridge driver.

Figure 1. JDBC type 3 driver architecture. Click on thumbnail to view full-size image.

For applets, the client-tier driver files reside in the middle-tier server and download along with the applet. In the JDBC driver, RMI (Remote Method Invocation) serves as the net protocol for communicating between the driver's client and server tiers. The JDBC-ODBC Bridge lets the driver middle tier translate the JDBC requests to the database server.

The driver's client tier, which provides the standard JDBC interface to the client programs, consists of a Driver class that implements the java.sql.Driver interface. It also consists of the implementations of JDBC's Connection, Statement and ResultSet interfaces.

Client programs, applets for example, are developed using the type 3 driver's client-tier class. Since these Driver client classes implement the JDBC interface, client programs receive the standard JDBC functionality. The Driver client-tier classes internally maintain the references of the corresponding remote interfaces exposed by the middle tier. Such remote interfaces include basic methods the client-tier classes use to process the JDBC requests from the programs. The application program calls JDBC methods in the client-tier objects implemented by the Driver. Those calls then become delegated to the middle tier using remote interface methods via RMI. Therefore, the Driver client classes manage the internal RMI communications with the middle tier.

The driver's server tier, an RMI server, uses the JDBC-ODBC Bridge—a type 1 driver—to finally communicate with the database. The driver's server tier includes the four remote interfaces and their implementations. The remote interfaces provide interface to the JDBC Driver, Connection, Statement, and ResultSet, respectively. The classes that implement the remote interfaces internally maintain the JDBC-ODBC Bridge driver's Connection, Statement, and ResultSet objects. When the client-tier class forwards a call to a remote interface, that interface's remote implementation uses the contained JDBC object to interact with the database.

Now that you understand our type 3 JDBC driver's architecture, let's examine its client- and server-tier classes.

JDBC driver class diagrams

To implement a type 3 JDBC driver, you must create both the driver's client and middle tiers. The client tier's classes reside in the com.jw.client package, while the middle tier's classes reside in the com.jw.server package.

Let's first examine the client tier.

Client-tier classes

The client-tier package, com.jw.client, features the following classes:

  • com.jw.client.JWDriver: The JDBC Driver implementation class
  • com.jw.client.JWConnection: The JDBC Connection implementation class
  • com.jw.client.JWStatement: The JDBC Statement implementation class
  • com.jw.client.JWResultSet: The JDBC ResultSet implementation class

Figure 2 shows the class diagram depicting the relationship between the Driver and Connection classes on the client and middle tiers.

Figure 2. The Driver and Connection class diagrams. Click on thumbnail to view full-size image.

Let's examine the JWDriver class in more detail.

The JWDriver class

The com.jw.client.JWDriver class implements the java.sql.Driver interface, which provides methods to register itself with the DriverManager and create new database connections. The class acts as a wrapper over the remote Driver to provide the JDBC driver interface. The JWDriver class loads whenever it's called in the program:


In the code above, the forName() method call invokes the JWDriver static clause, which registers itself with the DriverManager. Here is the code for the static clause:

                                       // Register the JWDriver with DriverManager
                                       JWDriver driverInst = new JWDriver();
                                       System.setSecurityManager(new RMISecurityManager());

The JWDriver class also maintains a reference to the remote driver, com.jw.server.IRemoteDriver, located on the middle-tier server. The remote driver reference creates the database connections for the client driver JWDriver class. So, when the calling program needs a database connection, it calls the DriverManager.getConnection() method. The DriverManager's JWDriver.connect() method gets invoked, which in turn uses the remote driver's reference to get the database connection.

In short, the JWDriver.connect() method:

  • Compares the driver's URL with the URL the client program passes and returns null if it is the wrong kind of driver to connect to.
  • If no remote driver reference exists, it creates such a reference using the Naming.lookup() method. JWDriver.connect() maintains the remote driver reference to create the database connections remotely:

    if(remoteDriver == null)
                                           remoteDriver= RemoteDriver)Naming.lookup("rmi://"+serverName
  • Creates, using the remote driver created above, a database connection and returns it to the calling program. The function call remoteDriver.getConnection() is a remote call to the JDBC driver's middle tier. The remote Connection retrieved from the middle tier is stored as a reference in the JWConnection class object. The JWConnection class's object then returns to the calling program:

                                           IRemoteConnection remoteConInstance = 
                                           localConInstance = new JWConnection(remoteConInstance);
                                           return (Connection)localConInstance;

Next, let's examine the JWConnection class.

JWConnection class

The com.jw.client.JWConnection class implements the JDBC Connection interface and contains the reference to the remote server IRemoteConnection interface.

The JWDriver.connect() method, which returns the JWConnection object reference to the client program, creates the JWConnection object. The client then calls any JDBC Connection interface method on the returned JWConnection object reference. The JWConnection object internally delegates the call to the remote server Connection for further action. For example, when the client calls conn.createStatement() (where conn is a reference of the JWConnection object), it internally calls RemoteConnection.createStatement(), which returns a remote Statement reference. The JWConnection's createStatement() method then creates a JWStatement object. Finally, a JWStatement object reference returns to the client program, which contains the remote Statement reference:

public Statement createStatement()  throws SQLException
                                       IRemoteStatement remStmt = 
                                       (IRemoteStatement) remoteConnection.createStatement();
                                       JWStatement localStmtInstance = new JWStatement(remStmt);
                                       return (Statement)localStmtInstance;

Figure 3 shows the class diagrams depicting the relationship between the Connection and Statement classes on the client and middle tiers.

Figure 3. Class diagrams for Connection and Statement classes. Click on thumbnail to view full-size image.

Next, we examine the JWStatement class in more detail.

The JWStatement class

The com.jw.client.JWStatement class, which implements the JDBC Statement interface, references the remote server Statement interface. The class acts as a wrapper on the remote Statement stub to provide the JDBC Statement interface. The JWConnection.createStatement() method, which returns the JWStatement object reference to the client program, creates JWStatement. The client then calls any method provided by the JDBC Statement interface on the returned JWStatement object reference. The JWStatement object delegates the call to the remote server Statement for processing. For example, when the client calls stmt.executeQuery() (where stmt is a reference of the JWStatement object), the executeQuery() method internally calls RemoteStatement.executeQuery(), which returns a remote ResultSet reference. Finally, a JWResultSet object reference returns to the client program, which contains the remote ResultSet reference:

1 2 3 Page 1
Page 1 of 3