SoFunction
Updated on 2025-04-07

Use connection pools to improve the efficiency of servlet accessing databases (2)

3. Description of DBConnectionPool

This class is implemented on lines 209 to 345, which represents a connection pool pointing to a certain database. The database is identified by a JDBC URL. A JDBC URL consists of three parts: protocol identifier (always jdbc), driver identifier (such as odbc, idb, oracle, etc.), and database identifier (the format depends on the driver). For example, jdbc:odbc:de
mo is a JDBC URL pointing to the demo database, and you need to use the JDBC-ODBC driver to access the database. Each connection pool has a name for the client program and optional user account, password, and maximum number of connections. If certain database operations supported by a web application can be performed by all users, while others should be performed by specially licensed users, you can define a connection pool for the two types of operations, using the same JDBC URL but using different accounts and passwords.
The constructor of the DBConnectionPool class requires all the above data as its parameters. As shown in lines 222 to 238, these data are saved as its instance variables:
As shown in lines 252 to 283 and lines 285 to 305, the client program can use the two methods provided by the DBConnectionPool class to obtain available connections. What's common to the two is that if there is an available connection in the connection pool, it will be returned directly, otherwise a new connection will be created and returned. If there are no connections available and the total number of connections is equal to the maximum limit
, the first method will return null directly, while the second method will wait until a connection is available.
All available connection objects are registered in a vector (Vector) named freeConnections. If there are more than one connection in the vector, getConnection() always selects the first one. Meanwhile, since new available connections are always added to vectors from the tail, the risk of database connections being closed due to long periods of idleness is minimized.
The first getConnection() method is called to verify that the connection is still valid before returning the available connection to the client program. If the connection is closed or an exception is triggered, getConnection() calls itself recursively to try to get another available connection. If there is no available connection in the vector freeConnections
Connection, getConnection() method checks whether the maximum number of connections has been specified. If specified, check whether the current number of connections has reached the limit. Here maxConn is 0, which means there is no limit. If no maximum number of connections is specified or the current number of connections is less than this value, the method attempts to create a new connection. If created successfully, increase the count of used connections and return, otherwise return a null value.
As shown in lines 325 to 345, creating a new connection is implemented by the newConnection() method. The creation process is related to whether the database account and password have been specified.
JDBC's DriverManager class provides multiple getConnection() methods, which use JDBC URL and other parameters, such as user account and password.
DriverManager will use the specified JDBC URL to determine the driver suitable for the target database and establish a connection.
The second getConnection() method implemented on lines 285 to 305 requires a time parameter in milliseconds, which represents the maximum time the client can wait. The specific operation of establishing a connection is still implemented by the first getConnection() method.
When this method is executed, startTime is initialized to the current time. Try to get a connection in a while loop. If it fails, wait() is called with the given time value as the parameter. The return of wait() may be due to calls to notify() or notifyAll() by other threads, or it may be due to the scheduled time. To find out the real reason why wait() returns, the program uses the current time to minus the startTime. If the difference is greater than the predetermined time, it returns a null value. Otherwise, getConnection() is called again.
Registering an idle connection to the connection pool is implemented by the freeConnection() method of lines 240 to 250, and its parameters are the connection object returned to the connection pool. The object is added to the end of the freeConnections vector and then decreases the used connection count. NotifyAll() is called to notify other threads waiting for available connections.
Many Servlet engines provide multiple methods for implementing secure shutdown. The database connection pool needs to know this event to ensure that all connections can be closed normally. The DBConnectionManager class negatively coordinates the closing process, but the task of closing all connections in the connection pool is the responsibility of the DBConnectionPool class. The release() method implemented in lines 307 to 323 is for DBConnectionManager to call. This method traverses the freeConnections vector and closes all connections, then removes those connections from the vector.


IV. Description of DBConnectionManager

This class can only create one instance, and other objects can call their static methods (also known as class methods) to get a reference to that unique instance. As shown in lines 031 to 036, the constructor of the DBConnectionManager class is private, which is to avoid other objects creating instances of the class.
The client program of the DBConnectionManager class can call the getInstance() method to obtain a reference to the unique instance of the class. As shown in lines 018 to 029, a unique instance of the class was created during the first call of the getInstance() method, and its reference has been kept in the static variable instance since then. GetInstance() is called each time
Both increase the client count of a DBConnectionManager. That is, this count represents the total number of clients referencing the unique instance of DBConnectionManager, which will be used to control the shutdown operation of the connection pool.
Initialization of this class instance is done by the private method init() between lines 146 and 168. where getResourceAsStream() method is used to locate and open external files. The location method of external files depends on the implementation of the class loader. Standard local class loader search operations always start with the path where the class file is located, and can also search for paths declared in CLASSPATH. is a property file that contains key-value pairs that define the connection pool. Common properties available for definition are as follows:

drivers space-separated list of JDBC driver classes
The absolute path to the logfile log file

Other properties are related to specific connection pools, and the connection pool name should be preceded by their attribute names:

<poolname>.url database's JDBC URL
<poolname>.maxconn The maximum number of connections allowed, 0 means there is no limit
<poolname>.user The database account used for this connection pool
<poolname>.password The corresponding password

Where the url attribute is required, while other attributes are optional. The database account and password must be legal. Examples of files for Windows platform
as follows:

drivers=
logfile=D:\\user\\src\\java\\DBConnectionManager\\

=jdbc:idb:c:\\local\\javawebserver1.1\\db\\
=2

=jdbc:odbc:demo
=demo
=demopw

Note that you must enter 2 backslashes in the Windows path, because the backslashes in the properties file are also an escape character.
After the init() method creates the attribute object and reads the file, it starts checking the logfile attribute. If no log file is specified in the properties file, the default is the file in the current directory. If the log file cannot be used, log records are recorded to the output.
Loading and registering all JDBC drivers specified in the drivers property are implemented by the loadDrivers() method between lines 170 and 192. This method first uses StringTokenizer to split the drivers attribute value into strings corresponding to the driver name, then load these classes in turn and create their instances, and finally register in DriverManager
This instance and add it to a private vector drivers. Vector drivers will be used to cancel all JDBC drivers from DriverManager when the service is shut down.
The last task of the init() method is to call the private method createPools() to create a connection pool object. As shown in lines 109 to 142, the createPools() method first creates an enumeration object with all attribute names (i.e., the Enumeration object. This object can be imagined as an element series. The nextElement() method is called successively to return the order.
back to each element), and search for the attribute whose name ends with ".url". For each attribute that meets the criteria, the connection pool name part is extracted first, and then all attributes belonging to the connection pool are read, and finally create a connection pool object and save it in the instance variable pools. Hashtable class) pools implement connection
The mapping between the pool name and the connection pool object, where the connection pool name is the key and the connection pool object is the value.
To facilitate the client to obtain available connections from a specified connection pool or return connections to the connection pool, the DBConnectionManager class provides methods getConnection() and freeConnection(). All these methods require that the connection pool name be specified in the parameters, and the specific connection acquisition or return operation is called to the corresponding connection pool object to complete. Their implementations are in lines 051 to 064, lines 066 to 080, and lines 038 to 049, respectively.
As shown in lines 082 to 107, in order to achieve secure shutdown of the connection pool, DBConnectionManager provides the method release(). As we have mentioned above, all DBConnectionManager clients should call the static method getInstance() to get a reference to the manager, this call will increase the client count.
The client program calls release() when it is closed to decrement the count. When the last client program calls release() and the decremented reference count is 0, you can call the release() method of each connection pool to close all connections. The last task of the management class release() method is to revoke all JDBC driver registration.


5. Example of servlet using connection pool

The Servlet lifecycle class defined by the Servlet API is such as:

1) Create and initialize the Servlet (init() method).
2) Respond to the client program's service request (service() method).
3) The servlet terminates and releases all resources (destroy() method).

This example demonstrates the connection pooling application. The relevant operations in the above key steps are:

1) In init(), use the instance variable connMgr to save the reference returned by the call ().
2) In service(), call getConnection(), perform database operations, and use freeConnection() to return the connection to the connection pool.
3) In destroy(), call release() to close all connections and release all resources.

The list of example programs is as follows:

import .*;
import .*;
import .*;
import .*;
public class TestServlet extends HttpServlet {
private DBConnectionManager connMgr;

public void init(ServletConfig conf) throws ServletException {
(conf);
connMgr = ();
}

public void service(HttpServletRequest req, HttpServletResponse res)
throws IOException {

("text/html");
PrintWriter out = ();
Connection con = ("idb");
if (con == null) {
("Cannot get database connection.");
return;
}
ResultSet rs = null;
ResultSetMetaData md = null;
Statement stmt = null;
try {
stmt = ();
rs = ("SELECT * FROM EMPLOYEE");
md = ();
("<H1>Employee Data</H1>");
while (()) {
("<BR>");
for (int i = 1; i < (); i++) {
((i) + ", ");
}
}
();
();
}
catch (SQLException e) {
(out);
}
("idb", con);
}

public void destroy() {
();
();
}
}