站内搜索: 请输入搜索关键词

当前页面: 开发资料首页Java 专题Java 的JDBC 数据库连接池实现方法

Java 的JDBC 数据库连接池实现方法

摘要: Java 的JDBC 数据库连接池实现方法
内容: Java 的JDBC 数据库连接池实现方法




Java 的JDBC 数据库连接池实现方法

Java, JDBC, Connection Pool, Database, 数据库连接池, sourcecode

虽然 J2EE 程序员一般都有现成的应用服务器所带的JDBC 数据库连接池,不过对于开发一般的 Java Application 、 Applet 或者 JSP、velocity 时,我们可用的JDBC 数据库连接池并不多,并且一般性能都不好。 Java 程序员都很羡慕 Windows ADO ,只需要 new Connection 就可以直接从数据库连接池中返回 Connection。并且 ADO Connection 是线程安全的,多个线程可以共用一个 Connection, 所以 ASP 程序一般都把 getConnection 放在 Global.asa 文件中,在 IIS 启动时建立数据库连接。ADO 的 Connection 和 Result 都有很好的缓冲,并且很容易使用。

其实我们可以自己写一个JDBC 数据库连接池。写 JDBC connection pool 的注意事项有:

1. 有一个简单的函数从连接池中得到一个 Connection。
2. close 函数必须将 connection 放回 数据库连接池。
3. 当数据库连接池中没有空闲的 connection, 数据库连接池必须能够自动增加 connection 个数。
4. 当数据库连接池中的 connection 个数在某一个特别的时间变得很大,但是以后很长时间只用其中一小部分,应该可以自动将多余的 connection 关闭掉。
5. 如果可能,应该提供debug 信息报告没有关闭的 new Connection 。

如果要 new Connection 就可以直接从数据库连接池中返回 Connection, 可以这样写( Mediator pattern ) (以下代码中使用了中文全角空格):

public class EasyConnection implements java.sql.Connection{
  private Connection m_delegate = null;

  public EasyConnection(){
    m_delegate = getConnectionFromPool();
  }

  public void close(){
    putConnectionBackToPool(m_delegate);
  }

  public PreparedStatement prepareStatement(String sql) throws SQLException{
    m_delegate.prepareStatement(sql);
  }

  //...... other method

}


看来并不难。不过不建议这种写法,因为应该尽量避免使用 Java Interface, 关于 Java Interface 的缺点我另外再写文章讨论。大家关注的是 Connection Pool 的实现方法。下面给出一种实现方法。

import java.sql.*;
import java.lang.reflect.*;
import java.util.*;
import java.io.*;

public class SimpleConnetionPool {
  private static LinkedList m_notUsedConnection = new LinkedList();
  private static HashSet m_usedUsedConnection = new HashSet();
  private static String m_url = "";
  private static String m_user = "";
  private static String m_password = "";
  static final boolean DEBUG = true;
  static private long m_lastClearClosedConnection = System.currentTimeMillis();
  public static long CHECK_CLOSED_CONNECTION_TIME = 4 * 60 * 60 * 1000; //4 hours

  static {
    initDriver();
  }

  private SimpleConnetionPool() {
  }

  private static void initDriver() {
    Driver driver = null;
    //load mysql driver
    try {
      driver = (Driver) Class.forName("com.mysql.jdbc.Driver").newInstance();
      installDriver(driver);
    } catch (Exception e) {
    }

    //load postgresql driver
    try {
      driver = (Driver) Class.forName("org.postgresql.Driver").newInstance();
      installDriver(driver);
    } catch (Exception e) {
    }
  }

  public static void installDriver(Driver driver) {
    try {
      DriverManager.registerDriver(driver);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }


  public static synchronized Connection getConnection() {
    clearClosedConnection();
    while (m_notUsedConnection.size() > 0) {
      try {
        ConnectionWrapper wrapper = (ConnectionWrapper) m_notUsedConnection.removeFirst();
        if (wrapper.connection.isClosed()) {
          continue;
        }
        m_usedUsedConnection.add(wrapper);
        if (DEBUG) {
          wrapper.debugInfo = new Throwable("Connection initial statement");
        }
        return wrapper.connection;
      } catch (Exception e) {
      }
    }
    int newCount = getIncreasingConnectionCount();
    LinkedList list = new LinkedList();
    ConnectionWrapper wrapper = null;
    for (int i = 0; i < newCount; i++) {
      wrapper = getNewConnection();
      if (wrapper != null) {
        list.add(wrapper);
      }
    }
    if (list.size() == 0) {
      return null;
    }
    wrapper = (ConnectionWrapper) list.removeFirst();
    m_usedUsedConnection.add(wrapper);

    m_notUsedConnection.addAll(list);
    list.clear();

    return wrapper.connection;
  }

  private static ConnectionWrapper getNewConnection() {
    try {
      Connection con = DriverManager.getConnection(m_url, m_user, m_password);
      ConnectionWrapper wrapper = new ConnectionWrapper(con);
      return wrapper;
    } catch (Exception e) {
      e.printStackTrace();
    }
    return null;
  }

  static synchronized void pushConnectionBackToPool(ConnectionWrapper con) {
    boolean exist = m_usedUsedConnection.remove(con);
    if (exist) {
      m_notUsedConnection.addLast(con);
    }
  }

  public static int close() {
    int count = 0;

    Iterator iterator = m_notUsedConnection.iterator();
    while (iterator.hasNext()) {
      try {
        ( (ConnectionWrapper) iterator.next()).close();
        count++;
      } catch (Exception e) {
      }
    }
    m_notUsedConnection.clear();

    iterator = m_usedUsedConnection.iterator();
    while (iterator.hasNext()) {
      try {
        ConnectionWrapper wrapper = (ConnectionWrapper) iterator.next();
        wrapper.close();
        if (DEBUG) {
          wrapper.debugInfo.printStackTrace();
        }
        count++;
      } catch (Exception e) {
      }
    }
    m_usedUsedConnection.clear();

    return count;
  }

  private static void clearClosedConnection() {
    long time = System.currentTimeMillis();
    //sometimes user change system time,just return
    if (time < m_lastClearClosedConnection) {
      time = m_lastClearClosedConnection;
      return;
    }
    //no need check very often
    if (time - m_lastClearClosedConnection < CHECK_CLOSED_CONNECTION_TIME) {
      return;
    }
    m_lastClearClosedConnection = time;

    //begin check
    Iterator iterator = m_notUsedConnection.iterator();
    while (iterator.hasNext()) {
      ConnectionWrapper wrapper = (ConnectionWrapper) iterator.next();
      try {
        if (wrapper.connection.isClosed()) {
          iterator.remove();
        }
      } catch (Exception e) {
        iterator.remove();
        if (DEBUG) {
          System.out.println("connection is closed, this connection initial StackTrace");
          wrapper.debugInfo.printStackTrace();
        }
      }
    }

    //make connection pool size smaller if too big
    int decrease = getDecreasingConnectionCount();
    if (m_notUsedConnection.size() < decrease) {
      return;
    }

    while (decrease-- > 0) {
      ConnectionWrapper wrapper = (ConnectionWrapper) m_notUsedConnection.removeFirst();
      try {
        wrapper.connection.close();
      } catch (Exception e) {
      }
    }
  }

  /**
   * get increasing connection count, not just add 1 connection
   * @return count
   */
  public static int getIncreasingConnectionCount() {
    int count = 1;
    int current = getConnectionCount();
    count = current / 4;
    if (count < 1) {
      count = 1;
    }
    return count;
  }

  /**
   * get decreasing connection count, not just remove 1 connection
   * @return count
   */
  public static int getDecreasingConnectionCount() {
    int count = 0;
    int current = getConnectionCount();
    if (current < 10) {
      return 0;
    }
    return current / 3;
  }

  public synchronized static void printDebugMsg() {
    printDebugMsg(System.out);
  }

  public synchronized static void printDebugMsg(PrintStream out) {
    if (DEBUG == false) {
      return;
    }
    StringBuffer msg = new StringBuffer();
    msg.append("debug message in " + SimpleConnetionPool.class.getName());
    msg.append("\r\n");
    msg.append("total count is connection pool: " + getConnectionCount());
    msg.append("\r\n");
    msg.append("not used connection count: " + getNotUsedConnectionCount());
    msg.append("\r\n");
    msg.append("used connection, count: " + getUsedConnectionCount());
    out.println(msg);
    Iterator iterator = m_usedUsedConnection.iterator();
    while (iterator.hasNext()) {
      ConnectionWrapper wrapper = (ConnectionWrapper) iterator.next();
      wrapper.debugInfo.printStackTrace(out);
    }
    out.println();
  }

  public static synchronized int getNotUsedConnectionCount() {
    return m_notUsedConnection.size();
  }

  public static synchronized int getUsedConnectionCount() {
    return m_usedUsedConnection.size();
  }

  public static synchronized int getConnectionCount() {
    return m_notUsedConnection.size() + m_usedUsedConnection.size();
  }

  public static String getUrl() {
    return m_url;
  }

  public static void setUrl(String url) {
    if (url == null) {
      return;
    }
    m_url = url.trim();
  }

  public static String getUser() {
    return m_user;
  }

  public static void setUser(String user) {
    if (user == null) {
      return;
    }
    m_user = user.trim();
  }

  public static String getPassword() {
    return m_password;
  }

  public static void setPassword(String password) {
    if (password == null) {
      return;
    }
    m_password = password.trim();
  }

}

class ConnectionWrapper implements InvocationHandler {
  private final static String CLOSE_METHOD_NAME = "close";
  public Connection connection = null;
  private Connection m_originConnection = null;
  public long lastAccessTime = System.currentTimeMillis();
  Throwable debugInfo = new Throwable("Connection initial statement");

  ConnectionWrapper(Connection con) {
    Class[] interfaces = {java.sql.Connection.class};
    this.connection = (Connection) Proxy.newProxyInstance(
      con.getClass().getClassLoader(),
      interfaces, this);
    m_originConnection = con;
  }

  void close() throws SQLException {
    m_originConnection.close();
  }

  public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
    Object obj = null;
    if (CLOSE_METHOD_NAME.equals(m.getName())) {
      SimpleConnetionPool.pushConnectionBackToPool(this);
    }
    else {
      obj = m.invoke(m_originConnection, args);
    }
    lastAccessTime = System.currentTimeMillis();
    return obj;
  }
}


使用方法

public class TestConnectionPool{
  public static void main(String[] args) {
    SimpleConnetionPool.setUrl(DBTools.getDatabaseUrl());
    SimpleConnetionPool.setUser(DBTools.getDatabaseUserName());
    SimpleConnetionPool.setPassword(DBTools.getDatabasePassword());

    Connection con = SimpleConnetionPool.getConnection();
    Connection con1 = SimpleConnetionPool.getConnection();
    Connection con2 = SimpleConnetionPool.getConnection();

    //do something with con ...

    try {
      con.close();
    } catch (Exception e) {}

    try {
      con1.close();
    } catch (Exception e) {}

    try {
      con2.close();
    } catch (Exception e) {}

    con = SimpleConnetionPool.getConnection();
    con1 = SimpleConnetionPool.getConnection();
    try {
      con1.close();
    } catch (Exception e) {}

    con2 = SimpleConnetionPool.getConnection();
    SimpleConnetionPool.printDebugMsg();

  }
}


运行测试程序后打印连接池中 Connection 状态, 以及正在使用的没有关闭 Connection 信息。


Java, java, J2SE, j2se, J2EE, j2ee, J2ME, j2me, ejb, ejb3, JBOSS, jboss, spring, hibernate, jdo, struts, webwork, ajax, AJAX, mysql, MySQL, Oracle, Weblogic, Websphere, scjp, scjd Java 的JDBC 数据库连接池实现方法




Java 的JDBC 数据库连接池实现方法

关键字: Java, JDBC, Connection Pool, Database, 数据库连接池, sourcecode

虽然 J2EE 程序员一般都有现成的应用服务器所带的JDBC 数据库连接池,不过
↑返回目录
前一篇: 配置Eclipse进行远程调试
后一篇: 我喜爱的Java 5.0的五个特性