You can develop your own connection and statement pooling without requiring support for DataSources or relying on another product.
The pooling techniques are demonstrated on a small Java™ application, but are equally applicable to servlets or large n-tiered applications. This example is used to demonstrate the performance issues.
The demonstration application has two functions:
The complete code to a connection pooling application can be downloaded from
The example application does not perform well. Running 100 calls to the getValue method and 100 calls to the putValue method through this code took an average of 31.86 seconds on a standard workstation.
The problem is that there is too much database work for every request. That is, you get a connection, get a statement, process the statement, close the statement, and close the connection. Instead of discarding everything after each request, there must be a way to reuse portions of this process. Connection pooling is replacing the create connection code with code to obtain a connection from the pool, and then replacing the close connection code with code to return the connection to the pool for use.
The connection pool's constructor creates the connections and places them in the pool. The pool class has take and put methods for locating a connection to use and for returning the connection to the pool when done working with the connection. These methods are synchronized because the pool object is a shared resource, but you do not want multiple threads to simultaneously try to manipulate the pooled resources.
There is a change to the calling code for the getValue method. The putValue method is not shown, but the exact change is made to it and is available from IBM's Developer Kit for Java JDBC Web page. The instantiation of the connection pool object is also not shown. You can call the constructor and pass in the number of connection objects that you want in the pool. This step should be done when you start up the application.
Running the previous application (that is, having 100 getValue method and 100 putValue method requests) with these changes took an average of 13.43 seconds with the connection pooling code in place. The processing time for the workload is cut by more than half the original processing time without connection pooling.
When using connection pooling, time is wasted when creating and closing a statement when each statement is processed. This is another example of wasting an object that can be reused.
To reuse an object, you can use the prepared statement class. In most applications, the same SQL statements are reused with minor changes. For example, one iteration through an application might generate the following query:
SELECT * from employee where salary > 100000
The next iteration might generate the following query:
SELECT * from employee where salary > 50000
This is the same query, but it uses a different parameter. Both queries can be accomplished with the following query:
SELECT * from employee where salary > ?
You can then set the parameter marker (denoted by the question mark) to 100000 when processing the first query and 50000 when processing the second query. This enhances performance for three reasons beyond what the connection pool can offer:
The demonstration program can be changed to pool PreparedStatement objects instead of Connections. Changing the program allows you to reuse more object and improve performance. You can begin by writing the class that contains the objects to be pooled. This class must encapsulate the various resources to be used. For the connection pool example, the Connection was the only pooled resource, so there was no need for an encapsulating class. Each pooled object must contain a Connection and two PreparedStatements. You can then create a pool class that contains database access objects instead of connections.
Finally, the application must change to obtain a database access object and specify which resource from the object it wants to use. Other than specifying the specific resource, the application remains the same.
With this change, the same test run now takes an average of 0.83 seconds. This time is about 38 times faster than the original version of the program.
Performance improves through replication. If an item is not reused, then it is wasting resources to pool it.
Most applications contain critical sections of code. Typically, an application uses 80 to 90 percent of its processing time on only 10 to 20 percent of the code. If there are 10,000 SQL statements potentially used in an application, not all of them are pooled. The objective is to identify and pool the SQL statements that are used in the application's critical sections of code.
Creating objects in a Java implementation can carry a heavy cost. The pooling solution can be used with advantage. Objects used in the process are created at the beginning, before other users attempt to use the system. These objects are reused as often as required. Performance is excellent and it is possible to fine-tune the application over time to facilitate its use for greater numbers of users. As a result, more objects are pooled. Moreover, it permits more efficient multithreading of the application's database access to gain greater throughput.
Java (using JDBC) is based on dynamic SQL and tends to be slow. Pooling can minimize this problem. By preparing the statements at startup, access to the database can be rendered static. There is little difference in performance between dynamic and static SQL after the statement is prepared.
The performance of database access in Java can be efficient and can be accomplished without sacrificing object-oriented design or code maintainability. Writing code to build statement and connection pooling is not difficult. Furthermore, the code can be changed and enhanced to support multiple applications and application types (Web-based, client/server) and so on.