
佛说:前世 500 次的回眸,才换来今生的一次擦肩而过。
人与人之间的缘分,真的无需强求,并不是所有的感情都能天长地久,正如《越过山丘》里唱的:总有人幸福白头,
总有人哭着分手,无论相遇还是不相遇,都是献给岁月的序曲人生。
这场长途跋涉的旅程中,有花繁盛开的世外桃源,也有遍地荒芜的戈壁沙漠,有注定的失去,也有没有结果的相遇,
遇见谁发生怎样的故事,大体都是缘分,有时候,有些事不用开口也明白。
有时候,有些路不走也会变长,在一起的时候,就用力爱,不留遗憾。
如果无法继续携手前行,缘分到头的时候,就痛快的说一声再见,他们只是你人生的过客,在你的生活当中,
陪你做过一段路,共同经历一些风景,然后经过时间的节点,在某个不经意间,又演了一场离别,是一场结束,
却是一场开始,放手才能感受解脱,失去了才懂得珍惜,我们要悄然收起过往的心酸,继续自己的人生。
有些人走进你的生命,只是为了证明你们没有缘分,不需说再见。
愿你得到而不喜,失而不忧。所有失去的,都会以另一种方式归来。
—————— 一禅心灵庙语
在使用开发基于数据库的 web 程序时,传统的模式基本是按以下步骤执行的
如下图示:

传统方式的连接数据库,存在的问题:
这里我们演示使用传统的数据库连接池,将向 Mysql数据库请求 5000 次的连接,看看会发生什么事情 ???
包如下错误: java.sql.SQLNonTransientConnectionException: Data source rejected establishment of connection, message from server: "Too many connections"
package Blogs.blogs04;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class JDBCTest {
public static void main(String[] args) {
// 读取配置文件信息
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties"); // 默认读取的是src目录下的文件
try {
Properties properties = new Properties(); // 创建读取.properties后缀的配置文件的对象
properties.load(is); // 以简单的线性格式输入字符读取属性列表(关键字/元素对)
// 通过对象传入关键字获取到其中的信息
String driverClass = properties.getProperty("driverClass");
String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
// 3. 加载/注册驱动
Class.forName(driverClass);
// 测试连接 5000 次
for(int i = 0; i < 5000; i ++) {
// 连接数据库
Connection connection = DriverManager.getConnection(url,user,password);
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

为此,为了解决传统开发中的数据库连接问题,基本上也就是上述提到的三个问题,可以采用 数据库连接池技术


工作原理:

这里我们使用一个生动形象的例子来说明传统 JDBC 数据库连接,与使用数据库连接池的连接:
比如我们要从上海 ——> 北京的天安门:

由于数据库连接得以重用 ,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的 平稳性 .
数据库连接池在初始化过程中,往往已经创建了若干个数据库连接存放于连接池 中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用的连接,避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间。
对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置,实现某一应用最大可用数据库连接数的限制,避免某一应用独占所有的数据库资源。
在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用的连接,从而避免了常规数据库连接操作中可能出现的资源的泄漏问题。
JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口,该接口通常由服务器(Weblogic,WebSphere,Tomcat) 提供实现,也有一些开源组织提供实现了:如下
DBCP 是Apache 提供的数据库连接池。tomcat 服务器自带 dbcp 数据库连接池,速度比 c3p0 较快,但因自身存在 BUG,Hibernate3 已不再提供支持了C3P0 是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以,Hibernate 官方推荐使用Proxool 是 sourceforge 下的一个开源项目数据库连接池,有监控连接状态的功能,稳定性较 C3P0 差一点。BoneCP 是一个开源组织提供的数据库连接池,速度快Druid 是阿里提供的数据库连接池,据说集 DBCP,C3P0,Proxool 优点于一身的数据库连接池,但是速度不确定是否有 BoneCP 快。市面上大多都是使用 该数据库连接池DataSource 通常被称为数据源,是在javax.sql.DataSource包下的一个接口。它包含连接池和管理连接池两部分,习惯上也经常把 DataSource 称为连接池
DataSource 用来取代 DriverManager 来获取 Connection 连接,获取速度快,同时可以大幅度提高数据库访问速度。
特别注意:
DataSource数据源(数据库连接池)和数据库连接不同,数据库连接池不需要创建多个,我们可以把它比作是生产数据库连接 的工厂,当我们的连接数量不够时,可以通过配置增加数据库连接池中的存储的连接,因此整个应用只需要一个数据库连接池(数据源)即可 。创建过多的数据库连接池,反而会适得其反。connection.close(),不过使用数据库连接池不同的是,这里仅仅是把数据库连接 归还 数据库连接池,并不是真正关闭对数据库的连接。这个连接还在。虽然它们使用都是 Connection.close() 方法,但是所在的包路径是不同的,因为实现方式是不同的。在’com.mysql.cj.jdbc.ConnectionImpl’包中的是真正的关闭与数据库的连接,而在``包下是 归还 连接给数据库连接池。
javax.sql.DataSource 包下的 DataSource 接口的文档:


C3P0 是第三方的封装的数据库连接池,所以我们需要导入相关的 c3p0-0.9.1.2-jdk1.3.jar 包才可以使用:如下

相关的jar 文件,我已经提交到 Github ,Gitee 当中的了,如有需要的可以自行获取。
这里我使用的是 IDEA 编辑器,下面为我们的项目中导入 c3p0-0.9.1.2-jdk1.3.jar 包,操作如下:






这里就导入jar包成功了,后面的 DBCP,Druid 同样也是需要这样导入相关的 jar 包也是这样操作。
至于如何创建 c3p0 数据库连接池以及 获取其中的连接,我们可以打开我们下载到的 c3p0 文档中路径为 c3p0\c3p0-0.9.1.2 下找了一个名为 index.html 的帮助文档,打开它,里面有相关的 c3p0 的使用

打开显示如下效果:



import com.mchange.v2.c3p0.*;
...
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass( "org.postgresql.Driver" ); //loads the jdbc driver
cpds.setJdbcUrl( "jdbc:postgresql://localhost/testdb" );
cpds.setUser("dbuser");
cpds.setPassword("dbpassword");
我们按照上面的创建 c3p0 的案例演示,创建 c3p0 数据库连接池并获取其中连接池中的连接
package Blogs.blogs04;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
public class C3P0Test {
public static void main(String[] args) {
try {
// 通过传入配置参数,创建 c3p0 数据库连接池
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass( "com.mysql.cj.jdbc.Driver" ); // 注册数据库驱动信息
cpds.setJdbcUrl( "jdbc:mysql://localhost:3306/test"); // 实际连接的数据库的url在网络中所在的位置
cpds.setUser("root"); // 用户名
cpds.setPassword("MySQL123"); // 密码
// 从c3p0 数据库连接池中获取连接数据库的对象
Connection connection = cpds.getConnection();
System.out.println(connection); // 打印连接的地址
System.out.println(connection.getClass()); // 打印显示实现该类的所在包路径
} catch (PropertyVetoException e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}

关于如何编写 c3p0 的配置文件,同样查看其 index.html 的文档,如下:



<c3p0-config>
<default-config>
<property name="automaticTestTable">con_testproperty>
<property name="checkoutTimeout">30000property>
<property name="idleConnectionTestPeriod">30property>
<property name="initialPoolSize">10property>
<property name="maxIdleTime">30property>
<property name="maxPoolSize">100property>
<property name="minPoolSize">10property>
<property name="maxStatements">200property>
<user-overrides user="test-user">
<property name="maxPoolSize">10property>
<property name="minPoolSize">1property>
<property name="maxStatements">0property>
user-overrides>
default-config>
<named-config name="intergalactoApp">
<property name="acquireIncrement">50property>
<property name="initialPoolSize">100property>
<property name="minPoolSize">50property>
<property name="maxPoolSize">1000property>
<property name="maxStatements">0property>
<property name="maxStatementsPerConnection">5property>
<user-overrides user="master-of-the-universe">
<property name="acquireIncrement">1property>
<property name="initialPoolSize">1property>
<property name="minPoolSize">1property>
<property name="maxPoolSize">5property>
<property name="maxStatementsPerConnection">50property>
user-overrides>
named-config>
c3p0-config>
需要注意的是:c3p0 的配置文件名是:c3p0-config.xml 是不可以修改的,默认使用的就是这个文件名.
我们使用的是 IDEA 编译器,下面是如果在 IDEA 中创建 xml 文件,有的 IDEA 中 new 没有 xml,这时候就需要我们自己手动配置了。具体操作如下:






接下来就是仿照上面 index.html 中 c3p0-config.xml 配置文件案例编写配置文件信息了。具体的编写的配置信息如下,大家可以直接复制粘贴上去,就可以直接使用了。不对,大家还要手动修改一下你所对应的 user用户名和 password 密码,以及 url 实际数据库的网络位置。其他的到没有什么问题了。

<c3p0-config>
<named-config name="helloc3p0">
<property name="user">rootproperty>
<property name="password">MySQL123property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=trueproperty>
<property name="driverClass">com.mysql.cj.jdbc.Driverproperty>
<property name="acquireIncrement">5property>
<property name="initialPoolSize">5property>
<property name="minPoolSize">5property>
<property name="maxPoolSize">10property>
<property name="maxStatements">20property>
<property name="maxStatementsPerConnection">5property>
named-config>
c3p0-config>
我们编写好相关的 c3p0-config.xml 配置文件以后,就可以通过访问配置文件中的信息,创建c3p0数据库连接池,以及获取其中的连接了。
具体代码实现如下:
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
public class C3P0Test {
// 方式二: 通过读取配置文件中的信息,创建c3p0数据库连接池
public static void main(String[] args) {
// 创建c3p0数据库连接池
DataSource dataSource = new ComboPooledDataSource("helloc3p0");
try {
// 获取到数据库连接池中的连接
Connection connection = dataSource.getConnection();
System.out.println(connection); // 打印连接地址
System.out.println(connection.getClass()); // 打印类所在包下的路径
} catch (SQLException e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
}
}
}

对 c3p0 数据库连接池进行一个封装,封装到 JDBCUtils 类当中。
错误演示:
package Blogs.blogs04;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class JDBCUtils {
public static Connection getConnection() {
// 通过 c3p0-config.xml 配置文件,创建c3p0数据库连接池
ComboPooledDataSource cpds = new ComboPooledDataSource("helloc3p0");
Connection connection = null;
try {
connection = cpds.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
}
return connection;
}
}
错误原因:
我们把创建
c3p0数据库连接池放到了,调用返回连接中了。
把创建c3p0数据库连接池定义在方法中的后果是:我们每次调用该方法获取连接池,都会创建一个新的 c3p0 数据库连接池,一个连接池的创建的消耗远远比创建一个连接消耗来的大,而我们这么做就是:每创建一个连接就需要创建一个数据库连接池,还不如不用数据库连接池呢。
所以注意:一个应用使用一个数据库连接池就足够了,需要连接时,从数据库连接池中获取,如果数据库连接池中的连接不够了,可以通过配置增加数据库连接池中存有的连接,就把数据库连接池比作是一个生产连接的工厂。
综上所述:我们只要一个数据库连接池,所以我们可以将创建 c3p0 数据库连接池定义成静态 static 的类属性,和类一起加载到内存当中,只定义一次,所有对象共用这个数据库连接池
修改代码如下:

package Blogs.blogs04;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class JDBCUtils {
// 通过 c3p0-config.xml 配置文件,创建c3p0数据库连接池
private static ComboPooledDataSource cpds = new ComboPooledDataSource("helloc3p0"); // 作为类属性存在
public static Connection getC3p0Connection() {
Connection connection = null;
try {
connection = cpds.getConnection(); // static 静态不要使用 this的引用
} catch (SQLException e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
}
return connection;
}
}
比较传统的 JDBC 连接数据库,和 C3P0 使用数据连接池的性能
jdbc 与 c3p0 都与数据库连接 5000 次,比较连接完后所消耗的时间:
传统的 JDBC 连接数据库 5000 次
package Blogs.blogs04;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class JDBCTest {
public static void main(String[] args) {
// 读取配置文件信息
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties"); // 默认读取的是src目录下的文件
try {
Properties properties = new Properties(); // 创建读取.properties后缀的配置文件的对象
properties.load(is); // 以简单的线性格式输入字符读取属性列表(关键字/元素对)
// 通过对象传入关键字获取到其中的信息
String driverClass = properties.getProperty("driverClass");
String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
// 3. 加载/注册驱动
Class.forName(driverClass);
long start = System.currentTimeMillis(); // 获取到 5000 次连接数据库的前的时间点:单位:毫秒
// 测试连接 5000 次
for(int i = 0; i < 5000; i ++) {
// 连接数据库
Connection connection = DriverManager.getConnection(url,user,password);
if(connection != null) {
try {
connection.close(); // 关闭数据库连接
} catch(SQLException e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
}
}
}
long end = System.currentTimeMillis(); // 5000次连接数据库结束的时间点:单位:毫秒
System.out.println("jdbc 5000 次连接数据库所消耗的时间:" + (end - start));
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

C3P0 数据库连接池 中获取 5000 次的连接所消耗的时间
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
public class C3P0Test {
// 方式二: 通过读取配置文件中的信息,创建c3p0数据库连接池
public static void main(String[] args) {
// 创建c3p0数据库连接池
DataSource dataSource = new ComboPooledDataSource("helloc3p0");
long start = System.currentTimeMillis(); // 连接前的时间点:单位:毫秒
try {
for(int i = 0 ; i < 5000; i++) {
// 获取到数据库连接池中的连接
Connection connection = dataSource.getConnection();
if(connection != null) {
try{
connection.close(); // 使用使用数据库连接池,归还连接,不是关闭连接
} catch(SQLException e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
}
}
}
long end = System.currentTimeMillis(); // 连接结束后的时间点: 单位:毫秒
System.out.println("c3p0 数据库连接池 5000 次连接所消耗的时间:"+ (end - start));
} catch (SQLException e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
}
}
}

使用 c3p0 我们再将连接次数 增加到 5W, 50W 查看所消耗的时间:


我们可以看到非常明显的性能差,c3p0 比传统的 jdbc 连接快了 100 多倍,这就是我们使用数据库连接池的原因之一。
DBCP 是 Apache 软件基金组织下的开源连接池实现,该连接池依赖该组织下的另一个开源系统:Common-pool 。如需要使用 DBCP数据库连接池 ,则需要导入如下两个 jar 文件:
Commons-dbcp.jar 连接池的实现架包
Commons-pool.jar 连接池实现的依赖库。
jar 包托放到 Github ,Gitee 当中了
Tomcat 的连接池正是采用该连接池来实现的。 该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
数据源(数据库连接池)和数据库连接不同,数据库连接池无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据库连接池即可
方式一: 使用传参数的方式创建 DBCP 数据库连接池,在获取连接,不推荐
关于如何创建 DBCP 数据库连接池,同样我们可以阅读其中的index.html帮助文档:在我们所下 jar包文件的路径下:Commons-dbcp\commons-dbcp-1.4\apidocs


导入 Commons-dbcp.jar,Commons-pool.jar 两个 jar 包

具体代码实现如下:
package Blogs.blogs04;
import org.apache.commons.dbcp.BasicDataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class DBCPTest {
public static void main(String[] args) {
// 定义创建 dbcp 数据库连接池的对象
BasicDataSource source = new BasicDataSource();
source.setDriverClassName("com.mysql.cj.jdbc.Driver"); // 注册驱动这里是mysql8.0/ 5.0的没有 cj
source.setUrl("jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true"); // 实际数据库在网络url的位置
// ?rewriteBatchedStatements=true批量处理
// 或者可以省略本地主机 localhost:3306 改为 jdbc:mysql:///test
source.setUsername("root"); // 用户名
source.setPassword("MySQL123"); // 密码
// 获取到数据库中的连接
try {
Connection connection = source.getConnection();
System.out.println(connection); // 所获得的连接的地址
System.out.println(connection.getClass()); // 类所在的包路径
} catch (SQLException e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
}
}
}

方式二:将所需的参数编写到配置文件当中,配置文件是以.properties为后缀的文件
注意是该配置文件创建在 src 目录下,这里我们将我们的 dbcp 所编写的配置文件名为为 dbcp.properties
IDEA 中的 src 目录中创建 .properties 文件


该文件的编写如下:
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
# ?rewriteBatchedStatements=true 表示批量处理
username=root
password=MySQL123
# 不要加空格和分号,不然报错,空格也会被当作是参数变量,识别使用
其他的配置文件的参数如下:大家可以自行参考,斟酌添加上
| 属性 | 默认值 | 说明 |
|---|---|---|
| initalSize | 0 | 数据库连接池启动时创建的初始化连接数量 |
| maxActive | 8 | 数据库连接池中可 同时 连接的最大的连接数量 |
| maxldle | 8 | 连接池中最大的空闲的连接数,超过的空闲连接将被释放 ,如果设置为负数表示无限制 |
| minldle | 0 | 连接池中最小的空闲的连接数,低于这个数量会被创建新的连接。该参数越接近 maxldle,性能越好,因为连接的创建和销毁,都需要消耗资源,但是不能太大 |
| maxWait | 无限制 | 最大等待时间,当没有空闲的连接,连接池中所有的连接都被占用时,再请求连接,则会加入到等待队列中,等待空闲的连接出现,如果超过了该设定的等待时间还没有出现空闲连接,就会自动放弃该连接请求。如果设置为 -1 表示无限等待 |
| poolPreparedStatements | false | 开启连接池中的 Statement 是否为 prepared |
| maxOpenPreparedStatements | 无限制 | 开启连接池的 prepared 后的同时最大连接数 |
| minEvictableldleTimeMillis | 连接池中连接,在时间段内一直空闲,被逐出连接池的时间 | |
| removerAbandonedTimeout | 300 | 超过时间限制,自动回收没有用(废弃)的连接 |
具体代码实现如下:
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class DBCPTest {
// 方式二:通过读取配置文件中的信息,创建dbcp数据库连接池
public static void main(String[] args) {
try {
// 读取配置文件中的信息,方式一:
FileInputStream is = new FileInputStream(new File("src/dbcp.properties"));
// 读取配置文件中信息,方式二:
InputStream io = ClassLoader.getSystemClassLoader().getResourceAsStream("dbcp.properties"); // 默认是读取src目录下的文件
// 创建对象获取读取到的配置文件的参数信息
Properties properties = new Properties();
properties.load(is); // 以简单的线性格式输入字符读取到属性列表(关键字/元素对)
// 通过传入获取配置文件的对象,创建dbcp 数据库连接池
DataSource dataSource = BasicDataSourceFactory.createDataSource(properties);
// 从dbcp 数据库连接池中获取到连接
Connection connection = dataSource.getConnection();
System.out.println(connection); // 显示连接地址
System.out.println(connection.getClass()); // 显示类所在的包下的路径
} catch (Exception e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
}
}
}

下面我们对 dbcp 数据库连接池进行一个封装,如下:同样注意,一个应用一个数据库连接池就足够了。
错误演示:
我们需要调用方法读取配置文件中的信息,定义对象在类当中:作为类属性存在没有问题,但是在类当中我们是无法调用方法的。
方法的调用需要是在:代码块当中:方法中的代码块,以及 类当中的 static 静态代码块,而静态代码块的特点十分符合我们一个应用一个 数据库连接池,因为 static 静态代码块(和类一起加载到内存当中,仅仅执行一次,所有对象共用它)。

改正:
具体代码如下:
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import javax.sql.DataSource;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class JDBCUtils {
private static DataSource dataSource = null;
static {
try {
// 读取配置文件中的信息,方式一:
FileInputStream is = new FileInputStream(new File("src/dbcp.properties"));
// 读取配置文件中信息,方式二:
InputStream io = ClassLoader.getSystemClassLoader().getResourceAsStream("dbcp.properties"); // 默认是读取src目录下的文件
// 创建对象获取读取到的配置文件的参数信息
Properties properties = new Properties();
properties.load(is); // 以简单的线性格式输入字符读取到属性列表(关键字/元素对)
// 通过传入获取配置文件的对象,创建dbcp 数据库连接池
dataSource = BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
}
}
public static Connection getDbcpConnection() {
try {
Connection connection = dataSource.getConnection();
return connection;
} catch (SQLException e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
}
}
}
测试 DBCP 数据库连接池处理 5000次/5w次50w次 连接所消耗的时间:
具体代码如下:
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import javax.sql.DataSource;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class DBCPTest {
// 方式二:通过读取配置文件中的信息,创建dbcp数据库连接池
public static void main(String[] args) {
try {
// 读取配置文件中的信息,方式一:
FileInputStream is = new FileInputStream(new File("src/dbcp.properties"));
// 读取配置文件中信息,方式二:
InputStream io = ClassLoader.getSystemClassLoader().getResourceAsStream("dbcp.properties"); // 默认是读取src目录下的文件
// 创建对象获取读取到的配置文件的参数信息
Properties properties = new Properties();
properties.load(is); // 以简单的线性格式输入字符读取到属性列表(关键字/元素对)
// 通过传入获取配置文件的对象,创建dbcp 数据库连接池
DataSource dataSource = BasicDataSourceFactory.createDataSource(properties);
long start = System.currentTimeMillis(); // 获取到连接前的时间点: 单位毫秒
for(int i = 0; i < 5000; i++) {
// 从dbcp数据库连接池中获取到连接
Connection connection = dataSource.getConnection();
if(connection != null) {
try{
// 归还连接,不是关闭连接
connection.close();
} catch(SQLException e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
}
}
}
long end = System.currentTimeMillis(); // 获取到所有连接完毕后的时间点:
System.out.println("5000次连接所消耗的时间: " + (end - start));
} catch (Exception e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
}
}
}




Druild 是阿里巴巴开源平台上一个数据库连接池实现,它结合了 C3P0,DBCP,Proxool 等 DB 池的优点,同时加入了 日志监控 ,可以很好的监控 数据库连接池 和 SQL的执行情况 ,可以说针对监控而生的 数据库连接池,同时也可以说是 目前最好的连接池之一 。是目前市场上大部分厂家使用的数据库连接池(数据源)。 同样的 Druid 也有相关的index.html帮助文档:在如下路径中 commons-dbutils-1.3\apidocs


和上面的两个数据库连接池是一样的,同样需要导入 有关 Druild 的 jar 包commons-dbutils-1.3.jar 如下
IDEA 导入 commons-dbutils-1.3.jar 包

同样我们也将相应的jar 包托放到 Github ,Gitee 当中了

这里我们直接使用编写配置文件,然后读取配置文件的方式 创建 Druid 数据库连接池的方式
注意编写配置文件是以 .properties后缀的,而且是创建在 src 目录下的配置文件的信息。
这里我们创建的 Druid 配置文件的名为 druid.properties
如下使用 IDEA 创建 .properties 的文件


下面是 druid.properties 配置文件的编写:
driverClassName=com.mysql.cj.jdbc.Driver
#?rewriteBatchedStatements=true 批量处理打开
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
username=root
password=MySQL123
#初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
initialSize=10
#最小连接池数量
minIdle=5
#最大连接池数量
maxActive=20
#获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
maxWait=5000
#属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
filters=wall
#不要加空格以及;分号,因为无论是空格还是分号都会被识别成对象匹配。防止匹配失败
下面是其他的有关 Druid 配置参数信息,大家可以自行参考,斟酌添加上
| 配置 | 缺省 | 说明 |
|---|---|---|
| name | 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this) | |
| url | 连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto | |
| username | 连接数据库的用户名 | |
| password | 连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter | |
| driverClassName | 根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下) | |
| initialSize | 0 | 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 |
| maxActive | 8 | 最大连接池数量 |
| maxIdle | 8 | 已经不再使用,配置了也没效果 |
| minIdle | 最小连接池数量 | |
| maxWait | 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 | |
| poolPreparedStatements | false | 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。 |
| maxOpenPreparedStatements | -1 | 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 |
| validationQuery | 用来检测连接是否有效的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,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系 |
通过读取配置文件信息,创建 Druid 数据库连接池,再获取其中的连接
package Blogs.blogs04;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
public class DruidTest {
public static void main(String[] args) {
// 读取配置文件的方式一:
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
try {
// 读取配置文件的方式二:
FileInputStream io = new FileInputStream(new File("src/druid.properties"));
// 创建对象获取到读取的配置文件中的关键字/属性值
Properties properties = new Properties();
properties.load(io);
// 通过传入读取配置文件信息对象,创建 druid数据库连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
// 从创建的 druid数据库连接池中获取到连接
Connection connection = dataSource.getConnection();
System.out.println(connection); // 连接的地址
System.out.println(connection.getClass()); // 类所在的包路径位置
} catch (Exception e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出。
}
}
}

同理将 创建 Druid 数据库连接池封装起来。注意:一个应用一个数据库连接池就足够了,方法的调用,写在static静态代码块中(和类一起加载到内存当中,仅仅执行一次,所有对象共用)。
具体实现如下:
public class JDBCUtils {
private static DataSource dataSource = null;
static {
try {
// 读取配置文件的方式二:
FileInputStream io = new FileInputStream(new File("src/druid.properties"));
// 创建对象获取到读取的配置文件中的关键字/属性值
Properties properties = new Properties();
properties.load(io);
// 通过传入读取配置文件信息对象,创建 druid数据库连接池
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
}
}
public static Connection getDruidConnection() {
try {
Connection connection = dataSource.getConnection();
return connection;
} catch (SQLException e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出
}
}
}
测试 Druid 数据库连接池处理 5000次/5w次50w次 连接所消耗的时间:
具体代码如下:
package Blogs.blogs04;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class DruidTest {
public static void main(String[] args) {
// 读取配置文件的方式一:
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
try {
// 读取配置文件的方式二:
FileInputStream io = new FileInputStream(new File("src/druid.properties"));
// 创建对象获取到读取的配置文件中的关键字/属性值
Properties properties = new Properties();
properties.load(io);
// 通过传入读取配置文件信息对象,创建 druid数据库连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
long start = System.currentTimeMillis(); // 获取到连接前的时间点:单位毫秒
for(int i = 0; i < 5000; i++) {
// 从创建的 druid数据库连接池中获取到连接
Connection connection = dataSource.getConnection();
if(connection != null) {
try {
connection.close(); // “归还连接”,不是关闭连接
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
long end = System.currentTimeMillis(); // 获取到连接完毕后的时间点:单位毫秒
System.out.println("5000次连接所消耗的时间:" +(end - start));
} catch (Exception e) {
throw new RuntimeException(e); // 将编译异常转换为运行异常抛出。
}
}
}




jar 包:
index.html大家不要忘记了它的存在。c3p0-config.xml ,而 DBCP以及 Druid(德鲁伊) 它们俩的 配置文件 是以 .properties后缀的,都将它们三者的配置文件信息都存放在项目 src 目录下。 // 读取配置文件的方式一:
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
// 读取配置文件的方式二:
FileInputStream io = new FileInputStream(new File("src/druid.properties"));
// 创建对象获取到读取的配置文件中的关键字/属性值
Properties properties = new Properties();
properties.load(io);

限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,后会有期,江湖再见 !!!。最后,朋友,请留下你来过的证明 。
