数据库链接池

来源:互联网 发布:2017正能量网络用语 编辑:程序博客网 时间:2024/05/18 02:11

     自己尝试着去写了个数据库链接池,是用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(),但这样的话,还得启用新的线程,这样并不是合理,最后用了递归调用,觉得这样也不是很对,有哪位高人指点一二也好呀!


 

 

 

 

 


 

 



 

原创粉丝点击