实际开发中“获得连接”或“释放资源”是非常消耗系统资源的两个过程,为了解决此类性能问题,通常情况我们采用连接池技术,来共享连接Connection。这样我们就不需要每次都创建连接、释放连接了,这些操作都交给了连接池
l 概念
用池来管理Connection,这样可以重复使用Connection。有了池,所以我们就不用自己来创建Connection,而是通过池来获取Connection对象。当使用完Connection后,调用Connection的close()方法也不会真的关闭Connection,而是把Connection“归还”给池。池就可以再利用这个Connection对象了。
l 规范
Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!
常见的连接池:DBCP、C3P0。
接下来,我们就详细的学习连接池。
C3P0开源免费的连接池!目前使用它的开源项目有:Spring、Hibernate等。使用第三方工具需要导入jar包,c3p0使用时还需要添加配置文件c3p0-config.xml
1.2.1 导入jar包
我们使用的0.9.2版本,需要导入2个jar包
1.2.2 核心类
@Test
public void demo01() throws Exception{
//1 获得连接池(数据源)
ComboPooledDataSource dataSource = new ComboPooledDataSource();
//1.1 设置基本项
dataSource.setDriverClass('com.mysql.jdbc.Driver');
dataSource.setJdbcUrl('jdbc:mysql://localhost:3306/webdb_4');
dataSource.setUser('root');
dataSource.setPassword('root');
//1.2其他项
// * 初始化连接池中连接的个数
dataSource.setInitialPoolSize(5);
// * 最小|最大连接池中连接的个数
dataSource.setMinPoolSize(2);
dataSource.setMaxPoolSize(10);
// * 最大空闲数
dataSource.setMaxIdleTime(60);
// * 每次增长个数
dataSource.setAcquireIncrement(2);
//2获得连接
Connectionconn = dataSource.getConnection();
System.out.println(conn);
}
1.2.3 配置文件
c3p0-config>
named-configname='itheima'>
propertyname='driverClass'>com.mysql.jdbc.Driverproperty>
propertyname='jdbcUrl'>jdbc:mysql://127.0.0.1:3306/webdbproperty>
propertyname='user'>rootproperty>
propertyname='password'>rootproperty>
propertyname='acquireIncrement'>5property>
propertyname='initialPoolSize'>20property>
propertyname='minPoolSize'>10property>
propertyname='maxPoolSize'>40property>
propertyname='maxStatements'>0property>
propertyname='maxStatementsPerConnection'>5property>
named-config>
c3p0-config>
c3p0-config>
default-config>
propertyname='driverClass'>com.mysql.jdbc.Driverproperty>
propertyname='jdbcUrl'>jdbc:mysql://127.0.0.1:3306/webdbproperty>
propertyname='user'>rootproperty>
propertyname='password'>rootproperty>
propertyname='checkoutTimeout'>30000property>
propertyname='idleConnectionTestPeriod'>30property>
propertyname='initialPoolSize'>10property>
propertyname='maxIdleTime'>30property>
propertyname='maxPoolSize'>100property>
propertyname='minPoolSize'>10property>
propertyname='maxStatements'>200property>
user-overridesuser='test-user'>
propertyname='maxPoolSize'>10property>
propertyname='minPoolSize'>1property>
propertyname='maxStatements'>0property>
user-overrides>
default-config>
1.2.4 常见配置项
分类 | 属性 | 描述 |
必须项 | user | 用户名 |
password | 密码 |
driverClass | 驱动 mysql驱动,com.mysql.jdbc.Driver |
jdbcUrl | 路径 mysql路径,jdbc:mysql://localhost:3306/数据库 |
基本配置 | acquireIncrement | 连接池无空闲连接可用时,一次性创建的新连接数 默认值:3 |
initialPoolSize | 连接池初始化时创建的连接数 默认值:3 |
maxPoolSize | 连接池中拥有的最大连接数 默认值:15 |
minPoolSize | 连接池保持的最小连接数。 |
maxIdleTime | 连接的最大空闲时间。如果超过这个时间,某个数据库连接还没有被使用,则会断开掉这个连接,如果为0,则永远不会断开连接。 默认值:0 |
管理连接池的大小和连接的生存时间(扩展) | maxConnectionAge | 配置连接的生存时间,超过这个时间的连接将由连接池自动断开丢弃掉。当然正在使用的连接不会马上断开,而是等待它close再断开。配置为0的时候则不会对连接的生存时间进行限制。默认值0 |
maxIdleTimeExcessConnections | 这个配置主要是为了减轻连接池的负载,配置不为0,则会将连接池中的连接数量保持到minPoolSize,为0则不处理。 |
配置Prepared Statement缓存(扩展) | maxStatements | 连接池为数据源缓存的PreparedStatement的总数。由于PreparedStatement属于单个Connection,所以这个数量应该根据应用中平均连接数乘以每个连接的平均PreparedStatement来计算。为0的时候不缓存,同时maxStatementsPerConnection的配置无效。 |
maxStatementsPerConnection | 连接池为数据源单个Connection缓存的PreparedStatement数,这个配置比maxStatements更有意义,因为它缓存的服务对象是单个数据连接,如果设置的好,肯定是可以提高性能的。为0的时候不缓存。 |
1.2.5 编写工具类
C3P0提供核心工具类:ComboPooledDataSource,如果要使用连接池,必须创建该类的实例对象。
public class C3P0Utils{
//使用默认配置
// privatestatic ComboPooledDataSource dataSource = new ComboPooledDataSource();
//使用命名配置
private static ComboPooledDataSource dataSource = new ComboPooledDataSource('itheima');
/**
* 获得数据源(连接池)
* @return
*/
public static DataSource getDataSource(){
returndataSource;
}
/**
* 获得连接
* @return
* @throws SQLException
*/
public static Connection getConnection(){
try {
returndataSource.getConnection();
}catch (Exception e) {
thrownew RuntimeException(e);
}
}
}
DBCP也是一个开源的连接池,是Apache Common成员之一,在企业开发中也比较常见,tomcat内置的连接池。
1.3.1 导入jar包

1.3.2 核心类
@Test
public void demo01() throws Exception{
//1 获得连接池
BasicDataSource dataSource = new BasicDataSource();
//1.1 基本项
dataSource.setDriverClassName('com.mysql.jdbc.Driver');
dataSource.setUrl('jdbc:mysql://localhost:3306/webdb_4');
dataSource.setUsername('root');
dataSource.setPassword('root');
//1.2 其他项
// * 初始化连接池中连个的个数
dataSource.setInitialSize(5);
// * 最大活动数
dataSource.setMaxActive(10);
//2获得连接
Connectionconn = dataSource.getConnection();
System.out.println(conn);
}
1.3.3 配置文件
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/webdb
username=root
password=root
#
initialSize=10
#最大连接数量
maxActive=50
#
maxIdle=20
#
minIdle=5
#
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:'user'与'password' 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=gbk
#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true
#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED,READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
1.3.4 常见配置项
分类 | 属性 | 描述 |
必须项 | driverClassName |
|
url |
|
username |
|
password |
|
基本项 | maxActive | 最大连接数量 |
minIdle | 最小空闲连接 |
maxIdle | 最大空闲连接 |
initialSize | 初始化连接 |
|
|
|
优化配置(扩展) | logAbandoned | 连接被泄露时是否打印 |
removeAbandoned | 是否自动回收超时连接 |
removeAbandonedTimeout | 超时时间(以秒数为单位) |
maxWait | 超时等待时间以毫秒为单位 1000等于60秒 |
timeBetweenEvictionRunsMillis | 在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位 |
numTestsPerEvictionRun | 在每次空闲连接回收器线程(如果有)运行时检查的连接数量 |
minEvictableIdleTimeMillis | 连接在池中保持空闲而不被空闲连接回收器线程 |
参考文档:http://commons./proper/commons-dbcp/configuration.html
1.3.5 编写工具类
public class DBCPUtils{
private static DataSource dataSource;
static{
try {
//1加载配置文件,获得文件流
InputStreamis =DBCPUtils.class.getClassLoader().getResourceAsStream('dbcp.properties');
//2使用Properties处理配置文件
Propertiesprops = new Properties();
props.load(is);
//3使用工具类创建连接池(数据源)
dataSource= BasicDataSourceFactory.createDataSource(props);
}catch (Exception e) {
thrownew RuntimeException(e);
}
}
public static DataSource getDataSource(){
returndataSource;
}
public static Connection getConnection(){
try {
return dataSource.getConnection();
}catch (Exception e) {
thrownew RuntimeException(e);
}
}
}
1.4.1 案例分析
根据我们对连接池简单的理解,如果我们要编写自定义连接池,需要完成以下步骤
1.创建连接池实现(数据源),并实现接口 javax.sql.DataSource 。因为我们只使用该接口中getConnection()方法,简化本案例,我们将自己提供方法,而没有实现接口
2.提供一个集合,用于存放连接,因为移除/添加操作过多,所以选择LinkedList
3.本案例在静态代码块中,为连接池初始化3个连接。
4.之后程序如果需要连接,调用实现类的getConnection(),本方法将从连接池(容器List)获得连接。为了保证当前连接只能提供给一个线程使用,所以我们需要将连接先从连接池中移除。
5.当用户使用完连接,释放资源时,不执行close()方法,而是将连接添加到连接池中。
1.4.2 案例实现
1.4.2.1 提供容器及初始化
//#1 创建容器,用于存放连接Connection
private static LinkedList pool =new LinkedList();
//#1.1初始化连接池中的连接
static{
try {
//1 注册驱动
Class.forName('com.mysql.jdbc.Driver');
for(int i = 0; i <>3; i++) {
//2获得连接
Connectionconn = DriverManager.getConnection('jdbc:mysql://localhost:3306/day10_db','root', '1234');
//3将连接添加到连接池中
pool.add(conn);
}
} catch(Exception e) {
thrownew RuntimeException(e);
}
}
1.4.2.2 获得连接
/**
* #2 获得连接,从连接池中获得连接
* @return
*/
public static Connection getConnection(){
returnpool.removeFirst();
}
1.4.2.3 归还连接
/**
* #3 释放资源,当链接connection close时,归还给连接池
* @paramconn
* @param st
* @param rs
*/
public static void release(Connection conn){
try {
if(conn != null) {
// conn.close(); //不是真的关闭
pool.add(conn); //将从连接池获得连接,归还给连接池
}
} catch(Exception e) {
}
}
1.4.2.4 测试使用
为了体现连接池优势,我们将采用多线程并发访问,使同一个连接在不同的时段,被不同的线程使用。
public class TestCustomPool {
public static void main(String[] args) {
//1 获得连接
Connectionconn = JdbcUtils.getConnection();
System.out.println('使用:' + conn + ' , ' +Thread.currentThread());
//2释放资源
JdbcUtils.release(conn);
}
}
1.5 自定义连接池:方法增强
1.5.1 需求
自定义连接池中存在严重问题,用户调用getConnection()获得连接后,必须使用release()方法进行连接的归还,如果用户调用 conn.close() 将连接真正的释放,连接池中将出现无连接可用。
此时我们希望,即使用户调用了close()方法,连接仍归还给连接池。close()方法原有功能时释放资源,期望功能:将当前连接归还连接池。说明close()方法没有我们希望的功能,我们将对close()方法进行增强,从而实现将连接归还给连接池的功能。
1.5.2 方法增强总结
1.继承,子类继承父类,将父类的方法进行复写,从而进行增强。
使用前提:必须有父类,且存在继承关系。
2.装饰者设计模式,此设计模式专门用于增强方法。
使用前提:必须有接口
缺点:需要将接口的所有方法都实现
3.动态代理:在运行时动态的创建代理类,完成增强操作。与装饰者相似
使用前提:必须有接口
难点:需要反射技术
1.5.3 装饰者设计模式
设计模式:专门为解决某一类问题,而编写的固定格式的代码。
装饰者固定结构:接口A,已知实现类C,需要装饰者创建代理类B
1.创建类B,并实现接口A
2.提供类B的构造方法,参数类型为A,用于接收A接口的其他实现类(C)
3.给类B添加类型为A成员变量,用于存放A接口的其他实现类
4.增强需要的方法
5.实现不需要增强的方法,方法体重调用成员变量存放的其他实现类对应的方法
A a = …C;
B b = new B(a);
class B implements A{
private Aa;
public B(Aa){
this.a =a;
}
//增强的方法
public void close(){
}
//不需要增强的方法
public voidcommit(){
this.a.commit();
}
}
1.5.4 实现
1.5.4.1 装饰类
public class MyConnection implements Connection {
private Connection conn;
private List pool;
public MyConnection(Connection conn){
this.conn= conn;
}
/* 因为与自定义连接池有关系,所以需要另外添加一个构造方法 */
public MyConnection(Connection conn,List pool){
this.conn= conn;
this.pool= pool;
}
/* 以下是增强的方法 */
@Override
public void close() throws SQLException {
//将调用当前close的链接Connection对象添加到链接池中
// System.out.println('连接归还:' + this);
this.pool.add(this);
}
/* 以下是不需要增强的方法 */
@Override
public void commit() throws SQLException {
this.conn.commit();
}
....
}
1.5.4.2 使用装饰类(包装类)
将由DriverManager创建的连接,使用装饰类包装一下,然后添加到连接池中,构造方法中将容器pool传递进去,方便连接的归还。

1.5.4.3 使用连接
