【Java】java数据库连接中C3P、DBCP、Druid连接池的使用

使用 JDBC 的步骤:
1. 加载数据库驱动
2. 通过 DriverManager 获得数据库连接
3. 通过 Connection 获得 Statement 对象
4. 使用 Statement 执行 SQL 语句。
5. 操作结果集合
6. 释放数据库连接
可以通过谐音来记忆“贾 (加载驱动) 莲(获取连接)预 (获得预编译语句) 执(执行 Sql 语句)事(释放资源)”。
数据库连接的建立及关闭是及耗费系统资源的操作,在多层结构的应用环境中,这种资源的耗费对系统性能影响尤为明显。
通过 DriverManager 获得数据库连接的方式,一个数据库连接对象对应一个物理数据库连接,每次操作都打开一个物理连接,使用完后立即关闭连接。频繁地打开、关闭连接将造成系统性能低下。
数据库连接池的解决方案是,当应用程序启动时候,系统建立足够多的数据库连接,并将这些连接组成一个连接池。每次应用程序请求数据库连接时,无需重新打开连接,而是从连接池中取出已有的连接使用,使用完后不再关闭数据库连接,而是直接将连接归还给连接池,通过使用连接池将大大提高程序的运行效率。
对于资源共享的情况,有一个通用的设计模式:资源池(Resource Pool),用于解决资源频繁请求释放资源的压力。 JDBC 2.0 规范引入了数据库连接池技术。JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口,该接口通常由商用服务器(WebLogic,WebSphere)等提供实现,也有一些开源组织提供实现(如 JDBC 和 C3P0 等)。DataSource 通常被称为数据源,它包含了连接池和连接池管理两个部分,但习惯上已经常把 DataSource 称为连接池。

1.DBCP 数据源

DBCP 是 Apache 软件基金组织下的开源连接池实现,该连接池依赖该组织下的另一个开源系统 common-pool。如果需要使用该连接池实现,则应该在系统中增加如下两个 jar 文件:
commons-dbcp.jar: 连接池的实现
commons-pool.jar: 连接池实现的依赖库
登录 http://commons.apache.org 站点既可下载 commons-pool.zip 和 commons-dbcp.zip 两个压缩软件。Tomcat 的连接池正是采用这种连接池实现的。数据库连接池既可以与应用服务器整合使用,也可以由程序独立使用。

        //创建数据库对象
        BasicDataSource ds=new BasicDataSource();
        //设置数据库的驱动
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        //设置数据库的地址
        ds.setUrl("jdbc:mysql://localhost:3306/javaee");
        //设置连接数据库的用户名
        ds.setUsername("root");
        //设置连接数据库的密码
        ds.setPassword("1234");
        //设置连接池的初始连接数
        ds.setInitialSize(6);
        //设置连接池最多有多少连接数
        ds.setMaxActive(20);
        //设置连接池中最少有两个连接
        ds.setMinIdle(2);

连接源设置成功后,就可以通过如下的代码获得数据库连接:

Connection conn=ds.getConnection();

释放资源

conn.close();

但上面的这行代码并未真正的关闭数据库的物理连接,它仅仅是把数据库连接释放,归还给连接池,让其他客户端可以使用该连接。

2.C3P0 数据源

和 dbcp 相比,C3P0 的性能更胜一筹,Hibernate 就推荐使用该连接池。C3P0 连接池不仅可以自动清理不再使用的 Connection,还可以自动清理 Statement 和 ResultSet。C3P0 连接池需要版本 1.3 以上的 JRE, 推荐使用 1.4 版本以上的 JRE。
如果需要使用 C3P0 数据源,应该添加如下两个 JRE 文件:
c3p0-0.9.1.2.jar: C3P0 连接池的实现
可以的登录 http://sourceforge.net/projects/c3p0/ 站点既可下载 C3P0 数据源的最新版本。

        //创建连接池实例
        ComboPooledDataSource ds=new ComboPooledDataSource();
        //设置数据库的驱动
        ds.setDriverClass("com.mysql.jdbc.Driver");
        //设置数据库的地址
        ds.setJdbcUrl("jdbc:mysql://localhost:3306/javaee");
        //设置连接数据库的用户名
        ds.setUser("root");
        //设置连接数据库的密码
        ds.setPassword("1234");
        //设置连接池的初始连接数
        ds.setInitialPoolSize(6);
        //设置连接池最多有多少连接数
        ds.setMaxPoolSize(40);
        //设置连接池中最少连接数
        ds.setMinPoolSize(2);
        //设置连接池缓存 Statement 的最大数
        ds.setMaxStatements(180);


设置完成后,可以通过下面的代码获得 Connection 连接

Connection conn= ds.getConnection();

 3.Druid 连接池的使用

Druid 作为一名后起之秀,凭借其出色的性能,也逐渐印入了大家的眼帘。接下来本教程就说一下 druid 的简单使用。
首先从 http://repo1.maven.org/maven2/com/alibaba/druid/ 下载最新的 jar 包。如果想使用最新的源码编译,可以从 https://github.com/alibaba/druid 下载源码,然后使用 maven 命令行,或者导入到 eclipse 中进行编译。
和 dbcp 类似,druid 的配置项如下

配置 缺省值 说明
name  

配置这个属性的意义在于,如果存在多个数据源,监控的时候

可以通过名字来区分开来。如果没有配置,将会生成一个名字,

格式是:"DataSource-" + System.identityHashCode(this)

jdbcUrl  

连接数据库的 url,不同数据库不一样。例如:

mysql : jdbc:mysql://10.20.153.104:3306/Test

oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto

username   连接数据库的用户名
password  

连接数据库的密码

如果要使用 ConfigFilter(读取配置文件、数据库加密),

可以查看这篇文章:使用 ConfigFilter

driverClassName 根据 url 自动识别 这一项可配可不配,如果不配置 druid 会根据 url 自动识别 dbType,然后选择相应的 driverClassName
initialSize 0 初始化时建立物理连接的个数。初始化发生在显示调用 init 方法,或者第一次 getConnection 时
maxActive 8 最大连接池数量
maxIdle 8 已经不再使用,配置了也没效果
minIdle   最小连接池数量
maxWait false 是否缓存 preparedStatement,也就是 PSCache。
PSCache 对支持游标的数据库性能提升巨大,比如说 oracle。
在 mysql5.5 以下的版本中没有 PSCache 功能,建议关闭掉。
作者在 5.5 版本中使用 PSCache,通过监控界面发现 PSCache 有缓存命中率记录,该应该是支持 PSCache。
maxOpenPreparedStatements   用来检测连接是否有效的 sql,要求是一个查询语句。如果 validationQuery 为 null,testOnBorrow、testOnReturn,testWhileIdle 都不会其作用。
testOnBorrow true 申请连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。
testOnReturn false 归还连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能
testWhileIdle false 建议配置为 true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis, 执行 validationQuery 检测连接是否有效。
timeBetweenEvictionRunsMillis   有两个含义: 
1) Destroy 线程会检测连接的间隔时间 
2) testWhileIdle 的判断依据,详细看 testWhileIdle 属性的说明
numTestsPerEvictionRun   不再使用,一个 DruidDataSource 只支持一个 EvictionRun
minEvictableIdleTimeMillis    
connectionInitSqls   物理连接初始化的时候执行的 sql
exceptionSorter 根据 dbType 自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接
filters   属性类型是字符串,通过别名的方式配置扩展插件, 常用的插件有: 
监控统计用的 filter:stat  
日志用的 filter:log4j 
防御 sql 注入的 filter:wall
proxyFilters   类型是 List<com.alibaba.druid.filter.Filter>, 
如果同时配置了 filters 和 proxyFilters, 
是组合关系,并非替换关系

将下载好的 druid.jar 包导入项目中,或是使用 Maven 工具管理。

Application.xml 文件

复制代码
    <bean name="transactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" >     
        <property name="dataSource" ref ="dataSource"></property >  
     </bean>
<span style="color: rgba(0, 128, 0, 1)">&lt;!--<span style="color: rgba(0, 128, 0, 1)"> 获取properties配置文件 <span style="color: rgba(0, 128, 0, 1)">--&gt;
<span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">bean <span style="color: rgba(255, 0, 0, 1)">id<span style="color: rgba(0, 0, 255, 1)">="propertyConfigurer"<span style="color: rgba(255, 0, 0, 1)"> class<span style="color: rgba(0, 0, 255, 1)">="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"<span style="color: rgba(0, 0, 255, 1)">&gt;
    <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">property <span style="color: rgba(255, 0, 0, 1)">name<span style="color: rgba(0, 0, 255, 1)">="locations"<span style="color: rgba(0, 0, 255, 1)">&gt;
        <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">list<span style="color: rgba(0, 0, 255, 1)">&gt;
            <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">value<span style="color: rgba(0, 0, 255, 1)">&gt;classpath:db-config.properties<span style="color: rgba(0, 0, 255, 1)">&lt;/<span style="color: rgba(128, 0, 0, 1)">value<span style="color: rgba(0, 0, 255, 1)">&gt;
        <span style="color: rgba(0, 0, 255, 1)">&lt;/<span style="color: rgba(128, 0, 0, 1)">list<span style="color: rgba(0, 0, 255, 1)">&gt;
    <span style="color: rgba(0, 0, 255, 1)">&lt;/<span style="color: rgba(128, 0, 0, 1)">property<span style="color: rgba(0, 0, 255, 1)">&gt;
<span style="color: rgba(0, 0, 255, 1)">&lt;/<span style="color: rgba(128, 0, 0, 1)">bean<span style="color: rgba(0, 0, 255, 1)">&gt;

<span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">bean <span style="color: rgba(255, 0, 0, 1)">id<span style="color: rgba(0, 0, 255, 1)">="dataSource"<span style="color: rgba(255, 0, 0, 1)"> class<span style="color: rgba(0, 0, 255, 1)">="com.alibaba.druid.pool.DruidDataSource"<span style="color: rgba(255, 0, 0, 1)"> destroy-method<span style="color: rgba(0, 0, 255, 1)">="close"<span style="color: rgba(0, 0, 255, 1)">&gt;    
   <span style="color: rgba(0, 128, 0, 1)">&lt;!--<span style="color: rgba(0, 128, 0, 1)"> 数据库基本信息配置 <span style="color: rgba(0, 128, 0, 1)">--&gt;  
   <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">property <span style="color: rgba(255, 0, 0, 1)">name <span style="color: rgba(0, 0, 255, 1)">= "url"<span style="color: rgba(255, 0, 0, 1)"> value <span style="color: rgba(0, 0, 255, 1)">= "${url}"<span style="color: rgba(0, 0, 255, 1)">/&gt;    
   <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">property <span style="color: rgba(255, 0, 0, 1)">name <span style="color: rgba(0, 0, 255, 1)">= "username"<span style="color: rgba(255, 0, 0, 1)"> value <span style="color: rgba(0, 0, 255, 1)">= "${username}"<span style="color: rgba(0, 0, 255, 1)">/&gt;    
   <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">property <span style="color: rgba(255, 0, 0, 1)">name <span style="color: rgba(0, 0, 255, 1)">= "password"<span style="color: rgba(255, 0, 0, 1)"> value <span style="color: rgba(0, 0, 255, 1)">= "${password}" <span style="color: rgba(0, 0, 255, 1)">/&gt;    
   <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">property <span style="color: rgba(255, 0, 0, 1)">name <span style="color: rgba(0, 0, 255, 1)">= "driverClassName"<span style="color: rgba(255, 0, 0, 1)"> value <span style="color: rgba(0, 0, 255, 1)">= "${driverClassName}" <span style="color: rgba(0, 0, 255, 1)">/&gt;    
   <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">property <span style="color: rgba(255, 0, 0, 1)">name <span style="color: rgba(0, 0, 255, 1)">= "filters"<span style="color: rgba(255, 0, 0, 1)"> value <span style="color: rgba(0, 0, 255, 1)">= "${filters}" <span style="color: rgba(0, 0, 255, 1)">/&gt;    
    <span style="color: rgba(0, 128, 0, 1)">&lt;!--<span style="color: rgba(0, 128, 0, 1)"> 最大并发连接数 <span style="color: rgba(0, 128, 0, 1)">--&gt;  
   <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">property <span style="color: rgba(255, 0, 0, 1)">name <span style="color: rgba(0, 0, 255, 1)">= "maxActive"<span style="color: rgba(255, 0, 0, 1)"> value <span style="color: rgba(0, 0, 255, 1)">= "${maxActive}" <span style="color: rgba(0, 0, 255, 1)">/&gt;  
   <span style="color: rgba(0, 128, 0, 1)">&lt;!--<span style="color: rgba(0, 128, 0, 1)"> 初始化连接数量 <span style="color: rgba(0, 128, 0, 1)">--&gt;  
   <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">property <span style="color: rgba(255, 0, 0, 1)">name <span style="color: rgba(0, 0, 255, 1)">= "initialSize"<span style="color: rgba(255, 0, 0, 1)"> value <span style="color: rgba(0, 0, 255, 1)">= "${initialSize}" <span style="color: rgba(0, 0, 255, 1)">/&gt;  
   <span style="color: rgba(0, 128, 0, 1)">&lt;!--<span style="color: rgba(0, 128, 0, 1)"> 配置获取连接等待超时的时间 <span style="color: rgba(0, 128, 0, 1)">--&gt;  
   <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">property <span style="color: rgba(255, 0, 0, 1)">name <span style="color: rgba(0, 0, 255, 1)">= "maxWait"<span style="color: rgba(255, 0, 0, 1)"> value <span style="color: rgba(0, 0, 255, 1)">= "${maxWait}" <span style="color: rgba(0, 0, 255, 1)">/&gt;  
   <span style="color: rgba(0, 128, 0, 1)">&lt;!--<span style="color: rgba(0, 128, 0, 1)"> 最小空闲连接数 <span style="color: rgba(0, 128, 0, 1)">--&gt;  
   <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">property <span style="color: rgba(255, 0, 0, 1)">name <span style="color: rgba(0, 0, 255, 1)">= "minIdle"<span style="color: rgba(255, 0, 0, 1)"> value <span style="color: rgba(0, 0, 255, 1)">= "${minIdle}" <span style="color: rgba(0, 0, 255, 1)">/&gt;    
   <span style="color: rgba(0, 128, 0, 1)">&lt;!--<span style="color: rgba(0, 128, 0, 1)"> 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 <span style="color: rgba(0, 128, 0, 1)">--&gt;  
   <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">property <span style="color: rgba(255, 0, 0, 1)">name <span style="color: rgba(0, 0, 255, 1)">= "timeBetweenEvictionRunsMillis"<span style="color: rgba(255, 0, 0, 1)"> value <span style="color: rgba(0, 0, 255, 1)">="${timeBetweenEvictionRunsMillis}" <span style="color: rgba(0, 0, 255, 1)">/&gt;  
   <span style="color: rgba(0, 128, 0, 1)">&lt;!--<span style="color: rgba(0, 128, 0, 1)"> 配置一个连接在池中最小生存的时间,单位是毫秒 <span style="color: rgba(0, 128, 0, 1)">--&gt;  
   <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">property <span style="color: rgba(255, 0, 0, 1)">name <span style="color: rgba(0, 0, 255, 1)">= "minEvictableIdleTimeMillis"<span style="color: rgba(255, 0, 0, 1)"> value <span style="color: rgba(0, 0, 255, 1)">="${minEvictableIdleTimeMillis}" <span style="color: rgba(0, 0, 255, 1)">/&gt;    
   <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">property <span style="color: rgba(255, 0, 0, 1)">name <span style="color: rgba(0, 0, 255, 1)">= "validationQuery"<span style="color: rgba(255, 0, 0, 1)"> value <span style="color: rgba(0, 0, 255, 1)">= "${validationQuery}" <span style="color: rgba(0, 0, 255, 1)">/&gt;    
   <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">property <span style="color: rgba(255, 0, 0, 1)">name <span style="color: rgba(0, 0, 255, 1)">= "testWhileIdle"<span style="color: rgba(255, 0, 0, 1)"> value <span style="color: rgba(0, 0, 255, 1)">= "${testWhileIdle}" <span style="color: rgba(0, 0, 255, 1)">/&gt;    
   <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">property <span style="color: rgba(255, 0, 0, 1)">name <span style="color: rgba(0, 0, 255, 1)">= "testOnBorrow"<span style="color: rgba(255, 0, 0, 1)"> value <span style="color: rgba(0, 0, 255, 1)">= "${testOnBorrow}" <span style="color: rgba(0, 0, 255, 1)">/&gt;    
   <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">property <span style="color: rgba(255, 0, 0, 1)">name <span style="color: rgba(0, 0, 255, 1)">= "testOnReturn"<span style="color: rgba(255, 0, 0, 1)"> value <span style="color: rgba(0, 0, 255, 1)">= "${testOnReturn}" <span style="color: rgba(0, 0, 255, 1)">/&gt;    
   <span style="color: rgba(0, 0, 255, 1)">&lt;<span style="color: rgba(128, 0, 0, 1)">property <span style="color: rgba(255, 0, 0, 1)">name <span style="color: rgba(0, 0, 255, 1)">= "maxOpenPreparedStatements"<span style="color: rgba(255, 0, 0, 1)"> value <span style="color: rgba(0, 0, 255, 1)">="${maxOpenPreparedStatements}" <span style="color: rgba(0, 0, 255, 1)">/&gt;  
   <span style="color: rgba(0, 128, 0, 1)">&lt;!--<span style="color: rgba(0, 128, 0, 1)"> 打开 removeAbandoned 功能 <span style="color: rgba(0, 128, 0, 1)">--&gt;  
   <span style="color: rgba(0, 0, 255, 1)">&lt;property name = "removeAbandoned" value = "${removeAbandoned}" /&gt;  
   &lt;!-- 1800 秒,也就是 30 分钟 --&gt;  
   &lt;property name = "removeAbandonedTimeout" value ="${removeAbandonedTimeout}" /&gt;  
   &lt;!-- 关闭 abanded 连接时输出错误日志 --&gt;     
   &lt;property name = "logAbandoned" value = "${logAbandoned}" /&gt;  

</bean>

复制代码

db-config.properties 文件在 src 目录下面:

复制代码
url:jdbc:mysql:///huitougo?characterEncoding=utf8&useSSL=true&serverTimezone=GMT
driverClassName:com.mysql.cj.jdbc.Driver
username:root
password:517839
filters:stat
maxActive:20
initialSize:1
maxWait:60000
minIdle:10
maxIdle:15
timeBetweenEvictionRunsMillis:60000 
minEvictableIdleTimeMillis:300000
validationQuery:SELECT 'x'
testWhileIdle:true
testOnBorrow:false
testOnReturn:false
maxOpenPreparedStatements:20
removeAbandoned:true
removeAbandonedTimeout:1800
logAbandoned:true
复制代码

web.xml 文件:

复制代码
    <!-- 连接池 启用 Web 监控统计功能    start-->  
    <filter>  
        <filter-name>DruidWebStatFilter </filter-name>  
        <filter-class>com.alibaba.druid.support.http.WebStatFilter </filter-class>  
        <init-param>  
            <param-name>exclusions</param-name> 
            <param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value >  
        </init-param>
    </filter>  
    <filter-mapping>  
        <filter-name>DruidWebStatFilter</filter-name >  
        <url-pattern >/*</url-pattern> 
    </filter-mapping>
    <servlet>
        <servlet-name>DruidStatView</servlet-name>  
        <servlet-class>com.alibaba.druid.support.http.StatViewServlet </servlet-class>  
    </servlet>
    <servlet-mapping>  
        <servlet-name>DruidStatView</servlet-name >  
        <url-pattern>/druid/*</url-pattern >  
    </servlet-mapping>
     <!-- 连接池 启用 Web 监控统计功能    end--> 
复制代码

访问监控页面: http://ip:port/projectName/druid/index.html