The database connection pool is written in the module that often needs to be used. Too much connection database is a bottleneck for service performance, using buffer pool technology to eliminate this bottleneck. We can find a lot of source programs on the database connection pool on the Internet, but all find such a common problem: the implementation methods of these connecting pools increase the coupling between the users varying degrees. Many connecting pools require users to obtain database connections through their methods, which we can understand that all applications are currently implemented in this way. But another common problem is that they do not allow users to explicitly call the connection.close () method, and you need to close the connection with one of its specified. This approach has two shortcomings:
First: Change the user usage habits, increasing the difficulty of users.
First let's take a look at a normal database operation process:
INT ExecuteSQL (String SQL) Throws Sqlexception
{
Connection conn = getConnection (); // Get database connections in some way
PreparedStatement PS = NULL;
INT RES = 0;
Try {
PS = conn.preparestatement (SQL);
RES = ps.executeUpdate ();
} finally {
Try {
ps.close ();
} catch (exception e) {}
Try {
CONN.CLOSE (); //
} catch (exception e) {}
}
Return res;
}
The user usually calls the connection method close to release the database resource after using the database connection. If the connection pool mentioned earlier, that statement is replaced by some specific statements. .
Second: Make the connection pool unable to control all the connections. Since the connection pool does not allow the user to directly call the connected Close method, once the user directly turns off the database connection due to habits, the connection pool will not maintain all connections, consider the connection pool and application by different development This problem is more prone to occur when implementing.
Comprehensively, the two issues mentioned above, let's discuss how to solve these two ordered problems.
First, let's consider how the user wants to use this database connection pool. The user can obtain the connection of the database through a specific method, and the type of this connection should be a standard java.sql.connection. The user can any operation on this connection after obtaining this database, including closing the connection, and the like.
Through the description of the user, how can I take over the connection.close method to become the subject of this article.
In order to take over the CLOSE method connected to the database, we should have a mechanism similar to the hook. For example, in Windows program we can use the Hook API to implement the takeover of a Windows API. There is also such a mechanism in Java. Java provides an Proxy class and an InvocationHandler, which are in the java.lang.reflect package. Let's first take a look at how the documentation provided by Sun describes these two classes.
Public Interface InvocationHandler
InvocationHandler Is The Interface Implement by The Invocation Handler of A Proxy Instance.
Each Proxy Instance Has An Associated Invocation Handler.
When a method is invoked on a proxy instance,
THE METHOD INVOCATION IS Encode and dispatch to the Invoke Method of Its Invocation Handler.Sun's API documentation About Proxy's description, here is not listed here. The description of the Interface INVOCATIONHANDLER is described by document We can see the Invoke method of the InvocationHanlder when an Proxy instance is called. From Java's document, we also learned that this dynamic proxy mechanism can only take over the interface, and the general class is invalid. Considering that java.sql.connection itself is also an interface to find out how to take over the Close method. Outline.
First, let's define a class of a database connection pool parameter, define some information of the database's JDBC driver class, the connection, and username password, etc., which is used to initialize the connection pool, the specific definition is as follows:
Public Class ConnectionParam IMPLEments Serializable
{
Private string driver; // Database driver
Private string url; // data connection URL
Private string user; // Database Username
Private string password; // Database password
Private int minConnection = 0; // Initialization connection
Private int maxConnection = 50; // maximum connection
PRIVATE long timeoutvalue = 600000; // The maximum idle time
Private long waittime = 30000; // If there is no connection to the maximum wait time when connecting
Secondly, the factory class CONNECTIACTORY connects the pool, and passes a connection pool object with a name by this class, and the user can obtain the specified connection pool object, the specific code is as follows:
/ **
* Connect the pool class, which is often used to save multiple data source names and have a hash corresponding to the pool.
* @Author Liusoft
* /
Public Class ConnectionFactory
{
/ / The hash table is used to save the data source name and the connection pool object.
Static hashtable connectionpools = null;
STATIC {
ConnectionPools = new hashtable (2,0.75f);
}
/ **
* Get the connection pool object corresponding to the specified name in the connection pool plant
* @Param DataSource Corresponding Name
* @Return DataSource Returns the corresponding connection pool object
* @Throws NameNotFoundException Unable to find the specified connection pool
* /
Public Static DataSource Lookup (String DataSource)
Throws NameNotFoundException
{
Object DS = NULL;
DS = connectionsPools.get (Datasource);
IF (DS == NULL ||! (DS InstanceOf Datasource)....................
Return (DataSource) DS;
}
/ **
* Configure the specified name and database connection to the database and initialize the database connection pool.
* @Param Name The name of the connection pool
* @Param Param connection pool configuration parameters, please see the CONNECTIONPARAM
* @Return DataSource Returns the connection pool object if the binding is successful
* @Throws NameAlReadyBoundException must have a name Name has been bound to throw this exception
* @Throws ClassNotFoundException Unable to find the driver class in the configuration of the connection pool
* @Throws IllegalaccessException connection pool configuration in the connection pool configuration is incorrect
* @Throws InstantiationException Uncologied driver class
* @Throws SQLException Unable to connect to the specified database normally
* /
Public Static DataSource Bind (String Name, ConnectionParam Param)
Throws nameAlReadyboundexception, ClassNotNotFoundexception,
IllegaCcessException, InstantiationException, SQLException
{
DataSourceImpl Source = NULL;
Try {
LOOKUP (Name);
Throw new nameAlReadyBoundException (name);
} catch (namenotfoundexception e) {
Source = New DataSourceImpl (Param);
Source.initConnection ();
ConnectionPools.put (name, source);
}
Return Source;
}
/ **
* Rebond database connection pool
* @Param Name The name of the connection pool
* @Param Param connection pool configuration parameters, please see the CONNECTIONPARAM
* @Return DataSource Returns the connection pool object if the binding is successful
* @Throws NameAlReadyBoundException must have a name Name has been bound to throw this exception
* @Throws ClassNotFoundException Unable to find the driver class in the configuration of the connection pool
* @Throws IllegalaccessException connection pool configuration in the connection pool configuration is incorrect
* @Throws InstantiationException Uncologied driver class
* @Throws SQLException Unable to connect to the specified database normally
* /
Public Static Datasource Rebind (String Name, ConnectionParam Param)
Throws NameAlReadyBoundexception, ClassNotFoundException, IllegaCcessException, InstantiationException, Sqlexception
{
Try {
UNBIND (NAME);
} catch (exception e) {}
Return Bind (Name, Param);
}
/ **
* Delete a database connection pool object
* @Param Name
* @throws namenotfoundexception
* /
Public static void unbind (String name) throws namenotfoundexception
{
DataSource DataSource = Lookup (name);
IF (DataSource InstanceOf DataSourceImpl) {
DataSourceImpl DSI = (DatasourceImpl) DataSource;
Try {
DSI.stop ();
DSI.Close ();
} catch (exception e) {
} finally {
DSI = NULL;
}
}
ConnectionPools.Remove (Name);
}
}
ConnectionFactory mainly provides users who will bind the connection pool to a specific name and cancel the binding operation. Users only need to care about these two classes to use the functionality of the database connection pool. Let's give a section how to use the connection pool:
String name = "pool";
String driver = "sun.jdbc.odbc.jdbcodbcdriver";
String Url = "JDBC: ODBC: Datasource";
ConnectionParam Param = New ConnectionParam (Driver, URL, NULL, NULL);
Param.SetminConnection (1);
Param.SetMaxConnection (5);
Param.SetTimeoutValue (20000);
ConnectionFactory.bind (Name, param);
System.out.println ("Bind Datasource OK.");
// The above code is used to register a connection pool object, which can only be done once at the initialization of the program.
// The following start is the code that the user really needs to be written.
DataSource DS = ConnectionFactory.lookup (Name);
Try {
For (int i = 0; i <10; i ) {
Connection conn = ds.getConnection ();
Try {
Testsql (CONN, SQL);
} finally {
Try {
CONN.CLOSE ();
} catch (exception e) {}
}
}
} catch (exception e) {
E.PrintStackTrace ();
} finally {
ConnectionFactory.unbind (Name);
System.out.println ("Unbind DataSource OK.");
System.exit (0);
}
As can be seen from the user's sample code, we have solved two problems generated by the conventional connection pool. But we are most concerned about how to solve the way to take over the Close method. The takeover work is mainly two-sentence code in ConnectionFactory:
Source = New DataSourceImpl (Param);
Source.initConnection ();
DataSourceImpl is a class that implements interface javax.sql.datasource, which maintains an object that connects to a pool. Since this class is a protected class, it is exposed to the user's approach to only the method defined in the interface DataSource, and all other methods are not visible to the user. Let's first care about the user accessible to getConnection / **
* @see javax.sql.datasource # getConnection (String, String)
* /
Public Connection GetConnection (String User, String Password) THROWS SQLEXCEPTION
{
// First, find an idle object from the connection pool
Connection conn = GetfreeConnection (0);
IF (conn == null) {
/ / Judgment Whether it exceeds the maximum number of connections, if the maximum number of connections is exceeded
// Wait for a certain time to see if there is a free connection, otherwise throw an exception tells the user no connection
IF (getConnectionCount ()> = connParam.getMaxConnection ())
Conn = getFreeConnection (connparam.getwaittime ());
ELSE {// No exceeding the number of connections, re-acquire a database connection
Connparam.Setuser (User);
Connparam.SetPassword (Password);
Connection conn2 = drivermanager.getConnection (connparam.geturl (),
User, Password);
/ / The agent will return the connection object
_CONNECTION _CONN = New _Connection (conn2, true);
SYNCHRONIZED (CONNS) {
Conn.add (_conn);
}
CONN = _CONN.GETCONNECTION ();
}
}
Return conn;
}
/ **
* Take an idle connection from the connection pool
* @Param ntimeout If this parameter value is 0, it is just returned when it is not connected.
* Otherwise, wait for NTIMEOUT in milliseconds whether there is idle connection, if there is no exception
* @Return Connection
* @Throws SQLException
* /
Protected Synchronized Connection GetFreeConnection (long ntimeout)
Throws SQLEXCEPTION
{
Connection conn = NULL;
Iterator it = connS.Itemrator ();
While (ore.hasnext ()) {
_Connection _Conn = (_Connection) iter.next ();
IF (! _ conn.isinuse ()) {
CONN = _CONN.GETCONNECTION ();
_Conn.setinuse (True);
Break;
}
}
IF (conn == null && ntimeout> 0) {
// Wait for NTIMEOUT milliseconds to see if there is a free connection
Try {
Thread.sleep (ntimeout);
} catch (exception e) {}
CONN = GetFreeConnection (0);
IF (conn == null)
Throw new SQLException ("Database connection available");
}
Return conn;
}
The logic of the GetConnection method in the DataSourceImpl class is consistent with the normal database connection pool. First, it is first determined whether there is an idle connection, and if not, it is not possible to determine whether the number of connections has exceeded the maximum number of connections, and the like. But a little difference is that the database connection obtained through DriverManager is not returned in time, but is returned by a class interveneer called _Connection, then call _connection.getConnection. If we don't pass a mediation, it is the proxy in Java to take over the interface object to return, then we have no way to intercept the connection.close method. Finally, let's take a look at how _Connection is implemented, and then the client calls the connect. Close method how to get a process, why not really shut down the connection.
/ **
* Self-encapsulation of data connection, blocking Close method
* @Author liudong
* /
Class _Connection Implements InvocationHandler
{
Private final static string close_method_name = "close";
PRIVATE CONNECTION CONN = NULL;
// Database busy state
PRIVATE BOOLEAN INUSE = FALSE;
// The user's last time access to the connection method
Private long lastaccesstime = system.currenttimemillis ();
_Connection (Connection Conn, Boolean Inuse) {
THIS.CONN = conn;
THIS.INUSE = INUSE;
}
/ **
* Returns the conn.
* @Return Connection
* /
Public connection getConnection () {
/ / Return to the database connection CONN's takeover class to intercept the Close method
Connection Conn2 = (Connection) proxy.newproxyinstance
CONN.GETCLASS (). getClassLoader (),
CONN.GETCLASS (). GetInterfaces (), THIS)
Return conn2;
}
/ **
* This method really turns off the connection of the database
* @Throws SQLException
* /
Void Close () throws sqlexception {
// Since the class attribute conn is not connected to the connection, once the Close method is called, the connection is turned directly.
CONN.CLOSE ();
}
/ **
* Returns the inuse.
* @Return Boolean
* /
Public boolean isinuse () {
Return inuse;
}
/ **
* @see java.lang.reflect.InvocationHandler # invoke (java.lang.Object, java.lang.reflect.Method, java.lang.object)
* /
Public Object Invoke (Object Proxy, M, Object [] ARGS
Throws throwable
{
Object obj = NULL;
/ / Judgment whether the method is called, if the CLOSE method is called, the connection is used to useless.
IF (close_method_name.equals (m.getname ())))
Setinuse (false);
Else
Obj = m.invoke (conn, args); // Set the last access time in order to clear the timeout connection
Lastaccesstime = system.currenttimemillis ();
Return Obj;
}
/ **
* Returns the lastaccesstime.
* @Return Long
* /
Public long getlastaccesstime () {
Return LastAccesstime;
}
/ **
* Sets the inuse.
* @Param inuse the inuse to set
* /
Public void setinuse (boolean inuse) {
THIS.INUSE = INUSE;
}
}
Once the user calls the resulting close method, since the user's connection object is a taking the tube, the Java virtual opportunity first calls the _connection.invoke method, first determined whether it is a Close method, if not The code is forwarded to the true connection object that is not taken. Otherwise, just simply set the status of the connection to available. At this point, you may understand the process of the entire takeover, but there is also a question: This is not these established connections, there is always no way to really turn off? The answer is ok. Let's take a look at the ConnectionFactory.unbind method, which first finds the corresponding connection pool object, then close all connections in the connection pool and delete the connection pool. A Close method is defined in the DataSourceImpl class to close all connections, and the detailed code is as follows:
/ **
* Close all database connections in the connection pool
* @Return Int returns the number of closed connections
* @Throws SQLException
* /
Public int close () THROWS SQLEXCEPTION
{
INT CC = 0;
SQLEXCEPTION EXCP = NULL;
Iterator it = connS.Itemrator ();
While (ore.hasnext ()) {
Try {
((_Connection) ore.next ()). Close ();
CC ;
} catch (exception e) {
IF (E InstanceOf Sqlexception)
EXCP = (SQLException) E;
}
}
IF (eXCP! = null)
Throw eXCP;
Return CC;
}
The method calls the close method of each object in the connection pool. This close method corresponds to the implementation of the _Connection, when the _connection definition is turned off, it is directly invoked the shutdown method without the takeover. Therefore, the Close method really releases the database resources.