📄 connectionpool.java
字号:
package com.zhanghao.common.database;
import java.sql.*;
import java.util.*;
/**
* <p>
* 数据库连接池。
* 池里每个连接的最大活跃期是24小时,过期则自动重新连接数据库(以保证性能稳定)。
* 如果连接池中的某个连接因为某个原因被数据库断开连接,则系统把该连接从连接池移出。
* 客户端请求连接时,如果当前没有空闲连接,而连接总数小于连接池最大容量,则新建
* 连接加入到连接池
* </p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2004</p>
* <p>Company: </p>
* @author hmy
* @version 2004-06-11
*/
import com.zhanghao.common.util.*;
public class ConnectionPool {
public static final long SECOND = 1000; //秒
public static final long MINUTE = SECOND*60; //分
public static final long HOUR = MINUTE*60; //小时
//一个数据库连接的有效期。超过有效其的连接应该被释放,连接池重新建立一个连
// 接来代替它,默认为24(单位:小时)
//一个连接建立后,使用一定时间后,应该释放,重新建立一个连接代替它,否则
//一个连接时间长了会出现性能和可靠性不稳定
private final static int CONNECTION_ALIVE_TIME = 24;
private int poolSize; //连接池大小
private long timeout; //连接时的time out设置(单位:分钟)。必须大于0,默认为30
private String driver = null; //驱动程序类
private String dbURL = null; //数据库服务器URL地址
private String username = null; //访问数据库的用户名
private String password = null; //访问数据库的密码
/**
* 用来测试的数据库连接的有效性的数据表的表名。要求数据
* 库里存在这个表,而且这个表属于这个用户;至于这个表有
* 些什么字段,有多少记录,都不重要(记录条数最好不要超过10条,可以没有记录)。
* 这个属性的值可以为null,如果为null,则对某些数据库,这个连接池可能失去自动重连
* 的功能(即在数据库服务器重新启动或网络连接短暂断开后,
* 该连接池无法自动重新连接数据库)。
*/
private String testTableName = null;
/**
* 物理连接池(包含所有物理数据库连接,不论是否被用户占用)。
* 本集合的大小即代表与数据库的总连接数。
* 集合中每个元素都是一个java.sql.Connection对象。
*/
private Vector allConns;
// /**
// * 所有连接(包装)的池子
// */
// private Vector allWrappers;
/**
* 空闲连接(包装)池(只包含未被用户占用的连接)。
* 本集合的大小即代表当前空闲数据库连接(未被用户占用)的数目。
* 里面的每个元素都是一个ConnectionWrapper对象。用户请求连接时,
* 系统从本集合中取得ConnectionWrapper对象,同时把该对象从本集合中移除。
*/
private Vector freeWrappers;
private static ConnectionPool pool = null;
/**
*
* @param driver 驱动程序类名
* @param dbURL 数据库服务器的URL地址
* @param username 数据库用户名
* @param password 数据库用户的密码
* @param test_table_name 参见属性testTableName的说明
* @param poolSize 连接池大小
* @param timeout timeout设置,单位:分
*/
private ConnectionPool(
String driver,
String dbURL,
String username,
String password,
int poolSize,
int timeout) throws Exception{
this.driver = driver.trim();
this.dbURL = dbURL.trim();
this.username = username.trim();
this.password = password.trim();
this.timeout = timeout;
this.poolSize = poolSize;
// if(testTableName!=null && testTableName.trim().length()==0){
// testTableName = null;
// }
allConns = new Vector(poolSize);
freeWrappers = new Vector(poolSize);
try{
Class.forName(driver);
for(int i=0;i<poolSize;i++){//根据池的大小,建立若干个连接,放入池中
addConnection();
}
}catch(Exception e){
for(int i=0;i<allConns.size();i++){
try{
//关闭所有已经取得的物理连接
((Connection)allConns.get(i)).close();
}catch(Exception eee){}
}//end for i
allConns.clear();
freeWrappers.clear();
throw e;
}//end try
//启动检查线程,定期对过期连接进行释放、重新连接的工作
(new ReCreateThread()).start();
(new ReConnectThread()).start();
}//end of method
private void addConnection() {
try{
Connection conn = DriverManager.getConnection(dbURL, username, password); //建立连接
ConnectionWrapper wrapper = new ConnectionWrapper(conn, freeWrappers);
allConns.addElement(conn); //把物理连接放入数据库连接池中
//把物理连接放入包装器后,放入空闲连接池中
freeWrappers.addElement(wrapper);
}catch (SQLException e) {
System.out.println("ConnectionPool,addConnection(),新建数据库链接失败,原因:"+e);
}
}
/**
* 初始化数据库连接池
* @param driver
* @param url
* @param user
* @param password
* @param testTableName 用来测试的数据库连接的有效性的数据表的表名。要求数据
* 库里存在这个表,而且这个表属于这个用户;至于这个表有
* 些什么字段,有多少记录,都不重要(记录条数最好不要超过10条,
* 可以没有记录)。这个参数可以为null,
* 如果为null,则对某些数据库,这个连接池可能失去自动重连
* 的功能(即在数据库服务器重新启动或网络连接短暂断开后,
* 该连接池无法自动重新连接数据库)。
* 连接池将用这个表
* @param size 连接池大小
* @param timeout 连接超时设置(单位:秒)
*/
public static void initConnectionPool(String driver,String url,String user,
String password, int size,int timeout) throws SQLException {
if(pool!=null){ //连接池已经建立
return;
}
try{
pool = new ConnectionPool(driver,url,user,password,size,timeout);
}catch(Exception e){
throw new SQLException(e.toString());
}
}
/**
* 从空闲连接池取得连接。
* @return 取得的连接
* @throws 遇到SQL异常则抛出,连接超时也抛出异常。
*/
public static Connection getConnection() throws SQLException {
if(pool==null){
throw new SQLException("异常,ConnectionPool,getConnection(),连接池尚未初始化");
}
ConnectionWrapper wrapper;
Connection conn = null;
long startTime = System.currentTimeMillis(); //发起请求的时间
while(conn==null && System.currentTimeMillis()-startTime<SECOND*pool.timeout){
synchronized(pool.freeWrappers){
if (pool.freeWrappers.size() > 0) {
//从空闲连接池取出连接。
//从连接池取出连接时从队列的最前面取,把连接放回连接池时放到队列的最后
wrapper = (ConnectionWrapper) pool.freeWrappers.remove(0);
return wrapper;
}else{ //如果没有空闲连接
if(pool.allConns.size()<pool.poolSize){ //有效物理连接不够
pool.addConnection(); //增加连接
}
}
}//end synchronized
try{
Thread.sleep(SECOND); //等待1秒
}catch(Exception e){
//
}
}//end while
throw new SQLException("ConnectionPool,getConnection,请求连接超时");
}//end of method
/**
* 定期检查物理连接池里的连接,如果某个连接被数据库断开了,则重新建立连接替
* 代它,同时要对空闲连接(包装)池做处理
*/
private class ReConnectThread extends Thread{
public void run(){
while(true){
try {
Thread.sleep(10 * SECOND); //间隔10秒
}
catch (Exception e) {
//
}
deleteInvalidConns(); //删除物理连接池里的无效连接
deleteInvalidWrappers(); //删除无效的连接包装器
}//end while
}
private void deleteInvalidWrappers(){
if(allConns.size()==poolSize){
return;
}
synchronized(freeWrappers){
ConnectionWrapper wrapper = null;
Connection conn = null;
for(int i=freeWrappers.size()-1;i>=0;i--){
wrapper = (ConnectionWrapper)freeWrappers.get(i);
conn = wrapper.getPhysicalConnection();
if(allConns.contains(conn)==false){ //说明该包装器已经无效
freeWrappers.remove(wrapper); //删除无效包装器
}//end if
}//end for i
}//end synchronized
}//end method
private void deleteInvalidConns(){
synchronized(allConns){
Connection conn = null;
for(int i=allConns.size()-1;i>=0;i--){
conn = (Connection)allConns.get(i);
if(testConnection(conn)==false){
try{
conn.close(); //关闭物理连接
}catch(Exception e){}
allConns.remove(conn); //删除无效物理连接
}
}//end for i
}//end synchronized
// System.out.println("连接池大小:"+allConns.size());
}
/**
* 检查一个数据库连接是否有效
* @param conn
* @return 有效则返回true
*/
private boolean testConnection(Connection conn){
Statement stmt = null;
ResultSet rs = null;
boolean flag = false;
try{
stmt = conn.createStatement(); //测试是否有效
//if(testTableName!=null){
//String sql = "select getdate()"; //测试SQL,根据不同的数据库更改
//rs = stmt.executeQuery(sql);
//}
flag = true; //没出异常,说明连接有效
// System.out.println("------testConnection(),连接有效");
}catch(SQLException e){
// System.out.println("------testConnection(), table name: "+testTableName);
// System.out.println("------testConnection(), 异常:");
// e.printStackTrace();
flag = false; //认定连接无效
}finally{
if(rs!=null)
try{
rs.close();
}catch(Exception ee){}
if(stmt!=null)
try{
stmt.close();
}catch(Exception ee){}
}
return flag;
}//end of method testConnection
}
/**
* 负责释放过期的数据库连接、重新建立连接替代它的线程
* <p>Title: </p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2004</p>
* <p>Company: </p>
* @author hmy
* @version 1.0
*/
private class ReCreateThread extends Thread{
public void run(){
while(true){
try{
Thread.sleep(HOUR); //等待一个小时
}catch(Exception e){}
recreateConns(); //检查连接池中的连接,对于已经过期的,释放,用新的连接代替
}//end for while
}
//在空闲连接池中检查已经过期的连接,将它释放,新建一个连接代替它
private void recreateConns(){
//Debug.outInfo("ConnectionPool,ReCreateThread,巡视,检查过期连接");
//用来存储需要被释放、重新连接的ConnectionWrapper对象
//已经超过有效期的、当前正处于空闲状态的ConnectionWrapper对象需要被释放、重连
Vector timeouts = new Vector();
//从空闲连接池取出所有过期连接,放入timeouts集合中
long currTime = System.currentTimeMillis(); //当前时间
ConnectionWrapper wrapper = null;
synchronized(freeWrappers){
//当前长度。本次操作期间,被用户返回连接池的连接放到了队列的最后,本次不操作它
int currSize = freeWrappers.size();
//遍历空闲连接池中的连接(因为有删除操作,所以从后面开始),把该释放的对象取出
for (int i = currSize-1; i>=0; i--) {
wrapper = (ConnectionWrapper) freeWrappers.get(i);
//如果连接已经过了有效期:
if (currTime - wrapper.getCreatedTime() > HOUR * CONNECTION_ALIVE_TIME) {//已经超时
timeouts.addElement(freeWrappers.remove(i));
}//end if
}//end for i
}//end synchronized
//把需要重新连接的对象释放,并新建连接替代它
Connection timeoutConn; //过期的物理连接
Connection newConn; //新的物理连接
for(int i=0;i<timeouts.size();i++){
wrapper = (ConnectionWrapper)timeouts.get(i);
timeoutConn = wrapper.getPhysicalConnection(); //取得包装器所包装的物理连接
try{
allConns.remove(timeoutConn); //把过期物理连接从物理连接池移除
timeoutConn.close(); //释放过期物理连接
newConn = DriverManager.getConnection(dbURL,username,password);//新建一个物理连接
allConns.addElement(newConn); //放入物理连接池
//包装后放入空闲连接池:
freeWrappers.addElement(new ConnectionWrapper(newConn,freeWrappers));
}catch(Exception e){
//
}
}//end for i
}//end of method
}//end class ReCreateThread
//测试
public static void main(String[] args){
Connection conn = null;
int m = 1;
while(true){
try{
//取得连接,持有1秒再释放
conn = ConnectionPool.getConnection();
Debug.outInfo("得到连接: "+conn);
Debug.outInfo("conn.getAutoCommit(): "+conn.getAutoCommit());
Thread.sleep(1000); //等待1秒
conn.close();
Debug.outInfo("conn.getAutoCommit(): "+conn.getAutoCommit());
Debug.outInfo("------------------------------");
}catch(Exception e){
e.printStackTrace();
//Debug.outInfo("main(), e: "+e);
}
}//end while
}//end of method
}//end file
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -