您的位置:首页 > 其它

开源组件:(1)DBCP和C3P0

2016-06-07 01:12 239 查看
一种技术的出现,要么是解决实际问题,要么是优化现有技术。数据库连接池技术的出现,是为了优化数据库连接操作的性能。在使用JDBC进行数据库开发的时候,一般经历这样一个过程:1)加载数据库的驱动2)建立数据库的连接(Connection)3)创建SQL语句声明(Statement)4)执行更新(executeUpdate)或查询(executeQuery)
本文中讲的数据库连接池,只是针对Connection的部分的优化。学习连接池a. 自定义一个连接池b. 学习优秀的连接池组件1)DBCP2)C3P0

1、引入

思考:程序中Connection连接是如何管理的?数据库的连接(Connection)涉及到的操作有:a)数据库操作开始,创建连接,b)操作结束,关闭连接。我们知道连接资源十分宝贵,因此需要对它进行管理。如果频繁的打开和关闭连接,会影响程序的运行效率!连接管理的思路:预先创建一组连接,用的时候每次取出一个;用完之后,将连接放回去。

2、自定义连接池

第一个版本
package com.rk.pool;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;

/**
* 自定义连接池,管理连接(Connection)
* 全局参数:初始化数目、最大连接数、当前连接数、连接池集合
*
* 当启动的时候,就有3(init_count)个初始连接Connection
* 		1.创建连接的方法createConnection()
* 		2.提供获取连接的方法getConnection()
* 			2.1如果有空闲连接,则直接返回
* 			2.2如果没有空闲连接,且没有达到最大限制数量,则创建连接返回
* 			2.3如果没有空闲连接,且连接数据达到最大限制数,则无法获取到连接,返回null
* 		3.提供释放连接的方法releaseConnection(Connection conn)
* 			3.1如果连接池内的空闲连接数量小于初始的连接数量,则当前连接返回到连接池中
* 			3.2如果连接池内的空闲连接数量等于初始的连接数量,则将当前连接关闭
* @author lsieun
*
*/
public class MyConnectionPool
{
private final static String url = "jdbc:mysql:///testdb";
private final static String driverClassName = "com.mysql.jdbc.Driver";
private final static String user = "root";
private final static String password = "root";

private LinkedList<Connection> freeConnections = null;//// 连接池 (存放所有的初始化连接)
private final int init_count = 3;//初始的连接(Connection)数量(最小值)
private final int max_count = 6;//最大的连接数量(最大值)
private int current_count = 0;//当前拥有的连接数量(当前值)

/**
* 静态代码块,加载数据库的驱动程序
*/
static
{
try
{
Class.forName(driverClassName);
}
catch (ClassNotFoundException e)
{
throw new RuntimeException(e);
}
}

//1.初始化
public MyConnectionPool() throws SQLException
{
//初始化连接池
freeConnections = new LinkedList<Connection>();
//将指定数量(init_cont)加入到连接池中
for(int i=0;i<init_count;i++)
{
// 记录当前连接数目
current_count++;
// 创建连接对象
Connection con = createConnection();
// 把连接加入连接池
freeConnections.add(con);

}
}

public int getCurrentCount()
{
return current_count;
}

/**
* 私有的静态方法,用于创建数据库连接
*/
private static Connection createConnection() throws SQLException
{
return DriverManager.getConnection(url, user, password);
}
//2.获取数据库的连接
public Connection getConnection() throws SQLException
{
//判断连接池中是否有连接, 如果有连接,就直接从连接池取出
if(freeConnections.size()>0)
{
return freeConnections.removeFirst();
}
else if(current_count<max_count)//连接池中没有连接;并且如果没有达到最大连接数,创建新连接;
{
current_count++;
return createConnection();
}
else//当前已经达到最大连接数
{
System.out.println("已经达到连接最大数量限制,无法获得新的连接");
return null;
}
}

//3.释放连接
public void releaseConnection(Connection conn) throws SQLException
{
//池的数目如果小于初始化连接,就放入池中
if(freeConnections.size() < init_count)
{
freeConnections.addLast(conn);
}
else
{//关闭
current_count--;
conn.close();
}
}
public static void main(String[] args) throws SQLException
{
/*
MyConnectionPool pool = new MyConnectionPool();
System.out.println("0-->" + pool.getCurrentCount());
Connection conn1  = pool.getConnection();
Connection conn2  = pool.getConnection();
Connection conn3  = pool.getConnection();
System.out.println("3-->" + pool.getCurrentCount());
Connection conn4  = pool.getConnection();
System.out.println("4-->" + pool.getCurrentCount());
Connection conn5  = pool.getConnection();
System.out.println("5-->" + pool.getCurrentCount());
Connection conn6  = pool.getConnection();
System.out.println("6-->" + pool.getCurrentCount());
Connection conn7 = pool.getConnection();
System.out.println("7-->" + pool.getCurrentCount());
*/

MyConnectionPool pool = new MyConnectionPool();
System.out.println("0-->" + pool.getCurrentCount());
Connection conn1  = pool.getConnection();
Connection conn2  = pool.getConnection();
Connection conn3  = pool.getConnection();
System.out.println("3-->" + pool.getCurrentCount());
pool.releaseConnection(conn1);
Connection conn4  = pool.getConnection();
System.out.println("4-->" + pool.getCurrentCount());
}
}

第二个版本(在创建Connection时,增加了动态代理)
如果开发人员得到Connection对象时,并调用它的close方法,并不会关闭连接,而是将Connection对象放回到连接池中,这就是通过动态代理来实现的。如果对某个接口中的某个指定的方法的功能进行扩展,而不想实现接口里所有方法,可以使用(动态)代理模式! 使用动态代理,可以监测接口中方法的执行!Java中代理模式:静态/动态/Cglib代理(spring)
如何对Connection对象,生成一个代理对象:
|--Proxy
static Object newProxyInstance(
ClassLoader loader, 当前使用的类加载器
Class<?>[] interfaces, 目标对象(Connection)实现的接口类型
InvocationHandler h 事件处理器:当执行上面接口中的方法的时候,就会自动触发事件处理器代码,把当前执行的方法(method)作为参数传入。
)
package com.rk.pool;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;

/**
* 自定义连接池,管理连接(Connection)
* 全局参数:初始化数目、最大连接数、当前连接数、连接池集合
*
* 当启动的时候,就有3(init_count)个初始连接Connection
* 		1.创建连接的方法createConnection()
* 		2.提供获取连接的方法getConnection()
* 			2.1如果有空闲连接,则直接返回
* 			2.2如果没有空闲连接,且没有达到最大限制数量,则创建连接返回
* 			2.3如果没有空闲连接,且连接数据达到最大限制数,则无法获取到连接,返回null
* 		3.提供释放连接的方法releaseConnection(Connection conn)
* 			3.1如果连接池内的空闲连接数量小于初始的连接数量,则当前连接返回到连接池中
* 			3.2如果连接池内的空闲连接数量等于初始的连接数量,则将当前连接关闭
* @author lsieun
*
*/
public class MyConnectionPoolUpdate
{
private final static String url = "jdbc:mysql:///testdb";
private final static String driverClassName = "com.mysql.jdbc.Driver";
private final static String user = "root";
private final static String password = "root";

private LinkedList<Connection> freeConnections = null;//// 连接池 (存放所有的初始化连接)
private final int init_count = 3;//初始的连接(Connection)数量(最小值)
private final int max_count = 6;//最大的连接数量(最大值)
private int current_count = 0;//当前拥有的连接数量(当前值)

/**
* 静态代码块,加载数据库的驱动程序
*/
static
{
try
{
Class.forName(driverClassName);
}
catch (ClassNotFoundException e)
{
throw new RuntimeException(e);
}
}

//1.初始化
public MyConnectionPoolUpdate() throws SQLException
{
//初始化连接池
freeConnections = new LinkedList<Connection>();
//将指定数量(init_cont)加入到连接池中
for(int i=0;i<init_count;i++)
{
// 记录当前连接数目
current_count++;
// 创建连接对象
Connection con = createConnection();
// 把连接加入连接池
freeConnections.add(con);

}
}

public int getCurrentCount()
{
return current_count;
}

/**
* 私有的方法,用于创建数据库连接
*/
private Connection createConnection() throws SQLException
{
final Connection conn = DriverManager.getConnection(url, user, password);
Connection proxy = (Connection)Proxy.newProxyInstance(
//conn.getClass().getClassLoader();// 第一个参数,类加载器(方法一)
Connection.class.getClassLoader(), // 第一个参数,类加载器(方法二)
//conn.getClass().getInterfaces(),//第二个参数,如果当前目标对象是一个具体的类的时候,用这个
new Class[]{Connection.class}, //第二个参数,由于Connection是一个接口
new InvocationHandler()
{
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
// 方法返回值
Object result = null;
System.out.println(proxy.getClass());
// 当前执行的方法的方法名
String methodName = method.getName();
if("close".equals(methodName))
{
System.out.println("begin:当前执行close方法开始!");
// 调用释放连接池的方法
MyConnectionPoolUpdate.this.releaseConnection(conn);
System.out.println("end: 当前连接已经放入连接池了!");
}
else
{
// 调用目标对象方法
result = method.invoke(conn, args);
}
return result;
}
});
return proxy;
}
//2.获取数据库的连接
public Connection getConnection() throws SQLException
{
//判断连接池中是否有连接, 如果有连接,就直接从连接池取出
if(freeConnections.size()>0)
{
return freeConnections.removeFirst();
}
else if(current_count<max_count)//连接池中没有连接;并且如果没有达到最大连接数,创建新连接;
{
current_count++;
return createConnection();
}
else//当前已经达到最大连接数
{
System.out.println("已经达到连接最大数量限制,无法获得新的连接");
return null;
}
}

//3.释放连接
public void releaseConnection(Connection conn) throws SQLException
{
System.out.println("MyConnectionPoolUpdate.releaseConnection()");
//池的数目如果小于初始化连接,就放入池中
if(freeConnections.size() < init_count)
{
freeConnections.addLast(conn);
}
else
{//关闭
current_count--;
conn.close();
}
}
public static void main(String[] args) throws SQLException
{
MyConnectionPoolUpdate pool = new MyConnectionPoolUpdate();
System.out.println("0-->" + pool.getCurrentCount());
Connection conn1  = pool.getConnection();
conn1.close();
}
}


3、开源的连接池技术

Sun公司约定: 如果是连接池技术,需要实现一个接口:javax.sql.DataSource!注意,DataSource位于javax.sql包下,而不是java.sql包下,其中x表示扩展的意思。下面是DataSource的源代码,它只提供了两个重载方法getConnection。
public interface DataSource  extends CommonDataSource,Wrapper {

/**
* <p>Attempts to establish a connection with the data source that
* this <code>DataSource</code> object represents.
*/
Connection getConnection() throws SQLException;

/**
* <p>Attempts to establish a connection with the data source that
* this <code>DataSource</code> object represents.
*/
Connection getConnection(String username, String password)
throws SQLException;

}

javax.sql.DataSource接口
(1)DataSource object是对DriverManager的一种替代。推荐使用DataSource来获得Connection对象。
An alternative to the DriverManager facility, a DataSource object is the preferred means of getting a connection.
(2)实现DataSource接口的object常常是用JNDI注册的。
An object that implements the DataSource interface will typically be registered with a naming service based on the JavaTM Naming and Directory (JNDI) API.
(3)DataSource接口由driver vendor实现,有三种实现类型:
3.1)Basic implementation:基本实现,产生标准的Connection对象
3.2)Connection pooling implementation:连接池实现,有connection pool,由a middle-tier connection pooling manager
3.3)Distributed transaction implementation:分布式事务实现,由a middle-tier transaction manager和a connection pooling manager.

The DataSource interface is implemented by a driver vendor. There are three types of implementations:
a)Basic implementation -- produces a standard Connection object
b)Connection pooling implementation -- produces a Connection object that will automatically participate in connection pooling. This implementation works with a middle-tier connection pooling manager.
c)Distributed transaction implementation -- produces a Connection object that may be used for distributed transactions and almost always participates in connection pooling. This implementation works with a middle-tier transaction manager and almost always with a connection pooling manager.

(4)通过DataSource获得的driver,并不会用DriverManager注册。
回忆一下:DriverManager可以注册驱动。
Driver driver = new com.mysql.jdbc.Driver();
//注册驱动程序(可以注册多个驱动程序)
DriverManager.registerDriver(driver);

A driver that is accessed via a DataSource object does not register itself with the DriverManager. Rather, a DataSource object is retrieved though a lookup operation and then used to create a Connection object.
(5)如果是basic implementation,那么通过DataSource object获得的Connection与通过DriverManager获得的Connection是一样的。
With a basic implementation, the connection obtained through a DataSource object is identical to a connection obtained through the DriverManager facility.

3.1、DBCP连接池

DBCP 是 Apache 软件基金组织下的开源连接池实现,使用DBCP数据源,应用程序应在系统中增加如下两个 jar 文件: Commons-dbcp.jar:连接池的实现 Commons-pool.jar:连接池实现的依赖库 Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。 核心类:BasicDataSource 使用步骤 引入jar文件commons-dbcp-1.4.jar http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgicommons-pool-1.6.jar http://commons.apache.org/proper/commons-pool/download_pool.cgi
使用连接池,创建连接a) 硬编码方式b) 配置方式(db.properties)
package com.rk.dbcp;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

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

public class Demo
{
// 1. 硬编码方式实现连接池
@Test
public void testDbcp() throws SQLException
{
// DBCP连接池核心类
BasicDataSource dataSource = new BasicDataSource();
// 连接池参数配置: 连接字符串、驱动、用户、密码
dataSource.setUrl("jdbc:mysql:///testdb");//"jdbc:mysql://localhost:3306/testdb"
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("root");
// 连接池参数配置:初始化连接数、最大连接数 、最大空闲时间
dataSource.setInitialSize(3);
dataSource.setMaxActive(6);
dataSource.setMaxIdle(3000);

// 获取连接
Connection conn = dataSource.getConnection();
System.out.println(conn);
conn.prepareStatement("DELETE FROM T_Student WHERE Id=10").executeUpdate();
// 关闭
conn.close();
}

@Test
// 2. 【推荐】配置方式实现连接池  ,  便于维护
public void testProp() throws Exception
{
// 获取文件流
InputStream inStream = Demo.class.getResourceAsStream("db.properties");
//创建Properties对象
Properties prop = new Properties();
// 加载属性配置文件
prop.load(inStream);

// 根据prop配置,直接创建数据源对象
DataSource dataSource = BasicDataSourceFactory.createDataSource(prop);
// 获取连接
Connection conn = dataSource.getConnection();
System.out.println(conn);
conn.prepareStatement("DELETE FROM T_Student WHERE Id=10").executeUpdate();
// 关闭
conn.close();
}
}


配置方式实现DBCP连接池, 配置文件中的key与BaseDataSouce中的属性一样:
url=jdbc:mysql://localhost:3306/testdb
driverClassName=com.mysql.jdbc.Driver
username=root
password=root
initialSize=3
maxActive=6
maxIdle=3000

org.apache.commons.dbcp.BasicDataSource
(1)Apache的DBCP中的核心类BasicDataSource是对javax.sql.DataSource接口的Basic implementation。
Basic implementation of javax.sql.DataSource that is configured via JavaBeans properties.

org.apache.commons.dbcp.BasicDataSourceFactory
(1)BasicDataSourceFactory是一个JNDI object factory,用于创建一个BasicDataSource实例。
JNDI object factory that creates an instance of BasicDataSource.

3.2、C3P0连接池

C3P0连接池:最常用的连接池技术!Spring框架,默认支持C3P0连接池技术!核心类:ComboPooledDataSource使用:
1.下载,引入jar文件: c3p0-0.9.1.2.jar https://sourceforge.net/projects/c3p0/ http://www.mchange.com/projects/c3p0/
2. 使用连接池,创建连接a) 硬编码方式b) 配置方式(xml)
package com.rk.c3p0;

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;

import org.junit.Test;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class Demo
{
@Test
//1. 硬编码方式,使用C3P0连接池管理连接
public void testCode() throws PropertyVetoException, SQLException
{
// 创建连接池核心工具类
ComboPooledDataSource dataSource = new ComboPooledDataSource();
// 设置连接参数:url、驱动、用户密码、初始连接数、最大连接数
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/testdb");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setInitialPoolSize(3);
dataSource.setMaxPoolSize(6);
dataSource.setMaxIdleTime(1000);

// ---> 从连接池对象中,获取连接对象
Connection conn = dataSource.getConnection();
System.out.println(conn);
conn.prepareStatement("DELETE FROM T_Student WHERE Id=10").executeUpdate();
// 关闭
conn.close();
}

@Test
//2. XML配置方式,使用C3P0连接池管理连接
public void testXML() throws SQLException
{
// 创建c3p0连接池核心工具类
// 自动加载src下c3p0的配置文件【c3p0-config.xml】
ComboPooledDataSource dataSource = new ComboPooledDataSource();// 使用默认的配置
//ComboPooledDataSource dataSource = new ComboPooledDataSource("oracle_config");// 使用指定的配置

// ---> 从连接池对象中,获取连接对象
Connection conn = dataSource.getConnection();
System.out.println(conn);
conn.prepareStatement("DELETE FROM T_Student WHERE Id=10").executeUpdate();
// 关闭
conn.close();
}
}
c3p0-config.xml文件放在src目录下
<c3p0-config>
<default-config>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/testdb</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="initialPoolSize">3</property>
<property name="maxPoolSize">6</property>
<property name="maxIdleTime">1000</property>
</default-config>

<named-config name="oracle_config">
<property name="jdbcUrl">jdbc:mysql://localhost:3306/testdb</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="initialPoolSize">3</property>
<property name="maxPoolSize">6</property>
<property name="maxIdleTime">1000</property>
</named-config>

</c3p0-config>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  连接池 pool Connection