以前公司开发的一个项目使用的MySql数据库,用的是连接池的方式连接数据库,然后发现一个问题,如果一段时间不登陆该系统 ,下一次登陆的时候就会报“用户名或密码错误”,接着再次登陆就不会有问题了,而第一次登陆的用户名和密码肯定是正确的,之前的技术人员也一直没有找到出现这个问题的原因。我到这个公司之后,经理让我看看代码能不能解决这个问题,我当时怀疑是不是登陆跳转页面中有什么问题呢?可是看了几天也没有什么问题啊,最后我想到是不是数据库的原因呢?可是我对数据库也不是很熟悉,然后就问了一个赛迪的网友,经过他的提示和在网上查的资料,知道了原因和解决方法,原来是当MySql数据库连接空闲一定时间后,服务器就会断开等待超时的连接,默认时间是8小时,当我修改了连接池的连接方式,使用了DBCP连接方式,设置几个参数,这样,每次访问数据库的时候都会先检查连接是否有效,如果无效则建立有效连接,这样就不会出现我之前说的那个问题了,啰啰嗦嗦的说了一大堆,其实是个很简单的问题,只是当时我对数据库不了解才一直都找不到原因。

  下面是我当时参考的一篇文章和自己总结的一些需要注意的问题:
数据库连接池的好处是不言而喻的,现在大部分的application server都提供自己的数据库连接池方案,此时,只要按照application server的文档说明,正确配置,即可在应用中享受到数据库连接池的好处。
但是,有些时候,我们的应用是个独立的java application,并不是普通的WEB/J2EE应用,而且是单独运行的,不要什么application server的配合,这种情况下,我们就需要建立自己的数据库连接池方案了。这里,介绍如何利用apache的dbcp来建立为民自己的数据库连接池。

1、首先,下载必须的jar包
dbcp包,目前版本是1.2.1:http://jakarta.apache.org/commons/dbcp/
pool包,目前版本是1.3:http://jakarta.apache.org/commons/pool/,
如果下载的pool包是1.2的版本,还要下载common-collections包:http://jakarta.apache.org/commons/collections/
在建立我们自己的数据库连接池时,可以使用xml文件来传入需要的参数,这里只使用hard code的方式来简单介绍,所有需要我们自己写的代码很少,只要建立一个文件如下:

import org.apache.commons.dbcp.BasicDataSource; 
import org.apache.commons.dbcp.BasicDataSourceFactory; 

import java.sql.SQLException; 
import java.sql.Connection; 
import java.util.Properties; 

public class ConnectionSource { 
private static BasicDataSource dataSource = null; 

public ConnectionSource() { 
} 

public static void init() { 

if (dataSource != null) { 
try { 
dataSource.close(); 
} catch (Exception e) { 
// 
} 
dataSource = null; 
} 

try { 
Properties p = new Properties(); 
p.setProperty("driverClassName", "oracle.jdbc.driver.OracleDriver"); 
p.setProperty("url", "jdbc:oracle:thin:@192.168.0.1:1521:testDB"); 
p.setProperty("password", "scott"); 
p.setProperty("username", "tiger"); 
p.setProperty("maxActive", "30"); 
p.setProperty("maxIdle", "10"); 
p.setProperty("maxWait", "1000"); 
p.setProperty("removeAbandoned", "false"); 
p.setProperty("removeAbandonedTimeout", "120"); 
p.setProperty("testOnBorrow", "true"); 
p.setProperty("logAbandoned", "true"); 

dataSource = (BasicDataSource) BasicDataSourceFactory.createDataSource(p); 

} catch (Exception e) { 
// 
} 
} 


public static synchronized Connection getConnection() throws SQLException { 
if (dataSource == null) { 
init(); 
} 
Connection conn = null; 
if (dataSource != null) { 
conn = dataSource.getConnection(); 
} 
return conn; 
} 
}



  接下来,在我们的应用中,只要简单地使用ConnectionSource.getConnection()就可以取得连接池中的数据库连接,享受数据库连接带给我们的好处了。当我们使用完取得的数据库连接后,只要简单地使用connection.close()就可把此连接返回到连接池中,至于为什么不是直接关闭此连接,而是返回给连接池,这是因为dbcp使用委派模型来实现Connection接口了。

  在使用Properties来创建BasicDataSource时,有很多参数可以设置,比较重要的还有:

  testOnBorrow、testOnReturn、testWhileIdle,他们的意思是当是取得连接、返回连接或连接空闲时是否进行有效性验证(即是否还和数据库连通的),默认都为false。所以当数据库连接因为某种原因断掉后,再从连接池中取得的连接,实际上可能是无效的连接了,所以,为了确保取得的连接是有效的,可以把把这些属性设为true。当进行校验时,需要另一个参数:validationQuery,对oracle来说,可以是:SELECT COUNT(*) FROM DUAL,实际上就是个简单的SQL语句,验证时,就是把这个SQL语句在数据库上跑一下而已,如果连接正常的,当然就有结果返回了。

  还有2个参数:timeBetweenEvictionRunsMillis 和 minEvictableIdleTimeMillis,他们两个配合,可以持续更新连接池中的连接对象,当timeBetweenEvictionRunsMillis 大于0时,每过timeBetweenEvictionRunsMillis 时间,就会启动一个线程,校验连接池中闲置时间超过minEvictableIdleTimeMillis的连接对象。
还有其他的一些参数,可以参考源代码。
部分参数简要说明:
removeAbandoned :是否自动回收超时连接
removeAbandonedTimeout:超时时间(以秒数为单位)
removeAbandoned=true那么在getNumActive()快要到getMaxActive()的时候,系统会进行无效的Connection的回收,回收的Connection为removeAbandonedTimeout(默认300秒)中设置的秒数后没有使用的Connection
logAbandoned:logAbandoned=true的话,将会在回收事件后,在log中打印出回收Connection的错误信息,包括在哪个地方用了Connection却忘记关闭了,在调试的时候很有用。
maxWait:超时等待时间以毫秒为单位
maxIdle:最大空闲连接
minIdle:最小空闲连接
maxActive:最大连接数
testOnBorrow、testOnReturn、testWhileIdle、validationQuery:上面有介绍
另外很重要的一点是每次连接使用完了不要忘了调用connection.close()使连接返回到连接池