// // A very good JAVA database connection pool. // from:http://www.jxer.com/home/?uid-195-action-viewspace-itemid-332 // Although it is very convenient to use APACHE COMMONS DBCP to establish a database connection pool, // But like this article, the internal principles of the database connection pool are written so thoroughly and the attention is so complete. // It's really rare, so that developers can have a deeper understanding of the database connection pool, it's really impressive // Thanks to the author of this article. // import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.Driver; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; import java.util.Enumeration; import java.util.Vector; public class ConnectionPool { private String jdbcDriver = ""; // database driver private String dbUrl = ""; // data URL private String dbUsername = ""; // database username private String dbPassword = ""; // database user password private String testTable = ""; // The name of the test table to test whether the connection is available, there is no test table by default private int initialConnections = 10; // initial size of the connection pool private int incrementalConnections = 5; // Automatically increase the size of the connection pool private int maxConnections = 50; // maximum size of connection pool private Vector connections = null; // The vector that stores the database connection in the connection pool, initially null // The object stored in it is of type PooledConnection public ConnectionPool(String jdbcDriver, String dbUrl, String dbUsername, String dbPassword) { this.jdbcDriver = jdbcDriver; this.dbUrl = dbUrl; this.dbUsername = dbUsername; this.dbPassword = dbPassword; } public int getInitialConnections() { return this.initialConnections; } public void setInitialConnections(int initialConnections) { this.initialConnections = initialConnections; } public int getIncrementalConnections() { return this.incrementalConnections; } public void setIncrementalConnections(int incrementalConnections) { this.incrementalConnections = incrementalConnections; } public int getMaxConnections() { return this.maxConnections; } public void setMaxConnections(int maxConnections) { this.maxConnections = maxConnections; } public String getTestTable() { return this.testTable; } public void setTestTable(String testTable) { this.testTable = testTable; } public synchronized void createPool() throws Exception { // Make sure the connection pool is not created // If the connection pool has been created, the vector connections that hold the connection will not be empty if (connections != null) { return; // if already created, return } // Instantiate the instance of the driver class specified in the JDBC Driver Driver driver = (Driver) (Class.forName(this.jdbcDriver).newInstance()); DriverManager.registerDriver(driver); // Register the JDBC driver // Create a vector holding connections, initially with 0 elements connections = new Vector(); // Create a connection based on the value set in initialConnections. createConnections(this.initialConnections); System.out.println("The database connection pool was created successfully! "); } private void createConnections(int numConnections) throws SQLException { // Loop to create the specified number of database connections for (int x = 0; x < numConnections; x++) { // Has the maximum number of database connections in the connection pool been reached? The maximum value is determined by the class member maxConnections // Point out that if maxConnections is 0 or negative, there is no limit to the number of connections. // If the number of connections has reached the maximum, exit. if (this.maxConnections > 0 && this.connections.size() >= this.maxConnections) { break; } //add a new PooledConnection object to connections vector // Add a connection to the connection pool (in the vector connections) try { connections.addElement(new PooledConnection(newConnection())); } catch (SQLException e) { System.out.println("Failed to create database connection! " + e.getMessage()); throw new SQLException(); } System.out.println("Database connection created..."); } } private Connection newConnection() throws SQLException { // create a database connection Connection conn = DriverManager.getConnection(dbUrl, dbUsername, dbPassword); // If this is the first time to create a database connection, check the database to get what the database promises to support // maximum number of client connections //connections.size()==0 means that no connection has been created if (connections.size() == 0) { DatabaseMetaData metaData = conn.getMetaData(); int driverMaxConnections = metaData.getMaxConnections(); // If the driverMaxConnections returned by the database is 0, it means that the database has no maximum // Connection limit, or the maximum connection limit of the database is not known //driverMaxConnections is an integer returned, indicating the number of client connections allowed by this database // If the maximum number of connections set in the connection pool is greater than the number of connections allowed by the database, set the maximum number of connections in the connection pool // The number of connections is the maximum number allowed by the database if (driverMaxConnections > 0 && this.maxConnections > driverMaxConnections) { this.maxConnections = driverMaxConnections; } } return conn; // return the new database connection created } public synchronized Connection getConnection() throws SQLException { // Make sure the connection pool has been created if (connections == null) { return null; // If the connection pool has not been created, return null } Connection conn = getFreeConnection(); // get a free database connection // If there are currently no available connections, all connections are in use while (conn == null) { // try again later wait(250); conn = getFreeConnection(); // retry until a free connection is obtained, if //getFreeConnection() returns null // Indicates that no available connections are available after a batch of connections is created } return conn; // return the available connection obtained } private Connection getFreeConnection() throws SQLException { // Get an available database connection from the connection pool Connection conn = findFreeConnection(); if (conn == null) { // If there is no connection available in the current connection pool // create some connections createConnections(incrementalConnections); // Re-check if there is an available connection from the pool conn = findFreeConnection(); if (conn == null) { // If no available connection is available after the connection is created, return null return null; } } return conn; } private Connection findFreeConnection() throws SQLException { Connection conn = null; PooledConnection pConn = null; // Get all objects in the connection pool vector Enumeration enumerate = connections.elements(); // loop through all objects to see if there is any connection available while (enumerate.hasMoreElements()) { pConn = (PooledConnection) enumerate.nextElement(); if (!pConn.isBusy()) { // If this object is not busy, get its database connection and make it busy conn = pConn.getConnection(); pConn.setBusy(true); // Test if this connection is available if (!testConnection(conn)) { // If this connection is no longer available, create a new connection, // and replace this unavailable connection object, if the creation fails, return null try { conn = newConnection(); } catch (SQLException e) { System.out.println("Failed to create database connection! " + e.getMessage()); return null; } pConn.setConnection(conn); } break; // found an available connection, exit } } return conn; // return the found available connection } private boolean testConnection(Connection conn) { try { // Determine if the test table exists if (testTable.equals("")) { // If the test table is empty, try to use the setAutoCommit() method of this connection // To determine whether the connection is available (this method is only available in some databases, if not available, // Throw an exception). Note: The method using the test table is more reliable conn.setAutoCommit(true); } else { // Use the test table to test when there is a test table //check if this connection is valid Statement stmt = conn.createStatement(); stmt.execute("select count(*) from " + testTable); } } catch (SQLException e) { // The above throws an exception, this connection is not available, close it, and return false; closeConnection(conn); return false; } // connection is available, return true return true; } public void returnConnection(Connection conn) { // Make sure the connection pool exists, if the connection is not created (doesn't exist), return directly if (connections == null) { System.out.println("The connection pool does not exist, cannot return this connection to the connection pool!"); return; } PooledConnection pConn = null; Enumeration enumerate = connections.elements(); // Traverse all connections in the connection pool to find the connection object to return while (enumerate.hasMoreElements()) { pConn = (PooledConnection) enumerate.nextElement(); // First find the connection object to be returned in the connection pool if (conn == pConn.getConnection()) { // found, set this connection to idle state pConn.setBusy(false); break; } } } public synchronized void refreshConnections() throws SQLException { // Make sure that the connection pool has been created if (connections == null) { System.out.println("The connection pool does not exist and cannot be refreshed!"); return; } PooledConnection pConn = null; Enumeration enumerate = connections.elements(); while (enumerate.hasMoreElements()) { // get a connection object pConn = (PooledConnection) enumerate.nextElement(); // If the object is busy, wait 5 seconds, and refresh directly after 5 seconds if (pConn.isBusy()) { wait(5000); // wait 5 seconds } // Close this connection and replace it with a new one. closeConnection(pConn.getConnection()); pConn.setConnection(newConnection()); pConn.setBusy(false); } } public synchronized void closeConnectionPool() throws SQLException { // Make sure the connection pool exists, if not, return if (connections == null) { System.out.println("The connection pool does not exist and cannot be closed!"); return; } PooledConnection pConn = null; Enumeration enumerate = connections.elements(); while (enumerate.hasMoreElements()) { pConn = (PooledConnection) enumerate.nextElement(); // if busy, wait 5 seconds if (pConn.isBusy()) { wait(5000); // wait 5 seconds } //Close it directly after 5 seconds closeConnection(pConn.getConnection()); // remove it from the connection pool vector connections.removeElement(pConn); } // set the connection pool to be empty connections = null; } private void closeConnection(Connection conn) { try { conn.close(); } catch (SQLException e) { System.out.println("Error closing database connection: " + e.getMessage()); } } private void wait(int mSeconds) { try { Thread.sleep(mSeconds); } catch (InterruptedException e) { } } class PooledConnection { Connection connection = null; // database connection boolean busy = false; // The flag of whether this connection is in use, the default is not in use // Constructor, according to a Connection to construct a PooledConnection object public PooledConnection(Connection connection) { this.connection = connection; } // return the connection in this object public Connection getConnection() { return connection; } // set this object's, connection public void setConnection(Connection connection) { this.connection = connection; } // Get whether the object connection is busy public boolean isBusy() { return busy; } // set the connection to the object is busy public void setBusy(boolean busy) { this.busy = busy; } } public static void main(String[] args) { ConnectionPool connPool = new ConnectionPool("oracle.jdbc.driver.OracleDriver", "jdbc:oracle:thin:@*.*.*.*" , "name", "password"); try { connPool.createPool(); } catch (Exception ex) { ex.printStackTrace(); } try { Connection conn = connPool.getConnection(); } catch (SQLException ex1) { ex1.printStackTrace(); } } }