DBCP数据源连接池实现原理分析
前些天在调试公司系统的时候发现这样的一个问题:mysql数据库服务停止一段时间后再次重启后吗,tomcat服务无法请求数据库服务,调试了半天对这个问题进行定位解决,期间也搞了很多有关mysql数据库的知识,包括数据库连接池的问题,以前没有遇到问题的时候只知道数据库连接池这个概念和如何配置,但是当遇到问题的时候就要去看怎么实现了,比如很简单的默认的数据库连接池的个数是多少呢,我相信没有看过源代码的是不知道的,答案是8.下面就针对最近学习的org.apache.commons.dbcp.BasicDataSource这个数据源的连接池做一个分享吧。
分享之前有关数据库连接池的一些概念性的问题就不解释,可以参考http://www.cnblogs.com/duanxiaojun/p/5413502.html。
下面我们以一个完成的数据库操作来分析详细的dbcp数据源连接池实现的原理:
package cn.com.chnsys.dbcpDataSource; import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.commons.dbcp.BasicDataSource;
/**
*
* <p>dbcp数据源连接池分析</p>
*
* 类说明
*
* @author duanxj
* @version 1.0
*/
public class BasicDataSourceExample { public static void main(String[] args) {
//设置数据源基本配置项
DataSource dataSource = setupDataSource(args[0]);
//创建连接
Connection conn = null;
Statement stmt = null;
ResultSet rset = null;
try {
//创建连接对象
conn = dataSource.getConnection();
//创建Statement 对象,这里我们使用Statement prepareStatement也是一样的
stmt = conn.createStatement();
//创建结果返回集
rset = stmt.executeQuery(args[1]);
//得到查询影响记录数
int numcols = rset.getMetaData().getColumnCount();
while(rset.next()) {
for(int i=1;i<=numcols;i++) {
System.out.print("\t" + rset.getString(i));
}
System.out.println("");
}
} catch(SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
try { if (rset != null) rset.close(); } catch(Exception e) { }
try { if (stmt != null) stmt.close(); } catch(Exception e) { }
try { if (conn != null) conn.close(); } catch(Exception e) { }
}
} /**
* 创建数据源,并设置数据源基本配置项
* @param connectURI
* @return
*/
public static DataSource setupDataSource(String connectURI) {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("oracle.jdbc.driver.OracleDriver"); //设置驱动
ds.setUsername("root"); //设置用户名
ds.setPassword("root"); //设置密码
ds.setUrl(connectURI);//设置连接url
return ds;
}
/**
* 打印创建的数据源的配置项
* @param ds
*/
public static void printDataSourceStats(DataSource ds) {
BasicDataSource bds = (BasicDataSource) ds;
System.out.println("NumActive: " + bds.getNumActive());
System.out.println("NumIdle: " + bds.getNumIdle());
}
/**
* 关闭销毁
* @param ds
* @throws SQLException
*/
public static void shutdownDataSource(DataSource ds) throws SQLException {
BasicDataSource bds = (BasicDataSource) ds;
bds.close();
}
}
上面的代码大家应该能看懂,我们只说其中比较关键的。首先哟啊创建一个dataSource,并对这个dataSource设置一些必须的配置项(数据库驱动,URL,用户名,密码)等,然后关键的操作是在dataSource.getConnection();处,我们查看dbcp源码的实现就可以知道,datasource的配置只是配置一些有关的配置信息,真正的创建连接池pool的操作是在用户第一次去获取connection的时候创建的,下面是源码getConnection()的实现:
public Connection getConnection()
throws SQLException
{
return createDataSource().getConnection();
}
protected synchronized DataSource createDataSource()
throws SQLException
{
if (this.closed) {
throw new SQLException("Data source is closed");
} if (this.dataSource != null) {
return this.dataSource;
} ConnectionFactory driverConnectionFactory = createConnectionFactory(); createConnectionPool(); GenericKeyedObjectPoolFactory statementPoolFactory = null;
if (isPoolPreparedStatements()) {
statementPoolFactory = new GenericKeyedObjectPoolFactory(null, -1, (byte)0, 0L, 1, this.maxOpenPreparedStatements);
} createPoolableConnectionFactory(driverConnectionFactory, statementPoolFactory, this.abandonedConfig); createDataSourceInstance();
try
{
for (int i = 0; i < this.initialSize; i++)
this.connectionPool.addObject();
}
catch (Exception e) {
throw new SQLNestedException("Error preloading the connection pool", e);
} return this.dataSource;
}
上面的代码我做如下的分析:
首先我们看到这个createDataSource()方法是用synchronized修饰的我们知道这是线程安全的。
1 首先是在getConnection()方法中先是去创建一个createDataSource()。在createDataSource(),的方法中先判断该数据源是否关闭,如果关闭直接抛出异常,如果这个数据源已经创建成功则直接返回,否则去创建这个数据源,
2 创建数据源之前先创建数据库的连接工厂createConnectionFactory();在这个方法中主要是通过反射CLASS.FORNAME()创建一个数据库连接对象,其中涉及到的参数主要是driverClassName,url,validationQuery,username,password(对这些链接数据库的配置应该不陌生,这些参数的作用就是创建一个连接工厂)。
ConnectionFactory driverConnectionFactory = new DriverConnectionFactory(driver, this.url, this.connectionProperties);并返回创建成功的连接工厂示例在下面创建连接池createConnectionPool使用。
3 下面是真正的创建连接池的核心部分createConnectionPool();我们先看一下这个方法的具体实现,这里的前提是我们已经创建成功一个与数据库的连接对象,
protected void createConnectionPool()
{
GenericObjectPool gop;
GenericObjectPool gop;
if ((this.abandonedConfig != null) && (this.abandonedConfig.getRemoveAbandoned())) {
gop = new AbandonedObjectPool(null, this.abandonedConfig);
}
else {
gop = new GenericObjectPool();
}
gop.setMaxActive(this.maxActive);
gop.setMaxIdle(this.maxIdle);
gop.setMinIdle(this.minIdle);
gop.setMaxWait(this.maxWait);
gop.setTestOnBorrow(this.testOnBorrow);
gop.setTestOnReturn(this.testOnReturn);
gop.setTimeBetweenEvictionRunsMillis(this.timeBetweenEvictionRunsMillis);
gop.setNumTestsPerEvictionRun(this.numTestsPerEvictionRun);
gop.setMinEvictableIdleTimeMillis(this.minEvictableIdleTimeMillis);
gop.setTestWhileIdle(this.testWhileIdle);
this.connectionPool = gop;
}
在源码中我们可以看到使用了GenericObjectPool对象,这个是使用commons-pool的GenericObjectPool 实现的,创建一个GenericObjectPool对象并设置连接池的配置参数信息,(这里知道我们再配置文件中配置的选项是如何在源码中起作用的了吧~~)。然后返回这个GenericObjectPool(这个commons-pool的GenericObjectPool的源码后续我们再分析,大家只要知道这里是使用的commons-pool的GenericObjectPool创建了一个池 )。
3.1 创建成功 this.connectionPool后
就是创建GenericKeyedObjectPoolFactory(暂时不知道这个是个什么东东?????)
// 设置连接池工厂 createPoolableConnectionFactory(driverConnectionFactory, statementPoolFactory, abandonedConfig);
// 建立数据库连接池实例 createDataSourceInstance();
然后是根据我们配置文件中配置的初始的数据库连接池的大小去设置连接池的初始个数
// 根据配置,初始化建立一些数据库连接 =
try {
for (int i = 0 ; i < initialSize ; i++) {
connectionPool.addObject();
}
} catch (Exception e) {
throw new SQLNestedException("Error preloading the connection pool", e);
}
这样一个完成的数据源dataSource和连接池就创建完成了。
DBCP数据源连接池实现原理分析的更多相关文章
- dbcp数据源连接池
一.数据源连接池 我们之前利用jdbc连接数据库,每次都要创建连接对象,销毁连接对象,如果并发访问量比较大,这样肯定比较辣 浪费数据库的效率,我们可以像之前mybatis中缓存查询到的数据一样,可以把 ...
- 阶段3 1.Mybatis_07.Mybatis的连接池及事务_4 mybatis中使用unpooled配置连接池的原理分析
把之前的CRUD的代码src下的代码都复制过来 依赖项也都复制过来, 配置文件 整理一番 执行findAll方法的测试 查看日志的输出部分 修改程序池 再来执行findAll方法 Plooled从连接 ...
- JavaWeb之数据源连接池(1)---DBCP
何为数据源呢?也就是数据的来源.我在前面的一篇文章<JavaWeb之原生数据库连接>中,采用了mysql数据库,数据来源于mysql,那么mysql就是一种数据源.在实际工作中,除了mys ...
- JDBC数据源连接池(3)---Tomcat集成DBCP
此文续<JDBC数据源连接池(2)---C3P0>. Apache Tomcat作为一款JavaWeb服务器,内置了DBCP数据源连接池.在使用中,只要进行相应配置即可. 首先,确保Web ...
- JDBC数据源连接池(1)---DBCP
何为数据源呢?也就是数据的来源.我在前面的一篇文章<JDBC原生数据库连接>中,采用了mysql数据库,数据来源于mysql,那么mysql就是一种数据源.在实际工作中,除了mysql,往 ...
- JavaWeb之数据源连接池(3)---Tomcat
此文续 <JavaWeb之数据源连接池(2)---C3P0>. Apache Tomcat作为一款JavaWeb服务器,内置了DBCP数据源连接池.在使用中,只要进行相应配置即可. 首先, ...
- JavaWeb之数据源连接池(4)---自定义数据源连接池
[续上文<JavaWeb之数据源连接池(3)---Tomcat>] 我们已经 了解了DBCP,C3P0,以及Tomcat内置的数据源连接池,那么,这些数据源连接池是如何实现的呢?为了究其原 ...
- JDBC数据源连接池(4)---自定义数据源连接池
[续上文<JDBC数据源连接池(3)---Tomcat集成DBCP>] 我们已经 了解了DBCP,C3P0,以及Tomcat内置的数据源连接池,那么,这些数据源连接池是如何实现的呢?为了究 ...
- JavaWeb之数据源连接池(2)---C3P0
我们接着<JavaWeb之数据源连接池(1)---DBCP>继续介绍数据源连接池. 首先,在Web项目的WebContent--->WEB-INF--->lib文件夹中添加C3 ...
随机推荐
- 在 Typescript 2.0 中使用 @types 类型定义
在 Typescript 2.0 中使用 @type 类型定义 基于 Typescript 开发的时候,很麻烦的一个问题就是类型定义.导致在编译的时候,经常会看到一连串的找不到类型的提示.解决的方式经 ...
- 冒泡排序,sql分页语句
对数组中的数字进行排序 public int[] PopSmall(int[] IntArray) { ; ; i < IntArray.Length - ; i++) { ; j < I ...
- .html()和.text()的区别
在页面调用接口显示数据的时候,正常情况下.html()和.text()都可以显示数据内容,但是在特殊情况下,比如接口中这个参数为空的时候就表现出差距了,.html()显示的是空白,而.text()显示 ...
- strcpy和memcpy的区别(转载)
strcpy和memcpy都是标准C库函数,它们有下面的特点.strcpy提供了字符串的复制.即strcpy只用于字符串复制,并且它不仅复制字符串内容之外,还会复制字符串的结束符. 已知strcpy函 ...
- PostgreSQL 在centos 7下的安装配置
安装postgresql: sudo yum install postgresql-server 初始化数据库: sudo postgresql-setup initdb 启动数据库: sudo sy ...
- js中array的join和concat的区别
首先:concat方法定义:concat() 方法用于连接两个或多个数组.该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本.举例说明:1 /*concat()结果返回的是一个数组*/ 2 3 ...
- What is Agile
Agile is a set of Values, Principles and Practices, that will change your behavior to will create gr ...
- 使用二分法查找mobile文件中区号归属地
#!/usr/bin/env python #coding:utf-8 ''' Created on 2015年12月8日 @author: DL @Description: 使用二分法查找mobil ...
- Java上面出现这个错误如何解决关于XML的
Java上面出现这个错误如何解决关于XML的 2015-01-07 14:49 hejiashun11325 | 分类:JAVA相关 | 浏览265次 The type org.xmlpull.v1. ...
- 策略模式(strategy pattern)
策略模式在java集合中的TreeSet和TreeMap中得到了很好的应用,我们可以实现Comparator接口实现Compareto()方法来定义自己的排序规则,然后通过TreeSet,TreeMa ...