1、连接池简介
通常情况下,在每次访问数据库之前都要先建立与数据库的连接,这将消耗一定的资源,并延长了访问数据库的时间,如果是访问量相对较低的系统还可以,如果访问量较高,将严重影响系统的性能。为了解决这一问题,引入了连接池的概念。所谓连接池,就是预先建立好一定数量的数据库连接,模拟存放在一个连接池中,由连接池负责对这些数据库连接进行管理。这样,当需要访问数据库时,就可以通过已经建立好的连接访问数据库了,从而免去了每次在访问数据库之前建立数据库连接的开销。
连接池还解决了数据库连接数量限制的问题。由于数据库能够承受的连接数量是有限的,当达到一定程度时,数据库的性能就会下降,甚至崩溃,而池化管理机制,通过有效地使用和调度这些连接池中的连接,则解决了这个问题(在这里我们不讨论连接池对连接数量限制的问题)。
2、数据库连接池的具体实施办法
(1)预先创建一定数量的连接,存放在空闲连接池中(存放已经创建但并未分配出去的连接,按照创建时间大小放在一个容器);
(2)用户请求连接时会看连接池内是否还有空闲连接,如果有空闲连接,判断该连接是否有效,如果是无效的连接,则把它移出连接池,重新检测是否还有其他空闲的连接;如果没有剩余空闲的连接,检查当前所开的连接池数是否达到连接池所允许的最大数,如果没有达到就分配一个新的连接,如果达到了就等待一段时间;在等待的时间内,任何连接被释放出来后都可以把这个连接分配给等待的客户,如果等待时间超过预定的时间则返回null值;注意:系统对已经分配出去了的正在使用的连接只做计数;
(3)当程序使用完连接后,该连接将重新回到连接池中,而不是直接将连接释放;
(4)当连接池中的空闲连接数量低于下限时,连接池将根据管理机制追加创建一定数量的连接;当空闲连接数量高于上限时,连接池将释放一定数量的连接;
(5)注意:在每次用完Connection后,要及时调用Connection对象的close()或dispose()方法显式关闭连接,以便连接可以及时返回到连接池中,非显式关闭的连接可能不会添加或返回到池中。
3、多数据库服务器和多用户
对于很多企业来说,可能要访问不同的数据库,例如Oracle与SqlServer如何连接不同的数据库?
采取措施:
(1)设计一个符合单例模式的连接池管理类.
(2)在连接池管理类唯一实例被创建的时候去读取一个资源文件,资源文件里面记录了相应的数据库信息,例如url,user,password等信息.
(3)创建多个连接池的实例,每一个实例都是一个特定数据库的连接池,连接池管理类实例,为每个连接池取一个名字,通过不同的名字来管理不同的连接池.
(4)对于同一个数据库有多个用户使用不同的名称和密码访问的情况,也可以通过资源文件去处理,即使用同一个url使用不同的用户名和密码的数据库连接信息
4、连接池的配置与维护
(1)设置最小连接数和最大连接数,如果设置过多,系统启动就慢,访问速度快。设置过少的话则启动快访问的时候慢。
(2)开发的时候设置较小的最小连接数,这样保证开发起来较快。系统实际使用时,设置较大的连接数保证访问速度。
(3)最大连接数具体值要看系统的访问量.要经过不断测试取一个平衡值
(4)隔一段时间对连接池进行检测,发现小于最小连接数的则补充相应数量的新连接
连接池的配置参数
属 性 名 称 | 说 明 |
name | 设置数据源的JNDI名 |
type | 设置数据源的类型 |
auth | 设置数据源的管理者,有两个可选值Container和Application,Container表示由容器来创建和管理数据源,Application表示由Web应用来创建和管理数据源 |
driverClassName | 设置连接数据库的JDBC驱动程序 |
url | 设置连接数据库的路径 |
username | 设置连接数据库的用户名 |
password | 设置连接数据库的密码 |
maxActive | 设置连接池中处于活动状态的数据库连接的最大数目,0表示不受限制 |
maxIdle | 设置连接池中处于空闲状态的数据库连接的最大数目,0表示不受限制 |
maxWait | 设置当连接池中没有处于空闲状态的连接时,请求数据库连接的请求的最长等待时间(单位为ms),如果超出该时间将抛出异常,−1表示无限期等待 |
5、连接池的优缺点
连接池具有下列优点:
(1)创建一个新的数据库连接所耗费的时间主要取决于网络的速度以及应用程序和数据库服务器的(网络)距离,而且这个过程通常是一个很耗时的过程,而采用数据库连接池后,数据库连接请求则可以直接通过连接池满足,而不需要为该请求重新连接、认证到数据库服务器,从而节省了时间;
(2)提高了数据库连接的重复使用率;
(3)解决了数据库对连接数量的限制。
与此同时,连接池具有下列缺点:
(1)连接池中可能存在多个与数据库保持连接但未被使用的连接,在一定程度上浪费了资源;
(2)要求开发人员和使用者准确估算系统需要提供的最大数据库连接的数量。
·连接池设计
1: 数据库连接池类DBconnctionPool
DBConnectionPool提供以下功能:
1) 从连接池获取(或创建)可用连接;
2) 把连接返回给连接池;
3) 在系统关闭时释放所有资源,关闭所有连接。
此外, DBConnectionPool类还能够处理无效连接(原来登记为可用的连接,由于某种原因不再可用,如超时,通讯问题),并能够限制连接池中的连接总数不超过某个预定值。
属性:
inuserd 当前正在使用的连接数(int 类型)
maxConn 最大的连接数(int 类型)
minConn 最小的连接数(int 类型)
poolName 连接池的名称(String 类型)
ArrayList freeConnections=new ArrayList() (容器,用来装空闲的连接)
username 用户名(String类型)
password 密码(String 类型)
url 连接的地址(String 类型)
driver 驱动(String类型)
方法:
1.构造器
public DBconnctionPool(int maxConn, String username, String password,
String url, String driver) {
super();
this.maxConn = maxConn;
this.username = username;
this.password = password;
this.url = url;
this.driver = driver;
}
2.创建新连接的方法
public Connection createConnection() {
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, username, password);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("sorry,can't find the driver");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("can't connect...");
}
return conn;
}
3.释放所有空闲连接的方法
public synchronized void release(){
Iterator allconns=freeConnections.iterator();
while(allconns.hasNext()){
Connection conn=(Connection) allconns.next();
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//移除集合里的所有元素
freeConnections.clear();
}
4.从连接池申请连接的方法
/**
* 从连接池获得一个可用连接.如没有空闲的连接且当前连接数小于最大连接
* 数限制,则创建新连接.如原来登记为可用的连接不再有效,则从向量删除之,
* 然后递归调用自己以尝试新的可用连接.
*/
public synchronized Connection getConnection()
{
Connection con=null;
if(this.freeConnections.size()>0)
{
con=(Connection)this.freeConnections.get(0);
this.freeConnections.remove(0);//如果连接分配出去了,就从空闲连接里删除
if(con==null)con=getConnection(); //继续获得连接
}
else
{
con=newConnection(); //新建连接
}
if(this.maxConn==0||this.maxConn<this.inUsed)
{
con=null;//等待 超过最大连接时
}
if(con!=null)
{
this.inUsed++;
System.out.println("得到 "+this.name+" 的连接,现有"+inUsed+"个连接在使用!");
}
return con;
}
/**
* 从连接池获取可用连接.可以指定客户程序能够等待的最长时间
* 参见前一个getConnection()方法.
*
* @param timeout 以毫秒计的等待时间限制
*/
public synchronized Connection getConnection(long timeout) {
long startTime = new Date().getTime();
Connection con;
while ((con = getConnection()) == null) {
try {
wait(timeout);
}
catch (InterruptedException e) {}
if ((new Date().getTime() - startTime) >= timeout) {
// wait()返回的原因是超时
return null;
}
}
return con;
}
5.用完释放连接的方法
public synchronized void freeConnection(Connection conn){
freeConnections.add(conn);
inuserd--;
}
2: 数据库连接池管理类DBConnectionManager
管理类DBConnectionManager用于管理多个连接池对象,它提供以下功能:
1) 装载和注册JDBC驱动程序。
2) 根据在属性文件中定义的属性创建连接池对象。
3) 实现连接池名字与其实例之间的映射。
4) 跟踪客户程序对连接池的引用,保证在最后一个客户程序结束时安全地关闭所有连接池。
属性:
public int clients;//客户的连接数
static private DBconnectionManager instance;//唯一的连接池管理类实例
HashMap<String,DBconnctionPool>connectPools=newHashMap<String,DBconnctionPool>();
ArrayList<DsconfigBean> drivers=new ArrayList<DsconfigBean>();//bean类集合
方法:
1. 利用单例模式保证取得唯一的连接池管理类实例
static public DBconnectionManager getInstance(){
if(instance==null){
instance=new DBconnectionManager();
}
clients++;
return instance;
}
2. 建构函数私有以防止其它对象创建本类实例
private DBConnectionManager() {
init();
}
3. 创建连接池
public void createPool(DsconfigBean bean){
DBconnctionPool pool=new DBconnctionPool();
System.out.println(bean.getName());
pool.setDriver(bean.getDriver());
pool.setMaxConn(bean.getMaxCount());
pool.setPassword(bean.getPassword());
pool.setPoolName(bean.getName());
pool.setUrl(bean.getUrl());
pool.setUsername(bean.getUsername());
connectPools.put(bean.getName(), pool);
}
3. 加载驱动程序
public void loadDrivers(){
ParseConfig oaConfig=new ParseConfig();
drivers=oaConfig.ReadConfig();
}
4. 初始化连接池参数
1) 调用类读取配置文件
2) 把读取到的有关driver的信息存储在集合drivers里
public void init(){
loadDrivers();
//遍历迭代创建相应的连接池
DsconfigBean myBean=new DsconfigBean();
for(int i=0;i<drivers.size();i++){
myBean=drivers.get(i);
createPool(myBean);
}
}
5. 根据连接池的名字得到一个连接
/**
* 获得一个可用的(空闲的)连接.如果没有可用连接,且已有连接数小于最大连接数
* 限制,则创建并返回新连接
*/
public Connection getConnection(String name){
DBconnctionPool pool=null;
Connection conn=null;
pool=(DBconnctionPool)connectPools.get(name);
conn=pool.getConnection();
return conn;
}
}
/**
* 获得一个可用连接.若没有可用连接,且已有连接数小于最大连接数限制,
* 则创建并返回新连接.否则,在指定的时间内等待其它线程释放连接.
*
*/
public Connection getConnection(String name, long time) {
DBConnectionPool pool = (DBConnectionPool) pools.get(name);
if (pool != null) {
return pool.getConnection(time);
}
return null;
}
6. 将连接对象返回给由名字指定的连接池
public void freeConnection(String name, Connection con) {
DBConnectionPool pool = (DBConnectionPool) pools.get(name);
if (pool != null) {
pool.freeConnection(con);
}
}
7. 关闭所有连接,撤销驱动程序的注册
public synchronized void release() {
// 等待直到最后一个客户程序调用
if (--clients != 0) {
return;
}
Enumeration allPools = pools.elements();
while (allPools.hasMoreElements()) {
DBConnectionPool pool = (DBConnectionPool) allPools.nextElement();
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());
}
}
}
3: 配置文件对应的bean类
public class DsconfigBean {
private String name;//数据库名字
private String type;//连接池类型
private String driver;//连接池驱动
private String url;//连接池地址
private String username;//用户名
private String password;//密码
private int maxCount;//最大连接数
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getMaxCount() {
return maxCount;
}
public void setMaxCount(int maxCount) {
this.maxCount = maxCount;
}
}
4: 测试类DBTest
public class DBTest {
public static void main(String[] args) {
DBconnectionManager manager=DBconnectionManager.getInstance();
manager.init();
Connection connection=manager.getConnection("sqlserver");
System.out.println(connection.toString());
}
}