千家信息网

开源组件:(1)DBCP和C3P0

发表于:2024-09-22 作者:千家信息网编辑
千家信息网最后更新 2024年09月22日,一种技术的出现,要么是解决实际问题,要么是优化现有技术。数据库连接池技术的出现,是为了优化数据库连接操作的性能。在使用JDBC进行数据库开发的时候,一般经历这样一个过程:1)加载数据库的驱动2)建立数
千家信息网最后更新 2024年09月22日开源组件:(1)DBCP和C3P0

一种技术的出现,要么是解决实际问题,要么是优化现有技术。数据库连接池技术的出现,是为了优化数据库连接操作的性能。

在使用JDBC进行数据库开发的时候,一般经历这样一个过程:

1)加载数据库的驱动

2)建立数据库的连接(Connection)

3)创建SQL语句声明(Statement)

4)执行更新(executeUpdate)或查询(executeQuery)


本文中讲的数据库连接池,只是针对Connection的部分的优化。


学习连接池

a. 自定义一个连接池

b. 学习优秀的连接池组件

1)DBCP

2)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 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();                        //将指定数量(init_cont)加入到连接池中                        for(int i=0;i0)                        {                                return freeConnections.removeFirst();                        }                        else if(current_count" + 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 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();                        //将指定数量(init_cont)加入到连接池中                        for(int i=0;i0)                        {                                return freeConnections.removeFirst();                        }                        else if(current_count" + 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 {  /**   * 

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

Attempts to establish a connection with the data source that * this DataSource 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.cgi

commons-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/testdbdriverClassName=com.mysql.jdbc.Driverusername=rootpassword=rootinitialSize=3maxActive=6maxIdle=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目录下

                        jdbc:mysql://localhost:3306/testdb                com.mysql.jdbc.Driver                root                root                3                6                1000                                jdbc:mysql://localhost:3306/testdb                com.mysql.jdbc.Driver                root                root                3                6                1000        


0