Using dynamic proxies in Java to implement database connection pooling

Source: Internet
Author: User
Tags bind connection pooling documentation hash connect odbc
Dynamic | data | Database connection database connection pool in writing application services is often required to use the module, too frequent connection of the database is a bottleneck in terms of service performance, the use of buffer pool technology can be used to eliminate this bottleneck. We can find many sources of database connection pools on the Internet, but we all find this common problem: the implementation of these connection pools increases the degree of coupling with the user in varying degrees. Many of the connection pools require the user to obtain the database connection through its prescribed method, which we can understand, after all, the way that all the application servers take the database connection is realized in this way. Another common problem, however, is that they do not allow the user to explicitly invoke the Connection.close () method, but rather to close the connection using one of its specified methods. This approach has two disadvantages:

First: Changed the user usage habit, increased the user's use difficulty.

First, let's take a look at a normal database operation process:

int ExecuteSQL (String sql) throws SQLException
{
Connection conn = getconnection (); Get a database connection 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;
}

After using the database connection, the consumer typically calls the connection's method close directly to free the database resource, and if we use the connection pooling implementation method that we mentioned earlier, the statement conn.close () will be replaced by some specific statements.

Second: The connection pool cannot be exclusive control of all connections in. Because the connection pool does not allow the user to directly invoke the connected Close method, the connection pool will not maintain the state of all connections properly, and the problem is more likely to occur when the connection pool and application are implemented by different developers, as long as the database connection is closed directly by a custom problem during use.

Combining the two issues mentioned above, let's discuss how to solve these two deadly problems.

First of all, we put ourselves in the right place to consider how users would like to use this database connection pool. A user can obtain a connection to a database in a specific way, while the type of the connection should be the standard java.sql.Connection. The user can take any action on this connection after obtaining the connection to the database, including closing the connection, and so on.

By describing the user, how to take over the Connection.close method has become the subject of our article.

In order to take over the Close method of database connection, we should have a mechanism similar to hooks. For example, in Windows programming, we can use the hook API to implement a takeover of a Windows API. There is also a mechanism in Java. Java provides a proxy class and a invocationhandler, all two classes in the Java.lang.reflect package. Let's take a look at how the documentation provided by Sun Company describes these two classes.

public interface Invocationhandler

Invocationhandler is the interface implemented by the invocation handler of a proxy instance.

Each proxy instance has a associated invocation handler.
When a is invoked on a proxy instance,
The method invocation are encoded and dispatched to the Invoke method of it invocation handler.

There are a lot of descriptions of proxy in Sun's API documentation, not listed here. Through the description of the interface Invocationhandler by the document we can see that the Invoke method of the Invocationhanlder is triggered when a method that invokes a proxy instance is invoked. From the Java documentation We also learned that this dynamic proxy mechanism can only take over the interface method, but not the general class, considering that Java.sql.Connection itself is also an interface, thus finding a way to solve how to take over the Close method.

First, we define a class of database connection pool parameters, define the JDBC driver class name for the database, the URL of the connection, the username password, and so on, which are the parameters for initializing the connection pool, which are defined as follows:

public class Connectionparam implements Serializable
{
Private String driver; Database driver
Private String URL; The URL of the data connection
Private String user; Database user Name
private String password; Database Password
private int minconnection = 0; Initialize the number of connections
private int maxconnection = 50; Maximum number of connections
Private long TimeOutValue = maximum idle time for 600000;//connection
Private long waittime = 30000; When the connection is taken, the maximum wait time if no connection is available

Next is the connection pool's factory class ConnectionFactory, which corresponds to a connection pool object with a name that allows the user to get the specified connection pool object, with the following code:

/**
* Connect to the pool plant, which is commonly used to hold multiple data source names to match the hash of the database connection pool
* @author Liusoft
*/
public class ConnectionFactory
{
This hash table is used to hold relational tables for data source names and connection pool objects
static Hashtable connectionpools = null;
static{
Connectionpools = new Hashtable (2,0.75f);
}
/**
* Get the connection pool object corresponding to the specified name from the connection pool factory
* @param the name of the DataSource connection pool object
* @return DataSource Return the connection pool object corresponding to the name
* @throws Namenotfoundexception cannot find the specified connection pool
*/
public static DataSource lookup (String DataSource)
Throws Namenotfoundexception
{
Object ds = null;
ds = Connectionpools.get (DataSource);
if (ds = NULL | |!) ( DS instanceof DataSource))
throw new Namenotfoundexception (DataSource);
Return (DataSource) DS;
}

/**
* Binds the specified name and database connection configuration together and initializes the database connection pool
* @param name of the connection pool
* @param param connection Pool configuration parameters, see class Connectionparam
* @return DataSource Returns the connection pool object if the binding succeeds
* @throws namealreadyboundexception A certain name is already bound to throw the exception
* @throws ClassNotFoundException cannot find the driver class in the configuration of the connection pool
* @throws the driver class in the Illegalaccessexception connection pool configuration is incorrect
* @throws Instantiationexception cannot instantiate driver classes
* @throws SQLException cannot connect the specified database properly
*/
public static DataSource bind (String name, Connectionparam param)
Throws Namealreadyboundexception,classnotfoundexception,
Illegalaccessexception,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;
}
/**
* Rebind database Connection Pool
* @param name of the connection pool
* @param param connection Pool configuration parameters, see class Connectionparam
* @return DataSource Returns the connection pool object if the binding succeeds
* @throws namealreadyboundexception A certain name is already bound to throw the exception
* @throws ClassNotFoundException cannot find the driver class in the configuration of the connection pool
* @throws the driver class in the Illegalaccessexception connection pool configuration is incorrect
* @throws Instantiationexception cannot instantiate driver classes
* @throws SQLException cannot connect the specified database properly
*/
public static DataSource rebind (String name, Connectionparam param)
Throws Namealreadyboundexception,classnotfoundexception,
Illegalaccessexception,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 the user to bind the connection pool to a specific name and unbind the operation. Users only need to care about these two classes to use the functionality of the database connection pool. Here's how to use the Connection pool code:

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 code above is used to enlist a connection pool object, which can be done only once in the initialization of the program
The following starts with the code that the user really needs to write
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 you can see from the sample code for the consumer, we have solved two problems with the common connection pool. But what we are most concerned about is how to handle the close approach. Take over two lines of code that are mainly in ConnectionFactory:

Source = new Datasourceimpl (param);
Source.initconnection ();

Datasourceimpl is a class that implements the interface Javax.sql.DataSource, which maintains the object of a connection pool. Because the class is a protected class, the method it exposes to the consumer is only the method defined in the interface datasource, and all other methods are not considered by the user. Let's first care about a method that users can access getconnection

/**
* @see javax.sql.datasource#getconnection (string,string)
*/
Public Connection getconnection (string user, string password) throws SQLException
{
First, find the idle objects from the connection pool
Connection conn = getfreeconnection (0);
if (conn = = null) {
Determines whether the maximum number of connections is exceeded if the maximum number of connections is exceeded
Wait a certain amount of time to see if there is an idle connection, or throw an exception telling the user that no connection is available
if (Getconnectioncount () >= connparam.getmaxconnection ())
conn = Getfreeconnection (Connparam.getwaittime ());
else{//no more than the number of connections, retrieve a connection to a database
Connparam.setuser (user);
Connparam.setpassword (password);
Connection conn2 = drivermanager.getconnection (Connparam.geturl (),
user, password);
The connection object that the agent will return
_connection _conn = new _connection (conn2,true);
Synchronized (Conns) {
Conns.add (_conn);
}
conn = _conn.getconnection ();
}
}
Return conn;
}
/**
* Take an idle connection from the connection pool
* @param ntimeout If the value of this parameter is 0, then a null is returned only if there is no connection
* Otherwise, wait ntimeout milliseconds to see if there are any idle connections, if no exceptions are thrown
* @return Connection
* @throws SQLException
*/
Protected synchronized Connection getfreeconnection (long ntimeout)
Throws SQLException
{
Connection conn = null;
Iterator iter = Conns.iterator ();
while (Iter.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 an idle connection
try{
Thread.Sleep (ntimeout);
}catch (Exception e) {}
conn = getfreeconnection (0);
if (conn = null)
throw new SQLException ("No database connections available");
}
Return conn;
}

The logic of the Getconnection method in the Datasourceimpl class is the same as the normal database connection pool, which first determines whether there are idle connections, and if not, whether the number of connections has exceeded the maximum number of connections, and so on some logic. But one of the differences is that the database connections obtained through DriverManager are not returned in time, but are mediated by a class called _connection, and then invoked _connection.getconnection returns. If we do not take over the interface object to be returned through an intermediary, the proxy in Java, then we have no way to intercept the Connection.close method.

Finally to the core, let's take a look at how the _connection is implemented, and then introduce the client to call the Connection.close method is how a process, why not really close the connection.

/**
* Self-mounted data connection, shielding the Close method
* @author Liudong
*/
Class _connection implements Invocationhandler
{
Private final static String Close_method_name = "Close";
PRIVATE Connection conn = null;
Busy state of the database
Private Boolean inUse = false;
The time the user last accessed 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 () {
Returns the takeover class of the database connection conn to intercept the Close method
Connection conn2 = (Connection) proxy.newproxyinstance (
Conn.getclass (). getClassLoader (),
Conn.getclass (). Getinterfaces (), this);
return conn2;
}
/**
* This method really closes the connection of the database
* @throws SQLException
*/
void Close () throws sqlexception{
Because the CLASS attribute conn is a connection that has not been taken over, the connection is closed directly once the Close method is called
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, Method M, object[] args)
Throws Throwable
{
Object obj = null;
Determines whether the Close method is invoked, and if the Close method is called, the connection is set to a useless state
if (Close_method_name.equals (M.getname ()))
Setinuse (FALSE);
Else
obj = M.invoke (conn, args);
Set the last access time to clear the timeout connection in a timely manner
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 invokes the Close method of the resulting connection, the Java virtual opportunity calls the _ Connection.invoke method, which first determines whether the Close method is the method, and if not, transfers the code to the true conn of the connection object that is not taken over. Otherwise, it's just simple. Set the status of the connection to be available. You may have understood the whole process of taking over, but there is also a question: Is it possible that these established connections will never be able to really shut down? The answer is yes. Let's look at the Connectionfactory.unbind method, which first finds the connection pool object that corresponds to the name, and then closes all connections in the connection pool and deletes the connection pool. A close method is defined in the Datasourceimpl class to turn off all connections, and the detailed code is as follows:

/**
* Close all database connections in this connection pool
* @return Int returns the number of closed connections
* @throws SQLException
*/
public int Close () throws SQLException
{
int cc = 0;
SQLException EXCP = null;
Iterator iter = Conns.iterator ();
while (Iter.hasnext ()) {
try{
((_connection) Iter.next ()). Close ();
CC + +;
}catch (Exception e) {
if (e instanceof SQLException)
EXCP = (SQLException) e;
}
}
if (EXCP!= null)
Throw EXCP;
return cc;
}

This method one by one invokes the Close method for each object in the connection pool, which corresponds to the implementation of the closing in _connection, and when closing the database connection in the _connection definition, it is a direct call to the closed method of the object that has not been taken over. So the Close method really frees up database resources.

The above text only describes the interface method to take over, a practical connection pool module also needs to monitor the idle connection and release the connection in time.



Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.