最近,本人着手开发要有一个有强大后台的网站,在使用连接池时,觉得使用服务器自带的连接池总有些受限制。同时,为了加深对Java的学习和研究。写下了下面的连接池类。 该连接池主要有一下功能; 1)初始化一次,到处使用。 2)强大的日志功能,记录每一个sql动作,包括Connection、ResultSet 和Statement 3)根据连接的数量,定时自动回收已经释放或超时的连接。 4)配置灵活,可以使用各种JDBC驱动程序,支持多驱动程序。
源代码:
/* * @Title 连接池 * @Author: zxg * @Version 1.0 * @Memo:定义数据库连接及其数据库连接池等 */ package com.drsl.db;
import java.io.*; import java.sql.*; import java.util.*; import java.util.Date; import java.lang.reflect.*; import com.mysql.jdbc.Driver;
import com.drsl.db.*;
public class ConnectionManager { static private ConnectionManager instance; // 唯一实例 static private int clients; static private int maxonlinetime=30*60*1000; private Vector drivers = new Vector(); private Hashtable pools = new Hashtable(); private Timer checkConnTimer=new Timer(); static private PrintWriter log; /** * 返回唯一实例.如果是第一次调用此方法,则创建实例 * * @return ConnectionManager 唯一实例 */ static synchronized public ConnectionManager getInstance() { if (instance == null) { instance = new ConnectionManager(); } // clients++; return instance; } /** * 建构函数私有以防止其它对象创建本类实例 */ private ConnectionManager() { init(); } /** * 读取属性完成初始化 */ private void init() { try { InputStream is = getClass().getResourceAsStream("db.properties"); Properties dbProps = new Properties(); dbProps.load(is); } catch (Exception e) { e.printStackTrace(); System.err.println("不能读取属性文件= " + "请确保db.properties在CLASSPATH指定的路径中"); return; } String logFile = dbProps.getProperty("logfile", "log.txt"); try { log = new PrintWriter(new FileWriter(logFile, true), true); } catch (IOException e) { System.err.println("无法打开日志文件: " + logFile); log = new PrintWriter(System.err); } loadDrivers(dbProps); createPools(dbProps); } /** * 装载和注册所有JDBC驱动程序 * * @param props 属性 */ private void loadDrivers(Properties props) { String driverClasses = props.getProperty("drivers"); StringTokenizer st = new StringTokenizer(driverClasses); while (st.hasMoreElements()) { String driverClassName = st.nextToken().trim(); try { Driver driver = (Driver)Class.forName(driverClassName).newInstance(); if(driver!=null){ DriverManager.registerDriver(driver); drivers.addElement(driver); log("Begin"); log("成功注册JDBC驱动程序" + driverClassName); } else{ log("Begin"); log("注册JDBC驱动程序" + driverClassName+"失败"); } } catch (Exception e) { log("Begin"); log("无法注册JDBC驱动程序: " + driverClassName + ", 错误: " + e); } } } /** * 根据指定属性创建连接池实例. * * @param props 连接池属性 */ private void createPools(Properties props) { Enumeration propNames = props.propertyNames(); while (propNames.hasMoreElements()) { String name = (String) propNames.nextElement(); if (name.endsWith(".url")) { String poolName = name.substring(0, name.lastIndexOf(".")); String url = props.getProperty(poolName + ".url"); if (url == null) { log("没有为连接池" + poolName + "指定URL"); continue; } String user = props.getProperty(poolName + ".user"); String password = props.getProperty(poolName + ".password"); String maxconn = props.getProperty(poolName + ".maxconn", "0"); String minconn = props.getProperty(poolName + ".minconn", "10"); String option=props.getProperty(poolName+".option",""); int max,min; try { max = Integer.valueOf(maxconn).intValue(); } catch (NumberFormatException e) { log("错误的最大连接数限制: " + maxconn + " .连接池: " + poolName); max = 0; } try { min = Integer.valueOf(minconn).intValue(); } catch (NumberFormatException e) { log("错误的最小连接数限制: " + minconn + " .连接池: " + poolName); min = 0; } try{ ConnectionPool pool = new ConnectionPool(poolName, url,user,password,min,max,option); pools.put(poolName, pool); //2秒钟后开始每个一分钟检查一次连接池情况 checkConnTimer.schedule(pool,2000,60*1000);
log("成功创建连接池" + poolName); }catch(Exception e){ log(e,"创建DBConnectionPool出错"); } } } }
/** * 将连接对象返回给由名字指定的连接池 * * @param name 在属性文件中定义的连接池名字 * @param con 连接对象 */ public void freeConnection(String name, Connection conn) { ConnectionPool pool = (ConnectionPool) pools.get(name); if (pool != null) { pool.freeConnection(conn); } } /** * 获得一个可用的(空闲的)连接.如果没有可用连接,且已有连接数小于最大连接数 * 限制,则创建并返回新连接 * * @param name 在属性文件中定义的连接池名字 * @return Connection 可用连接或null */ public Connection getConnection(String name) { ConnectionPool pool = (ConnectionPool) pools.get(name); if (pool != null) { return pool.getConnection(); } return null; } /** * 获得一个可用连接.若没有可用连接,且已有连接数小于最大连接数限制, * 则创建并返回新连接.否则,在指定的时间内等待其它线程释放连接. * * @param name 连接池名字 * @param time 以毫秒计的等待时间 * @return Connection 可用连接或null */ public Connection getConnection(String name, long time) { ConnectionPool pool = (ConnectionPool) pools.get(name); if (pool != null) { return pool.getConnection(time); } return null; } /** * 关闭所有连接,撤销驱动程序的注册 */ public synchronized void release() { // 等待直到最后一个客户程序调用 // if (--clients != 0) { // return; // } checkConnTimer.cancel(); Enumeration allPools = pools.elements(); while (allPools.hasMoreElements()) { ConnectionPool pool = (ConnectionPool) allPools.nextElement(); pool.cancel(); pool.release(); } Enumeration allDrivers = drivers.elements(); while (allDrivers.hasMoreElements()) { Driver driver = (Driver) allDrivers.nextElement(); try { DriverManager.deregisterDriver(driver); log("撤销JDBC驱动程序 " + driver.getClass().getName()+"的注册"); } catch (SQLException e) { log(e,"无法撤销下列JDBC驱动程序的注册: " + driver.getClass().getName()); } }
} /** * 将文本信息写入日志文件 */ static public void log(String msg) { log.println(new Date() + ": " + msg); } /** * 将文本信息与异常写入日志文件 */ static public void log(Throwable e, String msg) { log.println(new Date() + ": " + msg); e.printStackTrace(log); } //测试
static public void main(String[] args) { ConnectionManager dcm=null; try{ dcm=ConnectionManager.getInstance(); Connection conn=dcm.getConnection("mysql"); Connection conn1=dcm.getConnection("mysql"); Connection conn2=dcm.getConnection("mysql"); Connection conn3=dcm.getConnection("mysql"); Connection conn4=dcm.getConnection("mysql"); Connection conn5=dcm.getConnection("mysql"); Connection conn6=dcm.getConnection("mysql"); Connection conn7=dcm.getConnection("mysql"); Connection conn8=dcm.getConnection("mysql"); Connection conn9=dcm.getConnection("mysql"); Connection conn10=dcm.getConnection("mysql"); Connection conn11=dcm.getConnection("mysql"); Connection conn12=dcm.getConnection("mysql"); Connection conn13=dcm.getConnection("mysql"); Connection conn14=dcm.getConnection("mysql"); ResultSet rs; String sql="select * from css"; Statement st=conn.createStatement(); if(st==null) { log("main--error while get /"Statement/""); return; } rs=st.executeQuery(sql); if(rs==null){ log("main--error while get /"ResultSet/""); return; } System.out.println("/r/n"); while(rs.next()){ System.out.println(rs.getString(1)); } rs.close(); st.close(); rs=null; st=null; conn.close(); conn1.close(); conn2.close(); conn3.close(); conn4.close(); conn5.close(); conn6.close(); conn7.close(); conn8.close(); conn9.close(); conn10.close(); conn11.close(); conn12.close(); conn13.close(); conn14.close(); conn=null; conn1=null; conn2=null; conn3=null; conn4=null; conn5=null; conn6=null; conn7=null; conn8=null; conn9=null; conn10=null; conn11=null; conn12=null; conn13=null; conn14=null; }catch(SQLException e){ dcm.log(e,"main--error"); } }
}
//
/***************连接池类*************************************************/
/** * 此类定义了一个连接池.它能够根据要求创建新连接,直到预定的最 * 大连接数为止.在返回连接给客户程序之前,它能够验证连接的有效性. * 它继承自 TimerTask 被 ConnectionManager 类的timer成员调度 */ package com.drsl.db;
import java.io.*; import java.sql.*; import java.util.*; import java.util.Date; import java.lang.reflect.*;
import com.drsl.db.*;
public class ConnectionPool extends TimerTask{ private int countConn; private Vector freeConns = new Vector(); private Vector usingConns = new Vector(); private int maxUseTime;//使用中的连接最大空闲时间 private int maxFreeTime;//空闲的连接最大空闲时间(在连接数未小于最小连接数时,关闭此连接) private int maxConn;//最大连接数 private int minConn;//最小连接数 private String name;//pool name private String url; private String user; private String password; private String option; // private PrintWriter log;
/** * 创建新的连接池 * * @param name 连接池名字 * @param url 数据库的JDBC url * @param dbInfo 数据库连接信息 * @param maxConn 此连接池允许建立的最大连接数 */ public ConnectionPool(String name, String url,String user,String password, int minConn,int maxConn,String option) { this.name = name; this.url = url; this.user = user; this.password = password; this.option=option; this.maxConn = maxConn; this.minConn = minConn; if(this.minConn<=0) this.minConn=10; log("End One Part/r/n"); for(int i=0; i<minConn;i++){ newConnection(); } } /** * 将新建的连接添加到连接池 * * @param connobj 新建的连接 */ public synchronized void freeConnection(ConnectionObject connobj) { // 将指定连接加入到向量末尾 try{ connobj.setInUse(false); freeConns.addElement(connobj); log("成功记录一个新建连接或者回收一个已释放连接"); notifyAll(); }catch(ArrayIndexOutOfBoundsException e){ log(e,"freeConnection(ConnectionObject connobj) --失败"); } } /** * 将不再使用的连接返回给连接池 * * @param conn 客户程序主动释放的连接 */ public synchronized void freeConnection(Connection conn) { // 将指定连接加入到向量末尾 ConnectionObject connobj=null; for(int i=0;i<usingConns.size();i++) { connobj=(ConnectionObject)usingConns.get(i); if(connobj.getConnection()==conn) break; } if(connobj!=null){ try{ connobj.setInUse(false); freeConns.addElement(connobj); usingConns.removeElement(connobj); log("成功回收一个连接"); notifyAll(); }catch(Exception e){ log(e,"回收一个连接--失败"); } } } /** * 从连接池获得一个可用连接.如没有空闲的连接且当前连接数小于最大连接 * 数限制,则创建新连接.如原来登记为可用的连接不再有效,则从向量删除之, * 然后递归调用自己以尝试新的可用连接. */ public synchronized Connection getConnection() { ConnectionObject connobj = null; Connection conn=null; // 获取向量中第一个可用连接 try { connobj = (ConnectionObject) freeConns.get(0); } catch (Exception e) { log("End One Part/r/n");
log("从连接池" + name+"获取一个连接失败"); if( maxConn == 0 || countConn < maxConn) { connobj = newConnection(); } } //如没有空闲的连接且当前连接数小于最大连接数限制,则创建新连接 if(connobj==null && ( maxConn == 0 || countConn < maxConn)) { log("从连接池" + name+"获取一个连接失败"); log("End One Part/r/n"); connobj = newConnection(); } if (connobj != null) { connobj.setLastAccessTime(new Date().getTime()); connobj.setInUse(true); usingConns.addElement(connobj); freeConns.removeElementAt(0); conn=connobj.getConnection(); return conn; }else{ log("获取连接" + name+"失败--连接数量已达最大上限"); return null; } } /** * 从连接池获取可用连接.可以指定客户程序能够等待的最长时间 * 参见前一个getConnection()方法. * * @param timeout 以毫秒计的等待时间限制 */ public synchronized Connection getConnection(long timeout) { long startTime = new Date().getTime(); Connection conn=null; while ((conn = getConnection()) == null) { try { wait(timeout);//?????????????? } catch (InterruptedException e){ } if ((new Date().getTime() - startTime) >= timeout) { // wait()返回的原因是超时????????? return null; } } return conn; } /** * 关闭所有连接 */ public synchronized void release() { // cancel(); Enumeration allConnections = freeConns.elements(); while (allConnections.hasMoreElements()) { ConnectionObject connobj = (ConnectionObject) allConnections.nextElement(); try { connobj.close(); connobj=null; log("关闭连接池" + name+"中的一个连接"); } catch (SQLException e) {//SQLException log(e, "无法关闭连接池" + name+"中的连接"); } } freeConns.removeAllElements(); // allConnections = usingConns.elements(); while (allConnections.hasMoreElements()) { ConnectionObject connobj = (ConnectionObject) allConnections.nextElement(); try { connobj.close(); connobj=null; log("关闭连接池" + name+"中的一个连接"); } catch (SQLException e) {//SQLException log(e, "无法关闭连接池" + name+"中的连接"); } } usingConns.removeAllElements(); } /** * 创建新的连接 */ private ConnectionObject newConnection() { ConnectionObject connobj= null; try { log("连接池" + name+"创建一个新的连接对象"); String URL=url+option; log("URL=" +URL ); Connection conn = DriverManager.getConnection(URL,user,password); connobj=new ConnectionObject(conn,false); freeConnection(connobj); countConn++; } catch (SQLException e) { log(e, "无法创建下列URL的连接: " + url+" for User= " +user+" Password="+password); return null; } return connobj; } //检查各连接状态(每分钟一次) public void run (){ ConnectionObject connobj=null; //回收 正在使用中的已经"关闭"的连接 //和 使用时间已经超时的连接 int i=0; while(i<usingConns.size()){
connobj=(ConnectionObject)usingConns.get(i); if(connobj.isInUse()==false){ try{ log("run--回收 正在使用中的已经/"关闭/"的连接"); freeConnection(connobj); usingConns.removeElementAt(i); i--; }catch(ArrayIndexOutOfBoundsException e){ log(e,"run--回收 正在使用中的已经/"关闭/"的连接--失败"); } }else{ long nowtime=new Date().getTime(); long t=nowtime-connobj.getLastAccessTime(); try{ if(t>20*60*1000){//超时时间为20分钟 log("run--回收 使用时间已经超时的连接"); freeConnection(connobj); usingConns.removeElementAt(i); i--; } }catch(ArrayIndexOutOfBoundsException e ){ log(e,"run--回收 使用时间已经超时的连接--失败"); } } i++; } //删除 空闲的已经被意外关闭的连接 i=0; while(i<freeConns.size()){ connobj= (ConnectionObject)freeConns.get(i); try{ if(connobj.getConnection().isClosed()){ connobj=null; freeConns.removeElementAt(i); countConn--; i--; log("run--删除 空闲的已经被意外关闭的连接"); } }catch(Exception e){ log(e,"run--删除 空闲的已经被意外关闭的连接-失败"); } i++; } //删除 从空闲连接中多余的(大于最小连接数的)连接 long cc=countConn-minConn; i=0; while(i<cc && freeConns.size()>1){ try{ connobj=(ConnectionObject)freeConns.get(0); connobj.close(); connobj=null; freeConns.removeElementAt(0); countConn--; log("run--删除 从空闲连接中多余的(大于最小连接数的)连接 "); }catch(SQLException e){ log(e,"run--从空闲连接中多余的(大于最小连接数的)连接--失败"); } i++; } //增加连接 保持要求的最小连接数 if(cc<0){ cc=-cc; log("End One Part/r/n"); log("run--增加连接 保持要求的最小连接数"); for(i=0;i<cc;i++){ newConnection(); } } //增加连接 保持至少有一个可用连接 if(freeConns.size()<1){ log("End One Part/r/n"); log("run--增加连接 保持至少有一个可用连接"); newConnection(); } log("run--once"); // notifyAll(); } /** * 将文本信息写入日志文件 */ private void log(String msg) { ConnectionManager.log(msg); } /** * 将文本信息与异常写入日志文件 */ private void log(Throwable e, String msg) { ConnectionManager.log(e,msg); }
}
/
/** * 数据连接的自封装,屏蔽了close方法和createStatement,prepareStatement 方法以返回自己的接管类 * @author zxg */ package com.drsl.db;
import java.io.*; import java.sql.*; import java.util.*; import java.util.Date; import java.lang.reflect.*;
public class ConnectionObject implements InvocationHandler{ private final static String CLOSE_METHOD_NAME = "close"; private final static String CREATSTATMENT_METHOD_NAME = "createStatement"; private final static String PREPARESTATEMENT_METHOD_NAME = "prepareStatement"; private Connection conn = null; private Connection conn_proxy=null; private PreparedStatementObject pso=null; private StatementObject stmo=null; //数据库的忙状态 private boolean inUse = false; //用户最后一次访问该连接方法的时间 private static long lastAccessTime = new Date().getTime();
public ConnectionObject(Connection conn, boolean inUse){ this.conn = conn; this.inUse = inUse; } /** * Returns the conn. * @return Connection */ //返回数据库连接conn的接管类,以便截住Connection 的各个方法 public Connection getConnection() { if(conn_proxy==null){ ClassLoader classloader=conn.getClass().getClassLoader(); Class[] interfaces = conn.getClass().getInterfaces(); if(interfaces==null||interfaces.length==0){ interfaces = new Class[1]; interfaces[0] = Connection.class; } try{ conn_proxy= (Connection)Proxy.newProxyInstance(classloader,interfaces,this); }catch(NullPointerException e){ log(e,"ConnectionObject getConnection()--error"); } if(conn_proxy!=null) log("ConnectionObject getConnection()--success"); } return conn_proxy; } /** * 该方法真正的关闭了数据库的连接 * @throws SQLException */ synchronized void close() throws SQLException{ //由于类属性conn是没有被接管的连接,因此一旦调用close方法后就直接关闭连接 conn.close(); conn=null; } /** * 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; log("ConnectionObject--invoke:Method: /""+m.getName()+"/""); //判断是否调用了close的方法,如果调用close方法则把连接置为无用状态 if(CLOSE_METHOD_NAME.equals(m.getName())){ setInUse(false); }else if(CREATSTATMENT_METHOD_NAME.equals(m.getName())){ //如果调用了 createStatment 的方法,封装 Statement 类接受管理 Statement stm=(Statement) m.invoke(conn, args); if(stm!=null && stmo==null){ stmo=new StatementObject(stm); obj=stmo.getStatement(); }else{ ConnectionManager.log("ConnectionObject--invoke:Method: /""+m.getName()+"/"--失败"); } }else if(PREPARESTATEMENT_METHOD_NAME.equals(m.getName())){ //如果调用了 createStatment 的方法,封装 PreparedStatement 类接受管理 PreparedStatement ps=(PreparedStatement) m.invoke(conn, args); if(ps!=null && pso==null){ pso=new PreparedStatementObject(ps); } else if(pso!=null){ obj=pso.getPreparedStatement(); }else log("ConnectionObject--invoke:Method: /""+m.getName()+"/"--失败"); }else obj = m.invoke(conn, args); //设置最后一次访问时间,以便及时清除超时的连接 lastAccessTime = new Date().getTime(); return obj; } /** * Returns the lastAccessTime. * @return long */ public static long getLastAccessTime() { return lastAccessTime; } /** * set the lastAccessTime. * @param latimelong */ public static void setLastAccessTime(long latime) { lastAccessTime=latime; }
/** * Sets the inUse. * @param inUse The inUse to set */ public void setInUse(boolean inUse) { this.inUse = inUse; } public synchronized void release() { try{ close(); }catch(SQLException e){ log(e,"ConnectionObject--release 调用 close 失败"); } } /** * 将文本信息写入日志文件 */ private void log(String msg) { ConnectionManager.log(msg); } /** * 将文本信息与异常写入日志文件 */ private void log(Throwable e, String msg) { ConnectionManager.log(e,msg); }
}
待续。。。
|
|