Browse Source

README中增加UML图,方便理解

master
ZhangZiSheng001 6 years ago
parent
commit
43a8796d8c
8 changed files with 362 additions and 293 deletions
  1. +220
    -263
      README.md
  2. +3
    -3
      src/main/java/cn/zzs/dbcp/JDBCUtils.java
  3. +7
    -7
      src/main/resources/dbcp.properties
  4. +3
    -3
      src/main/resources/log4j.properties
  5. +36
    -0
      src/main/resources/uml/BasicDataSourceUML
  6. +35
    -0
      src/main/resources/uml/DelegatingConnectionUML
  7. +40
    -0
      src/main/resources/uml/GenericObjectPoolUML
  8. +18
    -17
      src/test/java/cn/zzs/dbcp/BasicDataSourceTest.java

+ 220
- 263
README.md View File

@@ -1,58 +1,8 @@
# 目录
* [简介](#简介)
* [使用例子](#使用例子)
* [需求](#需求)
* [工程环境](#工程环境)
* [主要步骤](#主要步骤)
* [创建项目](#创建项目)
* [引入依赖](#引入依赖)
* [编写`jdbc.prperties`](#编写jdbcprperties)
* [获取连接池和获取连接](#获取连接池和获取连接)
* [编写测试类](#编写测试类)
* [配置文件详解](#配置文件详解)
* [数据库连接参数](#数据库连接参数)
* [连接池基本参数](#连接池基本参数)
* [连接存活参数](#连接存活参数)
* [连接检查参数](#连接检查参数)
* [缓存语句](#缓存语句)
* [事务相关参数](#事务相关参数)
* [连接泄漏回收参数](#连接泄漏回收参数)
* [其他](#其他)
* [源码分析](#源码分析)
* [创建数据源](#创建数据源)
* [`BasicDataSource.getConnection()`](#basicdatasourcegetconnection)
* [`BasicDataSource.createDataSource()`](#basicdatasourcecreatedatasource)
* [获取连接对象](#获取连接对象)
* [`PoolingDataSource.getConnection()`](#poolingdatasourcegetconnection)
* [`GenericObjectPool.borrowObject()`](#genericobjectpoolborrowobject)
* [`GenericObjectPool.create()`](#genericobjectpoolcreate)
* [`PoolableConnectionFactory.makeObject()`](#poolableconnectionfactorymakeobject)
* [空闲对象回收器`Evictor`](#空闲对象回收器evictor)
* [`BasicDataSource.startPoolMaintenance()`](#basicdatasourcestartpoolmaintenance)
* [`BaseGenericObjectPool.setTimeBetweenEvictionRunsMillis(long)`](#basegenericobjectpoolsettimebetweenevictionrunsmillislong)
* [`BaseGenericObjectPool.startEvictor(long)`](#basegenericobjectpoolstartevictorlong)
* [`EvictionTimer.schedule(Evictor, long, long)`](#evictiontimerscheduleevictor-long-long)
* [`BaseGenericObjectPool.Evictor`](#basegenericobjectpoolevictor)
* [`GenericObjectPool.evict()`](#genericobjectpoolevict)
* [通过`JNDI`获取数据源对象](#通过jndi获取数据源对象)
* [需求](#需求-1)
* [引入依赖](#引入依赖-1)
* [编写`context.xml`](#编写contextxml)
* [编写`web.xml`](#编写webxml)
* [编写`jsp`](#编写jsp)
* [测试结果](#测试结果)
* [使用`DBCP`测试两阶段提交](#使用dbcp测试两阶段提交)
* [准备工作](#准备工作)
* [`mysql`的`XA`事务使用](#mysql的xa事务使用)
* [引入依赖](#引入依赖-2)
* [获取`BasicManagedDataSource`](#获取basicmanageddatasource)
* [编写两阶段提交的代码](#编写两阶段提交的代码)
# 简介 # 简介
`DBCP`用于创建和管理连接,利用“池”的方式复用连接减少资源开销,和其他连接池一样,也具有连接数控制、连接可靠性测试、连接泄露控制、缓存语句等功能。目前,`tomcat`自带的连接池就是`DBCP`,Spring开发组也推荐使用`DBCP`。
`DBCP`用于创建和管理连接,利用“池”的方式复用连接减少资源开销,和其他连接池一样,也具有连接数控制、连接有效性检测、连接泄露控制、缓存语句等功能。目前,`tomcat`自带的连接池就是`DBCP`,Spring开发组也推荐使用`DBCP`,阿里的`druid`也是参照`DBCP`开发出来的。
`DBCP`除了我们熟知的使用方式外,还支持通过`JNDI`获取数据源,并支持获取`JTA`或`XA`事务中用于`2PC`(两阶段提交)的连接对象,本文也将以例子说明。 `DBCP`除了我们熟知的使用方式外,还支持通过`JNDI`获取数据源,并支持获取`JTA`或`XA`事务中用于`2PC`(两阶段提交)的连接对象,本文也将以例子说明。
@@ -64,40 +14,43 @@
4. `DBCP`其他特性的使用方法,如`JNDI`和`JTA`支持。 4. `DBCP`其他特性的使用方法,如`JNDI`和`JTA`支持。
# 使用例子 # 使用例子
## 需求 ## 需求
使用`DBCP`连接池获取连接对象,对用户数据进行简单的增删改查。 使用`DBCP`连接池获取连接对象,对用户数据进行简单的增删改查。
## 工程环境 ## 工程环境
`JDK`:1.8.0_201
`maven`:3.6.1
`JDK`:1.8.0_201
`maven`:3.6.1
`IDE`:eclipse 4.12
`IDE`:eclipse 4.12
`mysql-connector-java`:8.0.15
`mysql-connector-java`:8.0.15
`mysql`:5.7
`mysql`:5.7.28
`DBCP`:2.6.0
`DBCP`:2.6.0
## 主要步骤 ## 主要步骤
1. 编写`jdbc.properties`,设置数据库连接参数和连接池基本参数等。
1. 编写`dbcp.properties`,设置数据库连接参数和连接池基本参数等。
2. 通过`BasicDataSourceFactory`加载`jdbc.properties`,并获得`BasicDataDource`对象。
2. 通过`BasicDataSourceFactory`加载`dbcp.properties`,并获得`BasicDataDource`对象。
3. 通过`BasicDataDource`对象获取`Connection`对象。
3. 通过`BasicDataDource`对象获取`Connection`对象。
4. 使用`Connection`对象对用户表进行增删改查。
4. 使用`Connection`对象对用户表进行增删改查。
## 创建项目 ## 创建项目
项目类型Maven Project,打包方式war(其实jar也可以,之所以使用war是为了测试`JNDI`)。
项目类型Maven Project,打包方式war(其实jar也可以,之所以使用war是为了测试`JNDI`)。
## 引入依赖 ## 引入依赖
```xml ```xml
<!-- junit --> <!-- junit -->
<dependency> <dependency>
@@ -126,46 +79,50 @@
</dependency> </dependency>
``` ```
## 编写`jdbc.prperties`
路径`resources`目录下,因为是入门例子,这里仅给出数据库连接参数和连接池基本参数,后面源码会对配置参数进行详细说明。另外,数据库`sql`脚本也在该目录下。
## 编写`dbcp.prperties`
路径`resources`目录下,因为是入门例子,这里仅给出数据库连接参数和连接池基本参数,后面源码会对配置参数进行详细说明。另外,数据库`sql`脚本也在该目录下。
```properties ```properties
#数据库基本配置
#连接基本属性
driverClassName=com.mysql.cj.jdbc.Driver driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/github_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true url=jdbc:mysql://localhost:3306/github_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true
username=root username=root
password=root password=root
#-------------连接数据相关参数--------------------------------
#初始化连接:连接池启动时创建的初始化连接数量
#-------------连接池大小和连接超时参数--------------------------------
#初始化连接数量:连接池启动时创建的初始化连接数量
#默认为0 #默认为0
initialSize=0 initialSize=0
#最大活动连接
#连接池在同一时间能够分配的最大活动连接的数量, 如果设置为非正数则表示不限制
#最大活动连接数量:连接池在同一时间能够分配的最大活动连接的数量, 如果设置为数则表示不限制
#默认为8 #默认为8
maxActive=8
#最大空闲连接
#连接池中容许保持空闲状态的最大连接数量,超过的空闲连接将被释放,如果设置为负数表示不限制
maxTotal=8
#最大空闲连接:连接池中容许保持空闲状态的最大连接数量,超过的空闲连接将被释放,如果设置为负数表示不限制
#默认为8 #默认为8
maxIdle=8 maxIdle=8
#最小空闲连接
#连接池中容许保持空闲状态的最小连接数量,低于这个数量将创建新的连接,如果设置为0则不创建
#最小空闲连接:连接池中容许保持空闲状态的最小连接数量,低于这个数量将创建新的连接,如果设置为0则不创建
#注意:timeBetweenEvictionRunsMillis为正数时,这个参数才能生效。
#默认为0 #默认为0
minIdle=0 minIdle=0
#最大等待时间 #最大等待时间
#当没有可用连接时,连接池等待连接被归还的最大时间(以毫秒计数),超过时间则抛出异常,如果设置为-1表示无限等待
#默认无限
maxWait=-1
#当没有可用连接时,连接池等待连接被归还的最大时间(以毫秒计数),超过时间则抛出异常,如果设置为<=0表示无限等待
#默认-1
maxWaitMillis=-1
``` ```
## 获取连接池和获取连接
项目中编写了`JDBCUtil`来初始化连接池、获取连接、管理事务和释放资源等,具体参见项目源码。
## 获取连接池和获取连接
项目中编写了`JDBCUtils`来初始化连接池、获取连接、管理事务和释放资源等,具体参见项目源码。
路径:`cn.zzs.dbcp` 路径:`cn.zzs.dbcp`
```java ```java
// 导入配置文件 // 导入配置文件
Properties properties = new Properties(); Properties properties = new Properties();
InputStream in = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
InputStream in = JDBCUtil.class.getClassLoader().getResourceAsStream("dbcp.properties");
properties.load(in); properties.load(in);
// 根据配置文件内容获得数据源对象 // 根据配置文件内容获得数据源对象
DataSource dataSource = BasicDataSourceFactory.createDataSource(properties); DataSource dataSource = BasicDataSourceFactory.createDataSource(properties);
@@ -174,6 +131,7 @@ maxWait=-1
``` ```
## 编写测试类 ## 编写测试类
这里以保存用户为例,路径test目录下的`cn.zzs.dbcp`。 这里以保存用户为例,路径test目录下的`cn.zzs.dbcp`。
```java ```java
@@ -185,9 +143,9 @@ maxWait=-1
PreparedStatement statement = null; PreparedStatement statement = null;
try { try {
// 获得连接 // 获得连接
connection = JDBCUtil.getConnection();
connection = JDBCUtils.getConnection();
// 开启事务设置非自动提交 // 开启事务设置非自动提交
JDBCUtil.startTrasaction();
JDBCUtils.startTrasaction();
// 获得Statement对象 // 获得Statement对象
statement = connection.prepareStatement(sql); statement = connection.prepareStatement(sql);
// 设置参数 // 设置参数
@@ -199,13 +157,13 @@ maxWait=-1
// 执行 // 执行
statement.executeUpdate(); statement.executeUpdate();
// 提交事务 // 提交事务
JDBCUtil.commit();
JDBCUtils.commit();
} catch(Exception e) { } catch(Exception e) {
JDBCUtil.rollback();
JDBCUtils.rollback();
log.error("保存用户失败", e); log.error("保存用户失败", e);
} finally { } finally {
// 释放资源 // 释放资源
JDBCUtil.release(connection, statement, null);
JDBCUtils.release(connection, statement, null);
} }
} }
``` ```
@@ -214,9 +172,10 @@ maxWait=-1
# 配置文件详解 # 配置文件详解
这部分内容从网上参照过来,同样的内容发的到处都是,暂时没找到出处。因为内容太过杂乱,而且最新版本更新了不少内容,所以我花了好大功夫才改好,后面找到出处再补上参考资料吧。
这部分内容从网上参照过来,同样的内容发的到处都是,暂时没找到出处。因为内容太过杂乱,而且最新版本更新了不少内容,所以我花了好大功夫才改好,后面找到出处再补上参考资料吧。
## 基本连接属性
## 数据库连接参数
注意,这里在`url`后面拼接了多个参数用于避免乱码、时区报错问题。 补充下,如果不想加入时区的参数,可以在`mysql`命令窗口执行如下命令:`set global time_zone='+8:00'`。 注意,这里在`url`后面拼接了多个参数用于避免乱码、时区报错问题。 补充下,如果不想加入时区的参数,可以在`mysql`命令窗口执行如下命令:`set global time_zone='+8:00'`。
@@ -227,11 +186,12 @@ username=root
password=root password=root
``` ```
## 连接池基本参数
这几个参数都比较常用,具体设置多少需根据项目调整。
## 连接池大小参数
这几个参数都比较常用,具体设置多少需根据项目调整。
```properties ```properties
#-------------连接数据相关参数--------------------------------
#-------------连接池大小和连接超时参数--------------------------------
#初始化连接数量:连接池启动时创建的初始化连接数量 #初始化连接数量:连接池启动时创建的初始化连接数量
#默认为0 #默认为0
initialSize=0 initialSize=0
@@ -253,24 +213,39 @@ minIdle=0
#当没有可用连接时,连接池等待连接被归还的最大时间(以毫秒计数),超过时间则抛出异常,如果设置为<=0表示无限等待 #当没有可用连接时,连接池等待连接被归还的最大时间(以毫秒计数),超过时间则抛出异常,如果设置为<=0表示无限等待
#默认-1 #默认-1
maxWaitMillis=-1 maxWaitMillis=-1
#连接池创建的连接的默认的数据库名,如果是使用DBCP的XA连接必须设置,不然注册不了多个资源管理器
#defaultCatalog=github_demo
#连接池创建的连接的默认的schema。如果是mysql,这个设置没什么用。
#defaultSchema=github_demo
``` ```
## 连接存活参数
## 缓存语句
缓存语句在`mysql`下建议关闭。
```properties ```properties
#资源池中资源最小空闲时间(单位为毫秒),达到此值后将被移除。
#默认值1000*60*30 = 30分钟
minEvictableIdleTimeMillis=1800000
#-------------缓存语句--------------------------------
#是否缓存preparedStatement,也就是PSCache。
#PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭
#默认为false
poolPreparedStatements=false
#资源池中资源最小空闲时间(单位为毫秒),达到此值后将被移除。但是会保证minIdle
#默认值-1
#softMinEvictableIdleTimeMillis=-1
#缓存PreparedStatements的最大个数
#默认为-1
#注意:poolPreparedStatements为true时,这个参数才有效
maxOpenPreparedStatements=-1
#缓存read-only和auto-commit状态。设置为true的话,所有连接的状态都会是一样的。
#默认是true
cacheState=true
#连接最大存活时间。非正数表示不限制
#默认-1
maxConnLifetimeMillis=-1
``` ```
## 连接检查参数 ## 连接检查参数
针对连接失效和连接泄露的问题,建议开启`testWhileIdle`,而不是开启`testOnReturn`或`testOnBorrow`,从(从性能考虑)。
针对连接失效和连接泄露的问题,建议开启`testWhileIdle`,而不是开启`testOnReturn`或`testOnBorrow`(从性能考虑)。
```properties ```properties
#-------------连接检查情况-------------------------------- #-------------连接检查情况--------------------------------
@@ -283,53 +258,49 @@ validationQuery=select 1 from dual
validationQueryTimeout=-1 validationQueryTimeout=-1
#是否从池中取出连接前进行检验。 #是否从池中取出连接前进行检验。
#默认为true。不建议开启。
#默认为true
testOnBorrow=true testOnBorrow=true
#是否在归还到池中前进行检验 #是否在归还到池中前进行检验
#默认为false #默认为false
testOnReturn=false testOnReturn=false
#是否开启空闲对象回收器。
#是否开启空闲资源回收器。
#默认为false #默认为false
testWhileIdle=false testWhileIdle=false
#空闲对象回收器的检测周期(单位为毫秒)。
#默认-1。即空闲对象回收器不工作。
#空闲资源的检测周期(单位为毫秒)。
#默认-1。即空闲资源回收器不工作。
timeBetweenEvictionRunsMillis=-1 timeBetweenEvictionRunsMillis=-1
#做空闲对象回收器时,每次的采样数。
#做空闲资源回收器时,每次的采样数。
#默认3,单位毫秒。如果设置为-1,就是对所有连接做空闲监测。 #默认3,单位毫秒。如果设置为-1,就是对所有连接做空闲监测。
numTestsPerEvictionRun=3 numTestsPerEvictionRun=3
#空闲对象回收器的回收策略
#资源池中资源最小空闲时间(单位为毫秒),达到此值后将被移除。
#默认值1000*60*30 = 30分钟
minEvictableIdleTimeMillis=1800000
#资源池中资源最小空闲时间(单位为毫秒),达到此值后将被移除。但是会保证minIdle
#默认值-1
#softMinEvictableIdleTimeMillis=-1
#空闲资源回收策略
#默认org.apache.commons.pool2.impl.DefaultEvictionPolicy #默认org.apache.commons.pool2.impl.DefaultEvictionPolicy
#如果要自定义的话,需要实现EvictionPolicy重写evict方法 #如果要自定义的话,需要实现EvictionPolicy重写evict方法
evictionPolicyClassName=org.apache.commons.pool2.impl.DefaultEvictionPolicy evictionPolicyClassName=org.apache.commons.pool2.impl.DefaultEvictionPolicy
```
## 缓存语句
缓存语句在`mysql`下建议关闭。
```properties
#-------------缓存语句--------------------------------
#是否缓存PreparedStatements
#PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭
#默认为false
poolPreparedStatements=false
#缓存PreparedStatements的最大个数
#默认为-1
#注意:poolPreparedStatements为true时,这个参数才有效
maxOpenPreparedStatements=-1
#缓存read-only和auto-commit状态。设置为true的话,所有连接的状态都会是一样的。
#默认是true
cacheState=true
#连接最大存活时间。非正数表示不限制
#默认-1
maxConnLifetimeMillis=-1
#当达到maxConnLifetimeMillis被关闭时,是否打印相关消息
#默认true
#注意:maxConnLifetimeMillis设置为正数时,这个参数才有效
logExpiredConnections=true
``` ```
## 事务相关参数 ## 事务相关参数
这里的参数主要和事务相关,一般默认就行。 这里的参数主要和事务相关,一般默认就行。
@@ -357,11 +328,6 @@ autoCommitOnReturn=true
#默认true #默认true
rollbackOnReturn=true rollbackOnReturn=true
#连接池创建的连接的默认的数据库名,如果是使用DBCP的XA连接必须设置,不然注册不了多个资源管理器
#defaultCatalog=github_demo
#连接池创建的连接的默认的schema。如果是mysql,这个设置没什么用。
#defaultSchema=github_demo
``` ```
## 连接泄漏回收参数 ## 连接泄漏回收参数
@@ -375,9 +341,9 @@ rollbackOnReturn=true
#注意:这个机制在(getNumIdle() < 2) and (getNumActive() > (getMaxActive() - 3))时被触发 #注意:这个机制在(getNumIdle() < 2) and (getNumActive() > (getMaxActive() - 3))时被触发
removeAbandonedOnBorrow=false removeAbandonedOnBorrow=false
#当未使用的时间超过removeAbandonedTimeout时,是否视该连接为泄露连接并删除
#当未使用的时间超过removeAbandonedTimeout时,是否视该连接为泄露连接并删除(空闲evictor检测)
#默认为false #默认为false
#注意:当空闲对象回收器开启才生效
#注意:当空闲资源回收器开启才生效
removeAbandonedOnMaintenance=false removeAbandonedOnMaintenance=false
#泄露的连接可以被删除的超时值, 单位秒 #泄露的连接可以被删除的超时值, 单位秒
@@ -399,11 +365,7 @@ abandonedUsageTracking=false
这部分参数比较少用。 这部分参数比较少用。
```properties ```properties
#当达到maxConnLifetimeMillis被关闭时,是否打印相关消息
#默认true
#注意:maxConnLifetimeMillis设置为正数时,这个参数才有效
logExpiredConnections=true
#-------------其他--------------------------------
#是否使用快速失败机制 #是否使用快速失败机制
#默认为空,由驱动决定 #默认为空,由驱动决定
fastFailValidation=false fastFailValidation=false
@@ -442,15 +404,17 @@ accessToUnderlyingConnectionAllowed=false
``` ```
# 源码分析 # 源码分析
通过使用例子可知,`DBCP`的`BasicDataSource`是我们获取连接对象的入口,至于`BasicDataSourceFactory`只是创建和初始化`BasicDataSource`实例,它的代码就不看了。这里直接从`BasicDataSource`的`getConnection()`方法开始分析。
注意:考虑篇幅和可读性,以下代码经过删减,仅保留所需部分。
注意:考虑篇幅和可读性,以下代码经过删减,仅保留所需部分。
## 创建数据源和连接池
## 创建数据源
研究数据源创建之前,先来看下`DBCP`的几种数据源:
研究之前,先来看下`BasicDataSource`的`UML`图:
![BasicDataSource的UML图](https://github.com/ZhangZiSheng001/dbcp-demo/tree/master/img/BasicDataSource.png)
这里介绍下这几个类的作用:
类名|描述 类名|描述
-|- -|-
@@ -458,15 +422,20 @@ accessToUnderlyingConnectionAllowed=false
`BasicManagedDataSource`|`BasicDataSource`的子类,用于创建支持`XA`事务或`JTA`事务的连接 `BasicManagedDataSource`|`BasicDataSource`的子类,用于创建支持`XA`事务或`JTA`事务的连接
`PoolingDataSource`|`BasicDataSource`中实际调用的数据源,可以说`BasicDataSource`只是封装了`PoolingDataSource` `PoolingDataSource`|`BasicDataSource`中实际调用的数据源,可以说`BasicDataSource`只是封装了`PoolingDataSource`
`ManagedDataSource`|`PoolingDataSource`的子类,用于支持`XA`事务或`JTA`事务的连接。是`BasicManagedDataSource`中实际调用的数据源,可以说`BasicManagedDataSource`只是封装了`ManagedDataSource` `ManagedDataSource`|`PoolingDataSource`的子类,用于支持`XA`事务或`JTA`事务的连接。是`BasicManagedDataSource`中实际调用的数据源,可以说`BasicManagedDataSource`只是封装了`ManagedDataSource`
`InstanceKeyDataSource`|用于支持`JDNI`环境的数据源
`PerUserPoolDataSource`|`InstanceKeyDataSource`的子类,针对每个用户会单独分配一个连接池,每个连接池可以设置不同属性。例如以下需求,相比user,`admin`可以创建更多地连接以保证
`SharedPoolDataSource`|`InstanceKeyDataSource`的子类,不同用户共享一个连接池
本文的源码分析仅会涉及到`BasicDataSource`(包含它封装的`PoolingDataSource`),其他的数据源暂时不扩展。
另外,为了支持`JNDI`,`DBCP`也提供了相应的类。
### `BasicDataSource.getConnection()`
| 类名 | 描述 |
| ----------------------- | ------------------------------------------------------------ |
| `InstanceKeyDataSource` | 用于支持`JDNI`环境的数据源 |
| `PerUserPoolDataSource` | `InstanceKeyDataSource`的子类,针对每个用户会单独分配一个连接池,每个连接池可以设置不同属性。例如以下需求,相比user,`admin`可以创建更多地连接以保证 |
| `SharedPoolDataSource` | `InstanceKeyDataSource`的子类,不同用户共享一个连接池 |
本文的源码分析仅会涉及到`BasicDataSource`(包含它封装的`PoolingDataSource`),其他的数据源暂时不扩展。
`BasicDataSource`是在第一次被调用获取获取连接时才创建`PoolingDataSource`对象。
### BasicDataSource.getConnection()
`BasicDataSourceFactory`只是简单地`new`了一个`BasicDataSource`对象并初始化配置参数,此时真正的数据源(`PoolingDataSource`)以及连接池(`GenericObjectPool<PoolableConnection>`)并没有创建,而创建的时机为我们第一次调用`getConnection()`的时候。因此,本文直接从`BasicDataSource`的`getConnection()`方法开始分析。
```java ```java
public Connection getConnection() throws SQLException { public Connection getConnection() throws SQLException {
@@ -474,104 +443,79 @@ accessToUnderlyingConnectionAllowed=false
} }
``` ```
### `BasicDataSource.createDataSource()`
接下来的方法又会涉及到四个类,如下:
### BasicDataSource.createDataSource()
| 类名 | 描述 |
| --------------------------- | ------------------------------------------------------------ |
| `ConnectionFactory` | 用于生成原生的Connection对象 |
| `PoolableConnectionFactory` | 用于生成包装过的Connection对象,持有`ConnectionFactory`对象的引用 |
| `GenericObjectPool` | 数据库连接池,用于管理连接。持有`PoolableConnectionFactory`对象的引用 |
| `PoolingDataSource` | 数据源,持有`GenericObjectPool`的引用。我们调用`BasicDataSource`获取连接对象,实际上调用的是它的`getConnection()`方法 |
这个方法会创建数据源和连接池,整个过程可以概括为以下几步:
1. 注册`MBean`,用于支持`JMX`;
2. 创建连接池对象`GenericObjectPool<PoolableConnection>`;
3. 创建数据源对象`PoolingDataSource<PoolableConnection>`;
4. 初始化连接数;
5. 开启空闲资源回收线程(如果设置`timeBetweenEvictionRunsMillis`为正数)。
```java ```java
// 数据源
private volatile DataSource dataSource;
// 连接池
private volatile GenericObjectPool<PoolableConnection> connectionPool;
protected DataSource createDataSource() throws SQLException {
if (closed) {
throw new SQLException("Data source is closed");
}
if (dataSource != null) {
return dataSource;
}
protected DataSource createDataSource() throws SQLException {
if(closed) {
throw new SQLException("Data source is closed");
}
if(dataSource != null) {
return dataSource;
}
synchronized (this) {
if (dataSource != null) {
return dataSource;
}
// 注册MBean,用于支持JMX,这方面的内容不在这里扩展
jmxRegister();
synchronized(this) {
if(dataSource != null) {
return dataSource;
}
// 注册MBean,用于支持JMX,这方面的内容不在这里扩展
jmxRegister();
// 创建原生Connection工厂:本质就是持有数据库驱动对象和几个连接参数
final ConnectionFactory driverConnectionFactory = createConnectionFactory();
// 将driverConnectionFactory包装成池化Connection工厂
PoolableConnectionFactory poolableConnectionFactory = createPoolableConnectionFactory(driverConnectionFactory);
// 设置PreparedStatements缓存(其实在这里可以发现,上面创建池化工厂时就设置了缓存,这里没必要再设置一遍)
poolableConnectionFactory.setPoolStatements(poolPreparedStatements);
poolableConnectionFactory.setMaxOpenPreparedStatements(maxOpenPreparedStatements);
// 创建数据库连接池对象GenericObjectPool,用于管理连接
// BasicDataSource将持有GenericObjectPool对象
createConnectionPool(poolableConnectionFactory);
// 创建PoolingDataSource对象
// 该对象持有GenericObjectPool对象的引用
DataSource newDataSource = createDataSourceInstance();
newDataSource.setLogWriter(logWriter);
// 根据我们设置的initialSize创建初始连接
for(int i = 0; i < initialSize; i++) {
connectionPool.addObject();
}
// 开启连接池的evictor线程
startPoolMaintenance();
// 最后BasicDataSource将持有上面创建的PoolingDataSource对象
dataSource = newDataSource;
return dataSource;
}
}
```
以上方法涉及到几个类,这里再补充下`UML`图。
// 创建原生Connection工厂:本质就是持有数据库驱动对象和几个连接参数
final ConnectionFactory driverConnectionFactory = createConnectionFactory();
![GenericObjectPool的UML图](https://github.com/ZhangZiSheng001/dbcp-demo/tree/master/img/GenericObjectPool.png)
// 将driverConnectionFactory包装成池化Connection工厂
boolean success = false;
PoolableConnectionFactory poolableConnectionFactory;
try {
poolableConnectionFactory = createPoolableConnectionFactory(driverConnectionFactory);
// 设置PreparedStatements缓存(其实在这里可以发现,上面创建池化工厂时就设置了缓存,这里没必要再设置一遍)
poolableConnectionFactory.setPoolStatements(poolPreparedStatements);
poolableConnectionFactory.setMaxOpenPreparedStatements(maxOpenPreparedStatements);
success = true;
} catch (final SQLException se) {
throw se;
} catch (final RuntimeException rte) {
throw rte;
} catch (final Exception ex) {
throw new SQLException("Error creating connection factory", ex);
}
if (success) {
// 创建数据库连接池对象GenericObjectPool,用于管理连接
// BasicDataSource将持有GenericObjectPool对象
createConnectionPool(poolableConnectionFactory);
}
// 创建PoolingDataSource对象
//该对象持有GenericObjectPool对象的引用
DataSource newDataSource;
success = false;
try {
newDataSource = createDataSourceInstance();
newDataSource.setLogWriter(logWriter);
success = true;
} catch (final SQLException se) {
throw se;
} catch (final RuntimeException rte) {
throw rte;
} catch (final Exception ex) {
throw new SQLException("Error creating datasource", ex);
} finally {
if (!success) {
closeConnectionPool();
}
}
| 类名 | 描述 |
| --------------------------- | ------------------------------------------------------------ |
| `DriverConnectionFactory` | 用于生成原生的Connection对象 |
| `PoolableConnectionFactory` | 用于生成池化的Connection对象,持有`ConnectionFactory`对象的引用 |
| `GenericObjectPool` | 数据库连接池,用于管理连接。持有`PoolableConnectionFactory`对象的引用 |
// 根据我们设置的initialSize创建初始连接
try {
for (int i = 0; i < initialSize; i++) {
connectionPool.addObject();
}
} catch (final Exception e) {
closeConnectionPool();
throw new SQLException("Error preloading the connection pool", e);
}
## 获取连接对象
// 开启连接池的evictor线程
startPoolMaintenance();
// 最后BasicDataSource将持有上面创建的PoolingDataSource对象
dataSource = newDataSource;
return dataSource;
}
}
```
上面已经大致分析了数据源和连接池对象的获取过程,接下来研究下连接对象的获取。在此之前先了解下`DBCP`中几个`Connection`实现类。
## 获取连接对象
上面已经大致分析了数据源对象的获取过程,接下来研究下连接对象的获取。在此之前先了解下`DBCP`中几个`Connection`实现类。
![DelegatingConnection的UML图](https://github.com/ZhangZiSheng001/dbcp-demo/tree/master/img/DelegatingConnection.png)
类名|描述 类名|描述
-|- -|-
@@ -582,9 +526,15 @@ accessToUnderlyingConnectionAllowed=false
`ManagedConnection`|用于包装原生的`Connection`,支持`JTA`和`XA`事务 `ManagedConnection`|用于包装原生的`Connection`,支持`JTA`和`XA`事务
`PoolGuardConnectionWrapper`|用于包装`PoolableConnection`,当`accessToUnderlyingConnectionAllowed`才能获取底层连接对象。我们获取到的就是这个对象 `PoolGuardConnectionWrapper`|用于包装`PoolableConnection`,当`accessToUnderlyingConnectionAllowed`才能获取底层连接对象。我们获取到的就是这个对象
另外,这里先概括下获得连接的整个过程:
1. 如果设置了`removeAbandonedOnBorrow`,达到条件会进行检测;
2. 从连接池中获取连接,如果没有就通过工厂创建(通过`DriverConnectionFactory`创建原生对象,再通过`PoolableConnectionFactory`包装为池化对象);
3. 通过工厂重新初始化连接对象;
4. 如果设置了`testOnBorrow`或者`testOnCreate`,会通过工厂校验连接有效性;
5. 使用`PoolGuardConnectionWrapper`包装连接对象,并返回给客户端
### `PoolingDataSource.getConnection()`
### PoolingDataSource.getConnection()
前面已经说过,`BasicDataSource`本质上是调用`PoolingDataSource`的方法来获取连接,所以这里从`PoolingDataSource.getConnection()`开始研究。 前面已经说过,`BasicDataSource`本质上是调用`PoolingDataSource`的方法来获取连接,所以这里从`PoolingDataSource.getConnection()`开始研究。
@@ -603,7 +553,7 @@ accessToUnderlyingConnectionAllowed=false
} }
``` ```
### `GenericObjectPool.borrowObject()`
### GenericObjectPool.borrowObject()
`GenericObjectPool`是一个很简练的类,里面涉及到的属性设置和锁机制都涉及得非常巧妙。 `GenericObjectPool`是一个很简练的类,里面涉及到的属性设置和锁机制都涉及得非常巧妙。
@@ -738,7 +688,7 @@ accessToUnderlyingConnectionAllowed=false
return p.getObject(); return p.getObject();
} }
``` ```
### `GenericObjectPool.create()`
### GenericObjectPool.create()
这里在创建连接对象时采用的锁机制非常值得学习,简练且高效。 这里在创建连接对象时采用的锁机制非常值得学习,简练且高效。
@@ -822,7 +772,7 @@ accessToUnderlyingConnectionAllowed=false
``` ```
### `PoolableConnectionFactory.makeObject()`
### PoolableConnectionFactory.makeObject()
```java ```java
public PooledObject<PoolableConnection> makeObject() throws Exception { public PooledObject<PoolableConnection> makeObject() throws Exception {
// 创建原生的Connection对象 // 创建原生的Connection对象
@@ -890,10 +840,11 @@ accessToUnderlyingConnectionAllowed=false
} }
``` ```
## 空闲对象回收器`Evictor`
以上基本已分析完连接对象的获取过程,下面再研究下空闲对象回收器。前面已经讲到当创建完数据源对象时会开启连接池的`evictor`线程,所以我们从`BasicDataSource.startPoolMaintenance()`开始分析。
## 空闲对象回收器Evictor
以上基本已分析完连接对象的获取过程,下面再研究下空闲对象回收器。前面已经讲到当创建完数据源对象时会开启连接池的`evictor`线程,所以我们从`BasicDataSource.startPoolMaintenance()`开始分析。
### `BasicDataSource.startPoolMaintenance()`
### BasicDataSource.startPoolMaintenance()
前面说过`timeBetweenEvictionRunsMillis`为非正数时不会开启开启空闲对象回收器,从以下代码可以理解具体逻辑。 前面说过`timeBetweenEvictionRunsMillis`为非正数时不会开启开启空闲对象回收器,从以下代码可以理解具体逻辑。
@@ -905,8 +856,9 @@ accessToUnderlyingConnectionAllowed=false
} }
} }
``` ```
### `BaseGenericObjectPool.setTimeBetweenEvictionRunsMillis(long)`
这个`BaseGenericObjectPool`是上面说到的`GenericObjectPool`的父类。
### BaseGenericObjectPool.setTimeBetweenEvictionRunsMillis(long)
这个`BaseGenericObjectPool`是上面说到的`GenericObjectPool`的父类。
```java ```java
public final void setTimeBetweenEvictionRunsMillis( public final void setTimeBetweenEvictionRunsMillis(
@@ -918,9 +870,9 @@ accessToUnderlyingConnectionAllowed=false
} }
``` ```
### `BaseGenericObjectPool.startEvictor(long)`
### BaseGenericObjectPool.startEvictor(long)
这里会去定义一个`Evictor`对象,这个其实是一个Runnable对象,后面会讲到。
这里会去定义一个`Evictor`对象,这个其实是一个`Runnable`对象,后面会讲到。
```java ```java
final void startEvictor(final long delay) { final void startEvictor(final long delay) {
@@ -939,7 +891,8 @@ accessToUnderlyingConnectionAllowed=false
} }
``` ```
### `EvictionTimer.schedule(Evictor, long, long)`
### EvictionTimer.schedule(Evictor, long, long)
`DBCP`是使用`ScheduledThreadPoolExecutor`来实现回收器的定时检测。 涉及到`ThreadPoolExecutor`为`JDK`自带的`api`,这里不再深入分析线程池如何实现定时调度。感兴趣的朋友可以复习下常用的几款线程池。 `DBCP`是使用`ScheduledThreadPoolExecutor`来实现回收器的定时检测。 涉及到`ThreadPoolExecutor`为`JDK`自带的`api`,这里不再深入分析线程池如何实现定时调度。感兴趣的朋友可以复习下常用的几款线程池。
```java ```java
@@ -957,8 +910,9 @@ accessToUnderlyingConnectionAllowed=false
task.setScheduledFuture(scheduledFuture); task.setScheduledFuture(scheduledFuture);
} }
``` ```
### `BaseGenericObjectPool.Evictor`
`Evictor`是`BaseGenericObjectPool`的内部类,实现了`Runnable`接口,这里看下它的run方法。
### BaseGenericObjectPool.Evictor
`Evictor`是`BaseGenericObjectPool`的内部类,实现了`Runnable`接口,这里看下它的run方法。
```java ```java
class Evictor implements Runnable { class Evictor implements Runnable {
@@ -1014,8 +968,9 @@ accessToUnderlyingConnectionAllowed=false
} }
``` ```
### `GenericObjectPool.evict()`
这里的回收过程包括以下四道校验:
### GenericObjectPool.evict()
这里的回收过程包括以下四道校验:
1. 按照`evictionPolicy`校验`idleSoftEvictTime`、`idleEvictTime`; 1. 按照`evictionPolicy`校验`idleSoftEvictTime`、`idleEvictTime`;
@@ -1132,7 +1087,7 @@ accessToUnderlyingConnectionAllowed=false
# 通过`JNDI`获取数据源对象
# 通过JNDI获取数据源对象
## 需求 ## 需求
@@ -1149,7 +1104,9 @@ accessToUnderlyingConnectionAllowed=false
| `SharedPoolDataSource` | `InstanceKeyDataSource`的子类,不同用户共享一个连接池 | | `SharedPoolDataSource` | `InstanceKeyDataSource`的子类,不同用户共享一个连接池 |
## 引入依赖 ## 引入依赖
本文在前面例子的基础上增加以下依赖,因为是web项目,所以打包方式为`war`:
本文在前面例子的基础上增加以下依赖,因为是web项目,所以打包方式为`war`:
```xml ```xml
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>
@@ -1171,7 +1128,7 @@ accessToUnderlyingConnectionAllowed=false
</dependency> </dependency>
``` ```
## 编写`context.xml`
## 编写context.xml
在`webapp`文件下创建目录`META-INF`,并创建`context.xml`文件。这里面的每个`resource`节点都是我们配置的对象,类似于`spring`的`bean`节点。其中`bean/DriverAdapterCPDS`这个对象需要被另外两个使用到。 在`webapp`文件下创建目录`META-INF`,并创建`context.xml`文件。这里面的每个`resource`节点都是我们配置的对象,类似于`spring`的`bean`节点。其中`bean/DriverAdapterCPDS`这个对象需要被另外两个使用到。
@@ -1221,7 +1178,7 @@ accessToUnderlyingConnectionAllowed=false
</Context> </Context>
``` ```
## 编写`web.xml`
## 编写web.xml
在`web-app`节点下配置资源引用,每个`resource-env-ref`指向了我们配置好的对象。 在`web-app`节点下配置资源引用,每个`resource-env-ref`指向了我们配置好的对象。
@@ -1243,7 +1200,7 @@ accessToUnderlyingConnectionAllowed=false
</resource-env-ref> </resource-env-ref>
``` ```
## 编写`jsp`
## 编写jsp
因为需要在`web`环境中使用,如果直接建类写个`main`方法测试,会一直报错的,目前没找到好的办法。这里就简单地使用`jsp`来测试吧(这是从tomcat官网参照的例子)。 因为需要在`web`环境中使用,如果直接建类写个`main`方法测试,会一直报错的,目前没找到好的办法。这里就简单地使用`jsp`来测试吧(这是从tomcat官网参照的例子)。
@@ -1282,7 +1239,7 @@ conn2=128868782, URL=jdbc:mysql://localhost:3306/github_demo?useUnicode=true&cha
# 使用`DBCP`测试两阶段提交
# 使用DBCP测试两阶段提交
前面源码分析已经讲到,以下类用于支持`JTA`事务。本文将介绍如何使用`DBCP`来实现`JTA`事务两阶段提交(当然,实际项目并不支持使用`2PC`,因为性能开销太大)。 前面源码分析已经讲到,以下类用于支持`JTA`事务。本文将介绍如何使用`DBCP`来实现`JTA`事务两阶段提交(当然,实际项目并不支持使用`2PC`,因为性能开销太大)。
@@ -1302,7 +1259,7 @@ SET innodb_support_xa = ON -- 开启XA事务
除了原来的`github_demo`数据库,我另外建了一个`test`数据库,简单地模拟两个数据库。 除了原来的`github_demo`数据库,我另外建了一个`test`数据库,简单地模拟两个数据库。
## `mysql``XA`事务使用
## mysql的XA事务使用
测试之前,这里简单回顾下直接使用`sql`操作`XA`事务的过程,将有助于对以下内容的理解: 测试之前,这里简单回顾下直接使用`sql`操作`XA`事务的过程,将有助于对以下内容的理解:
@@ -1334,7 +1291,7 @@ XA RECOVER; -- 查看处于prepare状态的事务列表
</dependency> </dependency>
``` ```
## 获取`BasicManagedDataSource`
## 获取BasicManagedDataSource
这里千万记得要设置`DefaultCatalog`,否则当前事务中注册不同资源管理器时,可能都会被当成同一个资源管理器而拒绝注册并报错,因为这个问题,花了我好长时间才解决。 这里千万记得要设置`DefaultCatalog`,否则当前事务中注册不同资源管理器时,可能都会被当成同一个资源管理器而拒绝注册并报错,因为这个问题,花了我好长时间才解决。


src/main/java/cn/zzs/dbcp/BasicDataSourceUtil.java → src/main/java/cn/zzs/dbcp/JDBCUtils.java View File

@@ -19,7 +19,7 @@ import org.apache.commons.logging.LogFactory;
* @author: zzs * @author: zzs
* @date: 2019年8月31日 下午9:05:08 * @date: 2019年8月31日 下午9:05:08
*/ */
public class BasicDataSourceUtil {
public class JDBCUtils {


private static DataSource dataSource; private static DataSource dataSource;


@@ -27,7 +27,7 @@ public class BasicDataSourceUtil {


private static final Object obj = new Object(); private static final Object obj = new Object();


private static final Log log = LogFactory.getLog(BasicDataSourceUtil.class);
private static final Log log = LogFactory.getLog(JDBCUtils.class);


static { static {
init(); init();
@@ -172,7 +172,7 @@ public class BasicDataSourceUtil {
private static void init() { private static void init() {
// 导入配置文件 // 导入配置文件
Properties properties = new Properties(); Properties properties = new Properties();
InputStream in = BasicDataSourceUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("dbcp.properties");
try { try {
properties.load(in); properties.load(in);
// 根据配置文件内容获得数据源对象 // 根据配置文件内容获得数据源对象

src/main/resources/jdbc.properties → src/main/resources/dbcp.properties View File

@@ -1,10 +1,10 @@
#\u6570\u636e\u5e93\u57fa\u672c\u914d\u7f6e
#\u57fa\u672c\u8fde\u63a5\u5c5e\u6027
driverClassName=com.mysql.cj.jdbc.Driver driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/github_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true url=jdbc:mysql://localhost:3306/github_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true
username=root username=root
password=root password=root
#-------------\u8fde\u63a5\u6c60\u6570\u636e\u76f8\u5173\u53c2\u6570--------------------------------
#-------------\u8fde\u63a5\u6c60\u5927\u5c0f\u548c\u8fde\u63a5\u8d85\u65f6\u53c2\u6570--------------------------------
#\u521d\u59cb\u5316\u8fde\u63a5\u6570\u91cf:\u8fde\u63a5\u6c60\u542f\u52a8\u65f6\u521b\u5efa\u7684\u521d\u59cb\u5316\u8fde\u63a5\u6570\u91cf #\u521d\u59cb\u5316\u8fde\u63a5\u6570\u91cf:\u8fde\u63a5\u6c60\u542f\u52a8\u65f6\u521b\u5efa\u7684\u521d\u59cb\u5316\u8fde\u63a5\u6570\u91cf
#\u9ed8\u8ba4\u4e3a0 #\u9ed8\u8ba4\u4e3a0
initialSize=0 initialSize=0
@@ -27,6 +27,11 @@ minIdle=0
#\u9ed8\u8ba4-1 #\u9ed8\u8ba4-1
maxWaitMillis=-1 maxWaitMillis=-1
#\u8fde\u63a5\u6c60\u521b\u5efa\u7684\u8fde\u63a5\u7684\u9ed8\u8ba4\u7684\u6570\u636e\u5e93\u540d\uff0c\u5982\u679c\u662f\u4f7f\u7528DBCP\u7684XA\u8fde\u63a5\u5fc5\u987b\u8bbe\u7f6e\uff0c\u4e0d\u7136\u6ce8\u518c\u4e0d\u4e86\u591a\u4e2a\u8d44\u6e90\u7ba1\u7406\u5668
#defaultCatalog=github_demo
#\u8fde\u63a5\u6c60\u521b\u5efa\u7684\u8fde\u63a5\u7684\u9ed8\u8ba4\u7684schema\u3002\u5982\u679c\u662fmysql\uff0c\u8fd9\u4e2a\u8bbe\u7f6e\u6ca1\u4ec0\u4e48\u7528\u3002
#defaultSchema=github_demo
#-------------\u7f13\u5b58\u8bed\u53e5-------------------------------- #-------------\u7f13\u5b58\u8bed\u53e5--------------------------------
#\u662f\u5426\u7f13\u5b58preparedStatement\uff0c\u4e5f\u5c31\u662fPSCache\u3002 #\u662f\u5426\u7f13\u5b58preparedStatement\uff0c\u4e5f\u5c31\u662fPSCache\u3002
@@ -118,11 +123,6 @@ autoCommitOnReturn=true
#\u9ed8\u8ba4true #\u9ed8\u8ba4true
rollbackOnReturn=true rollbackOnReturn=true
#\u8fde\u63a5\u6c60\u521b\u5efa\u7684\u8fde\u63a5\u7684\u9ed8\u8ba4\u7684\u6570\u636e\u5e93\u540d\uff0c\u5982\u679c\u662f\u4f7f\u7528DBCP\u7684XA\u8fde\u63a5\u5fc5\u987b\u8bbe\u7f6e\uff0c\u4e0d\u7136\u6ce8\u518c\u4e0d\u4e86\u591a\u4e2a\u8d44\u6e90\u7ba1\u7406\u5668
#defaultCatalog=github_demo
#\u8fde\u63a5\u6c60\u521b\u5efa\u7684\u8fde\u63a5\u7684\u9ed8\u8ba4\u7684schema\u3002\u5982\u679c\u662fmysql\uff0c\u8fd9\u4e2a\u8bbe\u7f6e\u6ca1\u4ec0\u4e48\u7528\u3002
#defaultSchema=github_demo
#-------------\u8fde\u63a5\u6cc4\u6f0f\u56de\u6536\u53c2\u6570-------------------------------- #-------------\u8fde\u63a5\u6cc4\u6f0f\u56de\u6536\u53c2\u6570--------------------------------
#\u5f53\u672a\u4f7f\u7528\u7684\u65f6\u95f4\u8d85\u8fc7removeAbandonedTimeout\u65f6\uff0c\u662f\u5426\u89c6\u8be5\u8fde\u63a5\u4e3a\u6cc4\u9732\u8fde\u63a5\u5e76\u5220\u9664\uff08\u5f53getConnection()\u88ab\u8c03\u7528\u65f6\u68c0\u6d4b\uff09 #\u5f53\u672a\u4f7f\u7528\u7684\u65f6\u95f4\u8d85\u8fc7removeAbandonedTimeout\u65f6\uff0c\u662f\u5426\u89c6\u8be5\u8fde\u63a5\u4e3a\u6cc4\u9732\u8fde\u63a5\u5e76\u5220\u9664\uff08\u5f53getConnection()\u88ab\u8c03\u7528\u65f6\u68c0\u6d4b\uff09

+ 3
- 3
src/main/resources/log4j.properties View File

@@ -1,8 +1,8 @@
#OFF,systemOut,logFile,logDailyFile,logRollingFile,logMail,logDB,ALL #OFF,systemOut,logFile,logDailyFile,logRollingFile,logMail,logDB,ALL
#\u2460\u914d\u7f6e\u6839Logger #\u2460\u914d\u7f6e\u6839Logger
log4j.rootLogger=info,systemOut
log4j.logger.cn.zzs.dbcp=debug,systemOut
log4j.additivity.cn.zzs.dbcp=false
log4j.rootLogger=debug,systemOut
#log4j.logger.cn.zzs.dbcp=debug,systemOut
#log4j.additivity.cn.zzs.dbcp=false
#\u8f93\u51fa\u5230\u63a7\u5236\u53f0 #\u8f93\u51fa\u5230\u63a7\u5236\u53f0
log4j.appender.systemOut= org.apache.log4j.ConsoleAppender log4j.appender.systemOut= org.apache.log4j.ConsoleAppender


+ 36
- 0
src/main/resources/uml/BasicDataSourceUML View File

@@ -0,0 +1,36 @@
@startuml
class BasicDataSource{
- volatile GenericObjectPool<PoolableConnection> connectionPool
- volatile DataSource dataSource
}
interface DataSource
interface BasicDataSourceMXBean
interface MBeanRegistration

class PoolingDataSource<C extends Connection>{
- final ObjectPool<C> pool
}

class ManagedDataSource<C extends Connection>{
- TransactionRegistry transactionRegistry
}

class BasicManagedDataSource{
- TransactionRegistry transactionRegistry
- TransactionManager transactionManager
- XADataSource xaDataSourceInstance
}

BasicDataSource .up.|> DataSource
BasicDataSource .up.|> BasicDataSourceMXBean
BasicDataSource .up.|> MBeanRegistration

BasicManagedDataSource -up-|> BasicDataSource

PoolingDataSource .up.|> DataSource
PoolingDataSource -left-* BasicDataSource

ManagedDataSource -up-|> PoolingDataSource
ManagedDataSource -left-* BasicManagedDataSource

@enduml

+ 35
- 0
src/main/resources/uml/DelegatingConnectionUML View File

@@ -0,0 +1,35 @@
@startuml
interface Connection
class DelegatingConnection<C extends Connection>{
- volatile C connection
}

class PoolingConnection

class PoolableConnection

class ManagedConnection<C extends Connection>

class PoolableManagedConnection

class PoolGuardConnectionWrapper<D extends Connection>

DelegatingConnection .up.|> Connection

PoolingConnection -up-|> DelegatingConnection

PoolingConnection .left.* PoolableConnection

PoolableConnection -up-|> DelegatingConnection

ManagedConnection -up-|> DelegatingConnection

PoolableManagedConnection -up-|> PoolableConnection

ManagedConnection .left.* PoolableManagedConnection

PoolGuardConnectionWrapper -up-|> DelegatingConnection

PoolableConnection .left.* PoolGuardConnectionWrapper

@enduml

+ 40
- 0
src/main/resources/uml/GenericObjectPoolUML View File

@@ -0,0 +1,40 @@
@startuml
class PoolingDataSource<C extends Connection>{
- final ObjectPool<C> pool
}

class GenericObjectPool<T>{
- final PooledObjectFactory<T> factory
- final Map<IdentityWrapper<T>, PooledObject<T>> allObjects
- final LinkedBlockingDeque<PooledObject<T>> idleObjects
- Evictor evictor
+ T borrowObject()
+ void returnObject(final T obj)
}

interface ConnectionFactory
class DriverConnectionFactory{
+ Connection createConnection()
}

interface PooledObjectFactory<T>
class PoolableConnectionFactory{
- final ConnectionFactory connectionFactory
- volatile ObjectPool<PoolableConnection> pool
+ PooledObject<T> makeObject()
+ void destroyObject(PooledObject<T> p)
+ boolean validateObject(PooledObject<T> p)
+ void activateObject(PooledObject<T> p)
}

GenericObjectPool -down-* PoolingDataSource

DriverConnectionFactory .up.|> ConnectionFactory

PoolableConnectionFactory .up.|> PooledObjectFactory

DriverConnectionFactory -down-* PoolableConnectionFactory

PoolableConnectionFactory -left-* GenericObjectPool
PoolableConnectionFactory *-right- GenericObjectPool
@enduml

+ 18
- 17
src/test/java/cn/zzs/dbcp/BasicDataSourceTest.java View File

@@ -30,9 +30,9 @@ public class BasicDataSourceTest {
PreparedStatement statement = null; PreparedStatement statement = null;
try { try {
// 获得连接 // 获得连接
connection = BasicDataSourceUtil.getConnection();
connection = JDBCUtils.getConnection();
// 开启事务设置非自动提交 // 开启事务设置非自动提交
BasicDataSourceUtil.startTrasaction();
JDBCUtils.startTrasaction();
// 获得Statement对象 // 获得Statement对象
statement = connection.prepareStatement(sql); statement = connection.prepareStatement(sql);
// 设置参数 // 设置参数
@@ -44,13 +44,13 @@ public class BasicDataSourceTest {
// 执行 // 执行
statement.executeUpdate(); statement.executeUpdate();
// 提交事务 // 提交事务
BasicDataSourceUtil.commit();
JDBCUtils.commit();
} catch(Exception e) { } catch(Exception e) {
BasicDataSourceUtil.rollback();
JDBCUtils.rollback();
log.error("保存用户失败", e); log.error("保存用户失败", e);
} finally { } finally {
// 释放资源 // 释放资源
BasicDataSourceUtil.release(connection, statement, null);
JDBCUtils.release(connection, statement, null);
} }
} }


@@ -65,9 +65,9 @@ public class BasicDataSourceTest {
PreparedStatement statement = null; PreparedStatement statement = null;
try { try {
// 获得连接 // 获得连接
connection = BasicDataSourceUtil.getConnection();
connection = JDBCUtils.getConnection();
// 开启事务 // 开启事务
BasicDataSourceUtil.startTrasaction();
JDBCUtils.startTrasaction();
// 获得Statement对象 // 获得Statement对象
statement = connection.prepareStatement(sql); statement = connection.prepareStatement(sql);
// 设置参数 // 设置参数
@@ -77,13 +77,13 @@ public class BasicDataSourceTest {
// 执行 // 执行
statement.executeUpdate(); statement.executeUpdate();
// 提交事务 // 提交事务
BasicDataSourceUtil.commit();
JDBCUtils.commit();
} catch(Exception e) { } catch(Exception e) {
log.error("异常导致操作回滚", e); log.error("异常导致操作回滚", e);
BasicDataSourceUtil.rollback();
JDBCUtils.rollback();
} finally { } finally {
// 释放资源 // 释放资源
BasicDataSourceUtil.release(connection, statement, null);
JDBCUtils.release(connection, statement, null);
} }
} }


@@ -99,7 +99,7 @@ public class BasicDataSourceTest {
ResultSet resultSet = null; ResultSet resultSet = null;
try { try {
// 获得连接 // 获得连接
connection = BasicDataSourceUtil.getConnection();
connection = JDBCUtils.getConnection();
// 获得Statement对象 // 获得Statement对象
statement = connection.prepareStatement(sql); statement = connection.prepareStatement(sql);
// 执行 // 执行
@@ -114,7 +114,7 @@ public class BasicDataSourceTest {
log.error("查询用户异常", e); log.error("查询用户异常", e);
} finally { } finally {
// 释放资源 // 释放资源
BasicDataSourceUtil.release(connection, statement, resultSet);
JDBCUtils.release(connection, statement, resultSet);
} }
} }


@@ -129,9 +129,9 @@ public class BasicDataSourceTest {
PreparedStatement statement = null; PreparedStatement statement = null;
try { try {
// 获得连接 // 获得连接
connection = BasicDataSourceUtil.getConnection();
connection = JDBCUtils.getConnection();
// 设置非自动提交 // 设置非自动提交
BasicDataSourceUtil.startTrasaction();
JDBCUtils.startTrasaction();
// 获得Statement对象 // 获得Statement对象
statement = connection.prepareStatement(sql); statement = connection.prepareStatement(sql);
// 设置参数 // 设置参数
@@ -139,13 +139,14 @@ public class BasicDataSourceTest {
// 执行 // 执行
statement.executeUpdate(); statement.executeUpdate();
// 提交事务 // 提交事务
BasicDataSourceUtil.commit();
JDBCUtils.commit();
} catch(Exception e) { } catch(Exception e) {
log.error("异常导致操作回滚", e); log.error("异常导致操作回滚", e);
BasicDataSourceUtil.rollback();
JDBCUtils.rollback();
} finally { } finally {
// 释放资源 // 释放资源
BasicDataSourceUtil.release(connection, statement, null);
JDBCUtils.release(connection, statement, null);
} }
} }
} }

Loading…
Cancel
Save