自己尝试着去写了个数据库链接池,是用java实现的,写了也就两三天,最终还是写出来了,可是对于这个程序的稳定性我还是不很肯定。
我将整个程序放置在我的资源中了: http://download.csdn.net/user/wszlh1981 感兴趣的可下载过来看看,给点反馈是最好的,哪怕是狠狠的拍砖也行。
先说说这个程序的结构吧!
总共有六个类(呵呵!好像有点多):
1.MyException.java 这个类是自定义异常类
2.ReadConfigureInfo.java 这个类是读取属性文件的,有关于链接池的配置就放置在一个属性文件中。
3.MyConnection.java 这个类很关键,实现了Connection接口,也是对Connection实例进行代理的一个类,主要是对
close方法进行覆盖
4.DataQueue.java 这个类就是数据库链接池类,所有的数据库链接对象存储在这个类的两个容器中,一个存储被激活的链
接对象,另一个容器存储空闲的链接对象。
5.DataQueueManage.java 这个类是对数据库链接池管理的类,我设计的时候,把它定义为线程类,没隔3秒钟扫描一次数
据库链接池,并且根据属性文件中的配置管理数据库链接池
6.GetConnection.java 这个类是与外部相关联的类,里面主要有两个方法,获取和归还数据库链接对像
先来看看属性文件中的配置:
driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
url=jdbc:sqlserver://localhost:1433;databaseName=books
password=123456
user=sa
maxActive=100
maxIdle=30
maxWait=-1
上面这个一看就能明白是什么意思,就是对数据库链接做配置,还有配置了链接池的上限与下限。顺便说一下,我用的是SQL2005,而且是直连。
=== MyConnection.java ===
package myDataPool;
import java.sql.*;
import java.util.*;
/*
* 自定义数据库链接对象,代理模式,代理close方法
*/
class MyConnection implements Connection{
private Connection con;
private long lastActiveTime = 0;
/*
*
*/
long getLastAccessTime() {
return lastActiveTime;
}
void setLastAccessTime(long lastAccessTime) {
this.lastActiveTime = lastAccessTime;
}
/*
* 受保护的构造函数,同一个包中才可用
*/
protected MyConnection(Connection conn){
this.con=conn;
lastActiveTime=System.currentTimeMillis();//创建最后被激活的时间(返回以毫秒为单位的当前时间)
}
... ... ... ...
/*
* 代理close方法
*/
public void close() throws SQLException{
try
{
DataQueue.Instance().put(this);//将数据库链接对象返回链接池
System.out.println("调用代理类的close()方法,将数据库链接对象返回链接池!");
}
catch(Exception ex)
{
throw new MyException("调用代理类的close()方法,将数据库链接对象返回链接池产生异常:"+ex.getMessage());
}
}
/*
* 受保护的方法,关闭数据库的链接
*/
protected void myClose() throws SQLException{
this.con.close();
}
... ... ... ...
}
在这里我省略了其他方法,因为这里最关键的就是close() 方法了,当对方要关闭数据库的链接时,其实调用的是将数据库链接对象返回链接池的方法,这里用到了代理模式,其实我这样写,挺费事的,要实现Connection接口中的所有方法,运用动态代理会好很多的。
=== DataQueue.java ===
package myDataPool;
import java.util.ArrayList;
import java.util.HashMap;
/**
* 存储数据库链接的对象的列表集合,此处用到了单态模式
*/
class DataQueue {
private ArrayList<MyConnection> idleQueue;//存储空闲状态的数据库链接的对象的列表集合
private HashMap<String ,MyConnection> activQueue;//存储激活状态的数据库链接对象的列表集合
private static DataQueue dataqueue;
/*
* 获取空闲状态的数据库链接的对象的列表集合(只限本包使用)
*/
ArrayList<MyConnection> getIdleQueue() {
return idleQueue;
}
/**
* 私有的构造函数
*/
private DataQueue()
{
idleQueue=new ArrayList<MyConnection>();
activQueue=new HashMap<String ,MyConnection>();
}
/**
* 返回本类实例的静态方法
*/
synchronized static DataQueue Instance()
{
if(dataqueue==null)
{
dataqueue=new DataQueue();
}
return dataqueue;
}
/
/*
* 获取Connection对象的方法
*/
synchronized MyConnection get()throws Exception
{
if(idleQueue.size()>0)
{
MyConnection con=idleQueue.get(0);//从空闲状态的链接池中取出链接对象
activQueue.put(new Integer(con.hashCode()).toString(), con);//将链接对象放入"激活状态"的数据库链接对象的列表集合,键值是此链接对象的哈希值
idleQueue.remove(con);//将对象从空闲列表中清除
return con;
}
else
{
throw new MyException("数据库链接池,还没有可链接的对象!");
}
}
/*
* 返回Connection对象的方法
*/
synchronized void put(MyConnection con)throws Exception
{
if(con!=null)
{
String hashcode=new Integer(con.hashCode()).toString();//获取数据库链接对象的哈希值
Object ObjConnection=activQueue.get(hashcode);//从"激活状态"的数据库链接对象的列表集合中取出链接对象
if(ObjConnection!=null)
{
activQueue.remove(hashcode);//从"激活状态"的数据库链接对象的列表集合中删除链接对象
idleQueue.add(idleQueue.size(),con);//将链接对象添加到空闲列表中的最后
}
else
{
throw new MyException("返回的对象不匹配!");
}
}
else
{
throw new MyException("返回数据库池的对象为空!");
}
}
/
/*
* 添加Connection对象到链接池中的方法
*/
synchronized void add(MyConnection con)throws Exception
{
if(con!=null)
{
idleQueue.add(idleQueue.size(),con);//将数据库链接对象添加到空闲列表中
}
else
{
throw new MyException("要添加的到数据库链接池中的对象为空!");
}
}
/*
* 从链接池中删除单个Connection对象的方法
*/
synchronized void delete()throws Exception
{
if(idleQueue.size()>0)
{
MyConnection conn=idleQueue.get(0);
delete(conn);
}
else
{
throw new MyException("数据库连接池中已经为空!");
}
}
/*
* 从链接池中删除具体的Connection对象的方法
*/
synchronized void delete(MyConnection con)throws Exception
{
if(con!=null)
{
con.myClose();//关闭数据库链接
idleQueue.remove(con);
}
else
{
throw new MyException("要从数据库链接池中删除的对象为空!");
}
}
/*
* 获取数据库链接池中空闲的Connection对象的个数
*/
synchronized long getIdleNumber()throws Exception
{
return idleQueue.size();
}
/*
* 获取数据库链接池中被激活的Connection对象的个数
*/
synchronized long getActivNumber()throws Exception
{
return activQueue.size();
}
/*
* 销毁链接池的方法,释放资源
*/
synchronized void DestroyPool()throws Exception
{
for(int i=0;i<idleQueue.size();i++)
{
MyConnection conn=idleQueue.get(i);
if(conn!=null)
{
if(!conn.isClosed())
{
conn.myClose();//关闭数据库的链接
}
conn=null;
}
}
idleQueue.clear();//清空列表中的数据
Object[] keys=activQueue.keySet().toArray();
for(int i=0;i<activQueue.size();i++)
{
MyConnection conn=activQueue.get(keys[i].toString());
if(conn!=null)
{
if(!conn.isClosed())
{
conn.myClose();//关闭数据库的链接
}
conn=null;
}
}
activQueue.clear();//清空列表中的数据
idleQueue=null;
activQueue=null;
dataqueue=null;
}
}
上面就是链接池类了,封装了一些方法,给其他类去调用,这里面最关键也是get 与put 这两个方法。
=== DataQueueManage.java ===
package myDataPool;
import java.sql.DriverManager;
import java.util.*;
/*
* 管理数据库链接池,单态模式,继承线程类
*/
class DataQueueManage extends Thread {
private int maxActive;//链接池中活动状态的数据库链接最大数目,取值为0表示不受限制
private int maxIdle;//链接池中空闲状态的数据库链接最大数目,取值为0表示不受限制
private int maxWait;//指定数据库链接池中处于空闲状态的最长时间,以毫秒为单位,取值为-1表示无限量的等待
private long ActiveNumber;//数据库链接池中活动状态的数据库链接数目
private long IdleNumber;//数据库链接池中空闲状态的数据库链接数目
private boolean isExecute=true;
private static DataQueueManage manage;
/*
* 私有的构造方法
*/
private DataQueueManage()throws Exception
{
InitPool();//初始化数据库链接池
this.start();//在构造方法中启用线程
}
/*
* 获取本类实例的方法
*/
synchronized static DataQueueManage Instance()throws Exception
{
if(manage==null)
{
manage=new DataQueueManage();
}
return manage;
}
/*
* 停止线程的方法,当数据库链接池要被销毁时调用此方法
*/
void StopCheckPool()
{
isExecute=false;
manage=null;
}
/*
* 启动线程时调用的方法
*/
public void run( )//实现线程接口中的方法
{
try
{
while(isExecute)
{
sleep(3000);//线程每间隔5秒检查一次链接池的状况
System.out.println("启动线程,开始检查数据库链接池的状态!");
CheckPool();
}
}
catch (Exception e) //线程被中断异常
{
throw new MyException(e.getMessage());
}
}
/**
* 创建SQL2005数据库链接实例,使用了线程同步
*/
private synchronized MyConnection CreateConnection ()throws Exception
{
ReadConfigureInfo configureInfo= new ReadConfigureInfo();//创建读取配置文件的对象
String driverClassName =configureInfo.getProperty("driverClassName").trim();
String url = configureInfo.getProperty("url").trim();
String password = configureInfo.getProperty("password").trim();
String user = configureInfo.getProperty("user").trim();
Class.forName(driverClassName).newInstance();
MyConnection conn= new MyConnection(DriverManager.getConnection(url,user,password)); //创建代理的数据库链接对象
return conn;
}
/*
* 初始化数据库链接池
*/
synchronized void InitPool()throws Exception
{
System.out.println("初始化链接池");
LoadConfigureInfo();//加载配置信息
LoadDataPoolInfo();//加载链接池的信息
if(this.maxIdle!=0)
{
AddConnectionToDataPool(maxIdle);
}
else//表示最大空闲状态链接数不受限制
{
AddConnectionToDataPool(20);//初始化20个链接对象
}
}
/*
* 添加链接对象到数据库链接池的方法
*/
synchronized void AddConnectionToDataPool(int number)throws Exception
{
for(int i=0;i<number;i++)
{
MyConnection con=CreateConnection();
if(con!=null)
{
DataQueue.Instance().add(con);//将创建的对象添加到数据库链接池
System.out.println("将创建的对象添加到数据库链接池");
}
else
{
throw new MyException("添加新的数据库链接对象到链接池产生异常,链接对象为空!无法创建,请检查配置文件!");
}
}
}
/*
* 检查数据库链接池的状态
*/
synchronized void CheckPool()throws Exception
{
LoadConfigureInfo();//加载配置信息
LoadDataPoolInfo();//加载链接池的信息
if(this.ActiveNumber==0&&this.IdleNumber==0)//表示数据库链接池中没有任何数据库链接对象
{
InitPool();//初始化数据库链接池
return ;
}
if(this.IdleNumber!=0)//表示空闲的链接列表中不为空,进行检查,清空空闲超时的链接
{
if(maxWait!=-1)//表示数据库链接池中处于空闲状态的最长时间受到限制
{
long currentTime=System.currentTimeMillis();//获取当前时间
ArrayList<MyConnection> list=DataQueue.Instance().getIdleQueue();//获取空闲列表集合
for(int i=0;i<list.size();i++)
{
MyConnection conn=list.get(i);
long LastAccessTime=conn.getLastAccessTime();//获取数据库最后被激活的时间
if(currentTime-LastAccessTime>maxWait)//判断是否空闲超时
{
DataQueue.Instance().delete(conn);//删除空闲超时的链接对象
System.out.println("超时,将对象删除");
}
}
}
if(this.maxIdle!=0)//表示链接池中空闲状态的数据库链接最大数目受到限制
{
LoadDataPoolInfo();//再次加载链接池的信息
if(this.IdleNumber>this.maxIdle)//表示目前的空闲数目大于最大的空闲数目
{
for(int i=0;i<IdleNumber-this.maxIdle;i++)
{
DataQueue.Instance().delete();//删除多余的链接对象
System.out.println("超出最大空闲数目,将对象删除");
}
}
}
}
if(this.IdleNumber==0)//表示空闲的链接列表中为空,创建新的链接对象
{
System.out.print("链接池为空,重新创建新的链接");
if(maxIdle==0)//表示最大的空闲数不受限制
{
AddConnectionToDataPool(20);//暂时给链接池添加20个链接对象
}
else
{
AddConnectionToDataPool(maxIdle);
}
}
}
/*
* 检查激活的链接数是否没有超出最大范围
* 参数是当前已经激活的链接数目
*/
synchronized boolean CheckActiveStatic(long activeNumber)throws Exception
{
LoadConfigureInfo();//加载配置信息
System.out.println("检查激活的链接数是否没有超出最大范围");
if(activeNumber!=0)//表示被激活的数据库链接列表中不为空,检查激活的链接数是否超出最大范围
{
if(this.maxActive!=0)//表示最大的链接数受限制
{
if(activeNumber>=this.maxActive)//表示实际链接数已经超出最大链接数
{
System.out.println("链接数已经超出最大链接数,等待!");
return false;
}
else
{
return true;
}
}
else
{
return true;
}
}
else if(activeNumber==0)//表示还没有任何数据库链接
{
return true;
}
return true;
}
/*
* 加载配置文件中对数据库链接池的配置信息
*/
private synchronized void LoadConfigureInfo()throws Exception
{
ReadConfigureInfo configureInfo= new ReadConfigureInfo();//创建读取配置文件的对象
String maxActiveConfigure=configureInfo.getProperty("maxActive");
String maxIdleConfigure=configureInfo.getProperty("maxIdle");
String maxWaitConfigure=configureInfo.getProperty("maxWait");
CheckMaxActiveConfigure(maxActiveConfigure);//给最大链接数赋值
CheckMaxIdleConfigure(maxIdleConfigure);//给最大空闲数赋值
CheckMaxWaitConfigure(maxWaitConfigure);//给最大的空闲等待时间赋值
}
/*
* 加载数据库链接池的信息
*/
private synchronized void LoadDataPoolInfo()throws Exception
{
this.ActiveNumber=DataQueue.Instance().getActivNumber();//获取连接池中处于活动状态的链接数
this.IdleNumber=DataQueue.Instance().getIdleNumber();//获取链接池中处于空闲状态的链接数
}
... ... ... ... ...
}
/
上面是数据库链接池管理类,他的任务是对链接池的初始化,根据配置信息创建与销毁链接对象。
=== GetConnection.java ===
package myDataPool;
import java.sql.*;
/**
* 获取数据库链接实例的类,此处用到了单态模式
*/
public class GetConnection {
private static GetConnection con;
private DataQueueManage manage;
/**
* 私有的构造函数
*/
private GetConnection()throws Exception{
manage=DataQueueManage.Instance();//创建数据库链接池管理类的实例
}
/**
* 返回本类实例的静态方法
*/
public static GetConnection Instance()throws Exception
{
if(con==null)
{
con=new GetConnection();
}
return con;
}
/**
* 获取数据库链接对象的实例的方法
*/
public synchronized MyConnection GetConnectionInstance()throws Exception
{
manage.CheckPool();//先检查数据库链接池的状态
if(manage.CheckActiveStatic(DataQueue.Instance().getActivNumber()))//判断是否启用获取数据库链接,如果数据库链接超出最大范围,将禁用获取链接
{
//notify();
return DataQueue.Instance().get();//从数据库链接池中获取数据库的链接
}
else//表示链接数已经满,进入等待状态
{
Thread thread=Thread.currentThread();
thread.sleep(3000);//当前线程挂起3秒
System.out.println("表示链接数已经满,进入等待状态,等待三秒后再次链接");
//wait();
return GetConnectionInstance();
}
}
/**
* 返回数据库链接对象的实例的方法
*/
public synchronized void BacktrackConnectionInstance(Connection con)throws Exception
{
try{
MyConnection conn=(MyConnection)con;
conn.setLastAccessTime(System.currentTimeMillis());//从新设置数据库连接对象的最后激活时间
DataQueue.Instance().put(conn);//将数据库链接对象返回链接池
manage.CheckPool();//再次检查数据库链接池的状态
// if(manage.CheckActiveStatic(DataQueue.Instance().getActivNumber()))//判断是否启用获取数据库链接,如果数据库链接超出最大范围,将禁用获取链接
// {
// notify();
// }
}
catch(ClassCastException ex)//类型转换异常
{
throw new MyException("返回数据库链接对象不能在外部创建");//参数不匹配必须是MyConnection对象
}
}
/*
* 销毁数据库链接池的方法,当整个系统关闭时,运行此方法
*/
public synchronized void DestroyDataPool()throws Exception
{
DataQueueManage.Instance().StopCheckPool();//停止数据库链接池的检查
DataQueue.Instance().DestroyPool();//清空链接池中的数据
manage=null;
con=null;
System.gc();//立即进行垃圾回收,将没有引用的对象进行垃圾处理
System.out.println("释放资源!");
}
}
上面这个类中的三个方法就是给外部用的,这里设计成接口比较好,由于考虑到我这个程序只是一个项目单个数据库,所以也就没必要了,如果有多数据库,恩,那得做成抽象工厂才行。
最后说一下我设计的不是很满意的地方,就是 “表示链接数已经满,进入等待状态”时我打算用wait() 与 notify(),但这样的话,还得启用新的线程,这样并不是合理,最后用了递归调用,觉得这样也不是很对,有哪位高人指点一二也好呀!