Edzy_Java

  BlogJava :: 首页 ::  ::  ::  :: 管理 ::
  58 随笔 :: 12 文章 :: 11 评论 :: 0 Trackbacks
关键字: 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 的实现方法。下面给出一种实现方法。

package connectionpool;

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

public class SimpleConnetionPool {
 private static LinkedList<Object> m_notUsedConnection = new LinkedList<Object>();
 private static HashSet<Object> m_usedUsedConnection = new HashSet<Object>();
 private static String m_url = "jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=campusblog";
 private static String m_user = "sa";
 private static String m_password = "sa";
 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.microsoft.jdbc.sqlserver.SQLServerDriver").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<Object> list = new LinkedList<Object>();
  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();
 }
}

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();

  }
}

posted on 2006-11-15 17:09 lbfeng 阅读(286) 评论(0)  编辑  收藏 所属分类: JSP&Servlet技术

只有注册用户登录后才能发表评论。


网站导航: