介绍
JDBC 连接池 org.apache.tomcat.jdbc.pool
是 Apache Commons DBCP 连接池的替代或备选。
那么为什么需要一个新的连接池呢?
以下是一些原因:
-
Commons DBCP 1.x 是单线程的。为了实现线程安全,Commons 在对象分配和对象返回期间都会在短时间内锁定整个池。请注意,这不适用于 Commons DBCP 2.x。
-
Commons DBCP 1.x 可能会很慢。随着逻辑 CPU 数量的增加以及尝试借用或返回对象的并发线程数量的增加,性能会受到影响。对于高度并发的系统,影响可能很大。请注意,这不适用于 Commons DBCP 2.x。
-
Commons DBCP 超过 60 个类。tomcat-jdbc-pool 核心是 8 个类,因此根据未来需求进行修改所需的更改要少得多。这就是运行连接池本身所需的全部内容,其余的就是附加功能。
-
Commons DBCP 使用静态接口。这意味着必须为给定的 JRE 版本使用正确的版本,否则可能会看到 NoSuchMethodException 异常。
-
不值得重写超过 60 个类,此时连接池可以通过更简单的实现来完成。
-
Tomcat jdbc 池实现了异步检索连接的能力,而无需向库本身添加额外的线程。
-
Tomcat jdbc pool 是一个 Tomcat 模块,依赖于 Tomcat 中使用的简化日志框架 Tomcat JULI。
-
使用 javax.sql.PooledConnection 接口检索底层连接。
-
防饥饿。如果池为空,并且线程正在等待连接,则当返回连接时,池将唤醒正在等待的正确线程。其他大多数连接池处于饥饿状态。
与其他连接池实现相比添加的功能。
-
支持高度并发环境和多核/CPU 系统。
-
接口的动态实现将支持运行时环境的 java.sql 和 javax.sql 接口(只要 JDBC 驱动程序执行相同的操作),即使使用较低版本的 JDK 进行编译也是如此。
-
验证间隔 - 不必在每次使用连接时都进行验证,可以在借用或归还连接时进行验证,只是频率不超过可以配置的间隔。
-
Run-Once query,一种可配置的查询,在建立与数据库的连接时,该查询将仅运行一次。对于设置会话设置非常有用,希望在建立连接的整个过程中都存在这些设置。
-
能够配置自定义侦听器。这允许编写自定义侦听器来增强功能。可以使用侦听器来收集查询统计信息、缓存会话状态、失败时重新连接连接、重试查询、缓存查询结果等。选择是无穷无尽的,并且拦截器是动态的,不依赖于 java.sql/javax.sql 接口的 JDK 版本。
-
高性能 - 稍后将展示一些性能差异
-
极其简单,由于实现非常简化,行数和源文件数都非常低,相比于有 200 多个源文件的 c3p0(上次查了一下),Tomcat jdbc 的核心有 8 个文件,连接池本身大约是它的一半。由于可能会发生错误,因此可以更快地追踪它们,并且更容易修复。从一开始,降低复杂性就一直是重点。
-
异步连接检索 - 可以将连接请求排队并接收<Connection> Future。
-
更好的空闲连接处理。它不仅可以直接关闭连接,还可以池连接并使用更智能的算法调整空闲池的大小。
-
可以通过指定池使用阈值来决定在什么时刻将连接视为已放弃,是当池已满时,还是直接在超时时。
-
放弃连接计时器将在 statement/query 活动时重置。允许长时间使用的连接不超时。这是使用 ResetAbandonedTimer 实现的
-
在连接一段时间后关闭连接。年龄基于返回泳池时的关闭时间。
-
当怀疑连接被放弃时,获取 JMX 通知和日志条目。这类似于 removeAbandonedTimeout,但它不执行任何操作,只报告信息。这是使用 suspectTimeout 属性实现的。
-
可以从
java.sql.Driver、javax.sql.DataSource
或javax.sql.XADataSource
检索连接这是使用 dataSource 和 dataSourceJNDI 属性实现的。 -
XA 连接支持
如何使用
Tomcat 连接池的使用已尽可能简单,对于熟悉 commons-dbcp 的人来说, 转换将非常简单。从其他连接池迁移也相当简单。
附加功能
Tomcat 连接池提供了一些其他功能,而大多数其他池允许执行的功能:
-
initSQL - 在创建连接时只运行 SQL 语句一次的能力
-
validationInterval - 除了在连接上运行验证外,还应避免过于频繁地运行验证。
-
jdbcInterceptors - 灵活且可插拔的拦截器,用于围绕池、查询执行和结果集处理创建任何自定义。有关此内容的更多信息,请参阅高级部分。
-
fairQueue - 将 fair 标志设置为 true 以实现线程公平性或使用异步连接检索
Apache Tomcat 容器内部
Tomcat 连接池配置为 Tomcat JDBC 文档中描述的资源,
唯一的区别是必须指定 factory 属性并将值设置为 org.apache.tomcat.jdbc.pool.DataSourceFactory
Standalone
连接池只有另一个依赖项,即 tomcat-juli.jar。要使用 Bean 实例化在独立项目中配置池, 要实例化的 Bean 是 org.apache.tomcat.jdbc.pool.DataSource。 用于将连接池配置为 JNDI 资源的相同属性(如下所述)用于将数据源配置为 Bean。
JMX
连接池对象公开一个可以注册的 MBean。为了使连接池对象创建 MBean,必须将标志 jmxEnabled 设置为 true。
这并不意味着池将注册到 MBean 服务器,只是说 MBean 已创建。
在像 Tomcat 这样的容器中,Tomcat 本身向 MBean 服务器注册 DataSource,
然后 org.apache.tomcat.jdbc.pool.DataSource
对象将注册实际的连接池 MBean。
如果在容器外部运行,则可以在指定的任何对象名称下自行注册 DataSource,它会将注册传播到底层池。
要做到这一点,需要调用 mBeanServer.registerMBean(dataSource.getPool().getJmxPool(),objectname)
。
在此调用之前,请确保已通过调用 dataSource.createPool() 创建池。
属性 Attributes
为了提供与 commons-dbcp 和 tomcat-jdbc-pool 之间的非常简单的切换, 大多数属性都是相同的,并且具有相同的含义。
JNDI 工厂和类型
属性Attribute | 说明Description |
---|---|
factory |
factory是必需的,该值应为 |
type |
类型type应始终为 |
系统属性
系统属性是 JVM 范围的,会影响在 JVM 中创建的所有池
属性Attribute | 说明Description |
---|---|
org.apache.tomcat.jdbc.pool.onlyAttemptCurrentClassLoader |
通用属性
这些属性在 commons-dbcp 和 tomcat-jdbc-pool 之间共享,在某些情况下,默认值不同。
属性Attribute | 说明Description |
---|---|
defaultAutoCommit |
(布尔值)此池创建的连接的默认自动提交状态。如果未设置,则默认为 JDBC 驱动程序默认值(如果未设置,则不会调用 setAutoCommit 方法。 |
defaultReadOnly |
(布尔值)此池创建的连接的默认只读状态。如果未设置,则不会调用 setReadOnly 方法。(某些驱动程序不支持只读模式,例如:Informix) |
defaultTransactionIsolation |
(字符串)此池创建的连接的默认 TransactionIsolation 状态。以下之一:(请参阅 javadoc ) * NONE * READ_COMMITTED * READ_UNCOMMITTED * REPEATABLE_READ * SERIALIZABLE 如果未设置,则不会调用该方法,并且默认为 JDBC 驱动程序。 |
defaultCatalog |
(字符串)此池创建的连接的默认目录。 |
driverClassName |
(字符串)要使用的 JDBC 驱动程序的完全限定 Java 类名。驱动程序必须可以从与 tomcat-jdbc.jar 相同的类加载器访问 |
username |
(字符串)要传递给 JDBC 驱动程序以建立连接的连接用户名。请注意,默认情况下,方法 |
password |
(字符串)要传递给 JDBC 驱动程序以建立连接的连接密码。请注意,默认情况下,方法 |
maxActive |
(整数)可以同时从此池中分配的活动连接的最大数量。默认值为 100 |
maxIdle |
(整数)应始终保留在池中的最大连接数。默认值为 maxActive:100 定期检查空闲连接(如果启用),空闲时间超过 minEvictableIdleTimeMillis 的连接将被释放。(另请参阅 testWhileIdle) |
minIdle (最小空闲) |
(整数)应始终保留在池中的已建立连接的最小数量。如果验证查询失败,连接池可能会缩小到此数字以下。默认值派生自 initialSize:10 (另请参阅 testWhileIdle) |
initialSize |
(整数)启动池时创建的初始连接数。默认值为 10 |
maxWait |
(整数)在引发异常之前,池将等待 (没有可用连接时) 返回连接的最大毫秒数。默认值为 30000(30 秒) |
testOnBorrow |
(布尔值)指示对象在从池中借用之前是否要验证。如果对象验证失败,将从池中删除,将尝试借用另一个对象。为了进行更高效的验证,请参阅 validationInterval。默认值为 false |
testOnConnect |
(布尔值)指示在首次创建连接时是否验证对象。如果对象验证失败,则会引发 SQLException。默认值为 false |
testOnReturn |
(布尔值)指示对象在返回到池之前是否要进行验证。默认值为 false。 |
testWhileIdle |
(布尔值)指示对象是否将由空闲对象 evictor(如果有进行验证。如果对象验证失败,则会将其从池中删除。默认值为 false,必须设置此属性才能运行池清理器/测试线程(另请参阅 timeBetweenEvictionRunsMillis) |
validationQuery |
(字符串)在将连接返回给调用方之前,将用于验证来自此池的连接的 SQL 查询。如果指定,则此查询不必返回任何数据,它只是不能抛出 SQLException。默认值为 null。如果未指定,则 isValid() 方法将验证连接。示例值包括 SELECT 1 (mysql)、from dual (oracle) SELECT 1 、 SELECT 1 (MS Sql Server) |
validationQueryTimeout |
(整数)连接验证查询失败前的超时时间(以秒为单位)。这是通过在执行 validationQuery 的语句上调用 |
validatorClassName |
(字符串)实现 |
timeBetweenEvictionRunsMillis |
(整数)空闲连接验证/清理线程运行之间休眠的毫秒数。此值不应设置在 1 秒以下。它决定了检查空闲、已放弃连接的频率,以及验证空闲连接的频率。如果 maxAge 为非零且更低,则此值将被 maxAge 覆盖。默认值为 5000 (5 秒)。 |
numTestsPerEvictionRun |
(整数)在 tomcat-jdbc-pool 中未使用的属性。 |
minEvictableIdleTimeMillis |
(整数)对象在符合逐出条件之前可以在池中闲置的最短时间。默认值为 60000(60 秒)。 |
accessToUnderlyingConnectionAllowed |
(布尔值)未使用的属性。可以通过在池连接上调用 unwrap 来实现访问。参见 |
removeAbandoned |
(布尔值)标记以在放弃的连接超过 removeAbandonedTimeout 时删除这些连接。如果设置为 true,则如果连接的使用时间超过 removeAbandonedTimeout,则将其视为已放弃并有资格删除。将此设置为 true 可以从无法关闭连接的应用程序中恢复数据库连接。参见 logAbandoned 默认值为 false。 |
removeAbandonedTimeout |
(整数)在可以删除已放弃(正在使用)的连接之前超时(以秒为单位)。默认值为 60 (60 秒)。该值应设置为您的应用程序可能具有的运行时间最长的查询。 |
logAbandoned |
(布尔值)用于记录放弃 Connection 的应用程序代码的堆栈跟踪的标志。记录已放弃的 Connections 会增加每个 Connection 借用的开销,因为必须生成堆栈跟踪。默认值为 false。 |
connectionProperties |
(字符串)建立新连接时将发送到 JDBC 驱动程序的连接属性。字符串的格式必须为 [propertyName=property;]* 注意 - “user” 和 “password” 属性将显式传递,因此它们不需要在此处包含。默认值为 null。 |
poolPreparedStatements |
(布尔值)未使用的属性。 |
maxOpenPreparedStatements |
(整数)未使用的属性。 |
Tomcat JDBC 增强属性
属性Attribute | 说明Description |
---|---|
initSQL |
(字符串)首次创建连接时要运行的自定义查询。默认值为 null |
jdbcInterceptors |
(字符串)扩展 这些拦截器将作为拦截器插入到 预定义的拦截器: 1. 2. ConnectionState - 跟踪自动提交、只读、目录和事务隔离级别 3. 4. StatementFinalizer - 跟踪打开的语句,并在连接返回到池时关闭它们 JDBC 拦截器部分详细介绍了更多预定义的拦截器。 |
validationInterval 验证间隔 |
(long) 避免过多的验证,最多仅以此频率 - 时间(以毫秒为单位)运行验证。如果连接应进行验证,但之前已在此间隔内进行验证,则不会再次验证该连接。默认值为 3000 (3 秒)。 |
jmxEnabled |
(布尔值)是否向 JMX 注册池。默认值为 true。 |
fairQueue 公平队列 |
(布尔值)如果希望以真正的 FIFO 方式公平对待对 getConnection 的调用,请设置为 true。这将 |
abandonWhenPercentageFull |
(整数)已放弃(超时)的连接不会关闭和报告,除非正在使用的连接数高于 abandonWhenPercentageFull 定义的百分比。该值应介于 0-100 之间。默认值为 0,这意味着一旦达到 removeAbandonedTimeout,连接就有资格关闭。 |
maxAge最大年龄 |
(长)在重新创建连接之前保持连接的时间(以毫秒为单位)。
从池中借用连接时,池将检查是否已达到 |
useEquals |
(布尔值)如果希望 ProxyConnection 类使用 String.equals,则设置为 true, 如果希望在比较方法名称时使用 ==,则设置为 false。 此属性不适用于添加的侦听器,因为这些侦听器是单独配置的。默认值为 true。 |
suspectTimeout 超时 |
(整数)超时值(以秒为单位)。默认值为 0。 类似于 removeAbandonedTimeout 值,但如果 logAbandoned 设置为 true, 则它只会记录警告,而不是将连接视为已放弃并可能关闭连接。 如果此值等于或小于 0,则不会执行可疑检查。仅当超时值大于 0 且连接未被放弃或禁用放弃检查时,才会进行可疑检查。 如果连接可疑,则会记录 WARN 消息并发送一次 JMX 通知。 |
rollbackOnReturn 回滚返回 |
(布尔值)如果 autoCommit==false,则池可以通过在连接上调用 rollback 来终止事务,因为它返回到池默认值为 false。 |
commitOnReturn |
(布尔值)如果 autoCommit==false 则池可以通过在连接上调用 commit 来完成事务,因为它已返回到池中。 如果 rollbackOnReturn==true 则忽略此属性。默认值为 false。 |
alternateUsernameAllowed |
(布尔值)默认情况下,出于性能原因,jdbc-pool 将忽略 DataSource.getConnection(username,password)调用, 并仅在全局配置的属性 username 和 password 下返回先前的池连接。 但是,可以将池配置为允许在每次请求连接时使用不同的凭据。 要启用 DataSource.getConnection(username,password)调用中描述的功能,只需将属性 alternateUsernameAllowed 设置为 true。 如果使用凭证 user1/password1 请求连接,并且该连接之前使用不同的 user2/password2 连接, 则连接将关闭,并使用请求的凭证重新打开。这样,池大小仍可在全局级别进行管理,而不是在每个架构级别进行管理。 默认值为 false。此属性是作为错误 50025 的增强功能添加的。 |
dataSource 数据源 |
(javax.sql.DataSource) 将数据源注入到连接池中,该池将使用该数据源来检索连接,而不是使用 java.sql.Driver 接口建立连接。当希望池化 XA 连接或使用数据源而不是连接字符串建立的连接时,这非常有用。默认值为 null |
dataSourceJNDI |
(字符串)要在 JNDI 中查找的数据源的 JNDI 名称,然后用于建立与数据库的连接。请参阅 dataSource 属性。默认值为 null |
useDisposableConnectionFacade |
(布尔值)如果希望在连接上放置一个门面,以便在关闭后无法重复使用,请将此项设置为 true。 这可以防止线程保留它已调用 closed 的 connection 的引用,以对其执行查询。默认值为 true。 |
logValidationErrors |
(布尔值)将此项设置为 true 可将验证阶段的错误记录到日志文件中。如果设置为 true,则错误将记录为 SEVERE。默认值为 false 以实现向后兼容性。 |
propagateInterruptState |
(布尔值)将此设置为 true 可传播已中断的线程的中断状态(不清除中断状态)。默认值为 false 以实现向后兼容性。 |
ignoreExceptionOnPreLoad |
(布尔值)标记在初始化池时是否忽略连接创建错误。如果要在初始化池时忽略连接创建错误,请设置为 true。 如果要通过引发异常来使池的初始化失败,请设置为 false。默认值为 false。 |
useStatementFacade |
(布尔值)如果希望包装语句,以便在设置任何语句代理的情况下,允许在关闭的语句上调用 equals() 和 hashCode() 方法,请将其设置为 true。默认值为 true。 |
高级用法
JDBC 拦截器
要查看如何使用拦截器的示例,请查看 org.apache.tomcat.jdbc.pool.interceptor.ConnectionState
。
这个简单的拦截器是三个属性的缓存,事务隔离级别、自动提交和只读状态,以便系统避免不必要的数据库往返。
如果需要,将向池的核心添加更多拦截器。随时欢迎贡献代码!
拦截器当然不仅限于 java.sql.Connection,还可以用于包装方法调用的任何结果。 可以构建查询性能分析器,以便在查询运行时间超过预期时间时提供 JMX 通知。
配置 JDBC 拦截器
配置 JDBC 拦截器是使用 jdbcInterceptors 属性完成的。该属性包含以分号分隔的类名列表。
如果类名不是完全限定的,它将以 org.apache.tomcat.jdbc.pool.interceptor
为前缀。前缀。
例如:
jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"
同以下一样
jdbcInterceptors="ConnectionState;StatementFinalizer"
拦截器也可以具有属性。侦听器的属性在类名后面的括号内指定。多个属性用逗号分隔。
例如:
jdbcInterceptors="ConnectionState;StatementFinalizer(useEquals=true)"
类名、属性名和值周围的额外空白字符将被忽略。
org.apache.tomcat.jdbc.pool.JdbcInterceptor
所有拦截器的抽象基类,不能实例化。
属性Attribute | 说明Description |
---|---|
useEquals |
(布尔值)如果您希望 ProxyConnection 类使用 String.equals,则设置为 true,如果您希望在比较方法名称时使用 ==,则设置为 false。默认值为 true。 |
org.apache.tomcat.jdbc.pool.interceptor.ConnectionState
缓存以下属性 autoCommit、readOnly、transactionIsolation 和 catalog 的连接。 这是一项性能增强,可避免在调用 getter 或使用已设置的值调用 setter 时往返数据库。
org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer
跟踪使用 createStatement、prepareStatement 或 prepareCall 创建的所有语句,并在连接返回到池时关闭这些语句。
属性Attribute | 说明Description |
---|---|
trace |
(布尔值为 String)启用对未关闭语句的跟踪。启用并且连接已关闭且语句未关闭时,侦听器将记录所有堆栈跟踪。默认值为 false。 |
org.apache.tomcat.jdbc.pool.interceptor.StatementCache
在连接上缓存 PreparedStatement 和/或 CallableStatement 实例。
这些语句按连接缓存。对于属于同一池的所有连接,计数限制将全局计数。 一旦计数达到 max,后续语句就不会返回到缓存中,而是立即关闭。
属性Attribute | 说明Description |
---|---|
prepared |
(布尔值为 String)启用使用 prepareStatement 调用创建的 PreparedStatement 实例的缓存。默认值为 true。 |
callable |
(布尔值为 String)启用使用 prepareCall 调用创建的 CallableStatement 实例的缓存。默认值为 false。 |
max |
(int 为 String)对连接池中缓存的语句计数的限制。默认值为 50。 |
org.apache.tomcat.jdbc.pool.interceptor.StatementDecoratorInterceptor
请参阅 48392。拦截器包装语句和结果集,以防止使用方法 ResultSet.getStatement().getConnection()
和 Statement.getConnection()
访问实际连接。
org.apache.tomcat.jdbc.pool.interceptor.QueryTimeoutInterceptor
创建新语句时自动调用 java.sql.Statement.setQueryTimeout(seconds)
。
池本身不会使查询超时,仍然由 JDBC 驱动程序来强制执行查询超时。
属性Attribute | 说明Description |
---|---|
queryTimeout |
(int 为 String)要为查询超时设置的秒数。小于或等于零的值将禁用此功能。默认值为 1 秒。 |
org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport
跟踪查询性能,并在查询超过失败时间阈值时发出日志条目。使用的日志级别为 WARN
属性Attribute | 说明Description |
---|---|
threshold 门槛 |
(int 为 String)在发出日志警报之前,查询必须超过的毫秒数。默认值为 1000 毫秒。 |
maxQueries |
(int 为 String)为了保留内存空间而要跟踪的最大查询数。小于或等于 0 的值将禁用此功能。默认值为 1000。 |
logSlow |
(布尔值为 String)如果您希望记录慢速查询,请设置为 true。默认值为 true。 |
logFailed |
(布尔值为 String)如果您希望记录失败的查询,请设置为 true。默认值为 false。 |
org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx
扩展了 SlowQueryReport,除了日志条目之外,还会发出 JMX 通知,以便监控工具做出反应。 从其父类继承所有属性。此类使用 Tomcat 的 JMX 引擎,因此不会在 Tomcat 容器之外工作。 默认情况下,如果启用了 JMX 通知,则通过 ConnectionPool mbean 发送。 如果 notifyPool=false,SlowQueryReportJmx 也可以注册一个 MBean
属性Attribute | 说明Description |
---|---|
notifyPool |
(布尔值为 String)如果希望 JMX 通知转到 SlowQueryReportJmx MBean,则设置为 false默认值为 true。 |
objectName |
(字符串)定义一个有效的 javax.management.ObjectName 字符串,该字符串将用于向平台 mbean 服务器注册此对象默认值为 null,
该对象将使用 |
org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer
当从池中签出连接时,放弃的计时器将启动。这意味着,如果有 30 秒的超时,并且使用该连接运行 10x10 秒的查询, 则它将被标记为已放弃,并可能被回收,具体取决于 abandonWhenPercentageFull 属性。 使用此拦截器,将在每次对连接执行操作或成功执行查询时重置检出计时器。
代码示例
可以在 Tomcat 文档中找到用于 JDBC 的 Tomcat 配置的其他示例。
Java代码
以下是如何创建和使用数据源的简单示例。
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
public class SimplePOJOExample {
public static void main(String[] args) throws Exception {
PoolProperties p = new PoolProperties();
p.setUrl("jdbc:mysql://localhost:3306/mysql");
p.setDriverClassName("com.mysql.jdbc.Driver");
p.setUsername("root");
p.setPassword("password");
p.setJmxEnabled(true);
p.setTestWhileIdle(false);
p.setTestOnBorrow(true);
p.setValidationQuery("SELECT 1");
p.setTestOnReturn(false);
p.setValidationInterval(30000);
p.setTimeBetweenEvictionRunsMillis(30000);
p.setMaxActive(100);
p.setInitialSize(10);
p.setMaxWait(10000);
p.setRemoveAbandonedTimeout(60);
p.setMinEvictableIdleTimeMillis(30000);
p.setMinIdle(10);
p.setLogAbandoned(true);
p.setRemoveAbandoned(true);
p.setJdbcInterceptors(
"org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"+
"org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer");
DataSource datasource = new DataSource();
datasource.setPoolProperties(p);
Connection con = null;
try {
con = datasource.getConnection();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select * from user");
int cnt = 1;
while (rs.next()) {
System.out.println((cnt++)+". Host:" +rs.getString("Host")+
" User:"+rs.getString("User")+" Password:"+rs.getString("Password"));
}
rs.close();
st.close();
} finally {
if (con!=null) try {con.close();}catch (Exception ignore) {}
}
}
}
作为资源
下面是一个关于如何为 JNDI 查找配置资源的示例
<Resource name="jdbc/TestDB"
auth="Container"
type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
testWhileIdle="true"
testOnBorrow="true"
testOnReturn="false"
validationQuery="SELECT 1"
validationInterval="30000"
timeBetweenEvictionRunsMillis="30000"
maxActive="100"
minIdle="10"
maxWait="10000"
initialSize="10"
removeAbandonedTimeout="60"
removeAbandoned="true"
logAbandoned="true"
minEvictableIdleTimeMillis="30000"
jmxEnabled="true"
jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;
org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer"
username="root"
password="password"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mysql"/>
异步连接检索
Tomcat JDBC 连接池支持异步连接检索,而无需向池库添加其他线程。
它通过向数据源添加一个名为 Future<Connection>.getConnectionAsync()
的方法来实现此目的。
要使用异步检索,必须满足两个条件:
-
必须将 fairQueue 属性配置为 true。
-
必须将数据源强制转换为
org.apache.tomcat.jdbc.pool.DataSource
使用异步功能的示例如下所示。
Connection con = null;
try {
Future<Connection> future = datasource.getConnectionAsync();
while (!future.isDone()) {
System.out.println("Connection is not yet available. Do some background work");
try {
Thread.sleep(100); //simulate work
}catch (InterruptedException x) {
Thread.currentThread().interrupt();
}
}
con = future.get(); //should return instantly
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select * from user");
拦截器
拦截器是一种在特定连接或其子组件上启用、禁用或修改功能的有效方法。
拦截器何时有用,有许多不同的用例。默认情况下,出于性能原因,连接池是无状态的。
池本身插入的唯一状态是 defaultAutoCommit、defaultReadOnly、defaultTransactionIsolation、defaultCatalog
(如果已设置)。
这 4 个属性仅在创建连接时设置。如果在使用连接期间修改了这些属性,则池本身不会重置这些属性。
拦截器必须扩展 `org.apache.tomcat.jdbc.pool.JdbcInterceptor`类。 这个类相当简单,需要一个无参数的构造函数
public JdbcInterceptor() { }
当从池中借用连接时,拦截器可以通过实现
public abstract void reset(ConnectionPool parent, PooledConnection con);
方法。使用两个参数调用此方法,一个是对连接池本身 ConnectionPool parent 的引用, 另一个是对底层连接 PooledConnection con 的引用。
调用 java.sql.Connection 对象上的方法时,将导致
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
方法调用。Method 方法是调用的实际方法,Object[] args 是参数。
看一个非常简单的示例,演示了如果连接已关闭,如何使对 java.sql.Connection.close()
的调用成为 noop
if (CLOSE_VAL==method.getName()) {
if (isClosed()) return null; //noop for already closed.
}
return super.invoke(proxy,method,args);
正在进行观察。它是方法名称的比较。
一种方法是执行 "close".equals(method.getName())
。
在上面,看到了方法名称和静态 final String 引用之间的直接引用比较。
根据 JVM 规范,方法名称和静态 final String 最终位于共享常量池中,因此引用比较应该有效。当然也可以这样做:
if (compare(CLOSE_VAL,method)) {
if (isClosed()) return null; //noop for already closed.
}
return super.invoke(proxy,method,args);
compare(String,Method) 将在拦截器上使用 useEquals 标志,并在设置 useEquals=true 标志时进行引用比较或字符串值比较。
池开始/停止
当连接池启动或关闭时,可以收到通知。每个拦截器类只会收到一次通知,即使它是一个实例方法。 并且将使用当前未连接到存储池的侦听器通知。
public void poolStarted(ConnectionPool pool) {
}
public void poolClosed(ConnectionPool pool) {
}
在覆盖这些方法时,如果要扩展 JdbcInterceptor 以外的类,请不要忘记调用 super
配置侦听器
拦截器是使用jdbcInterceptors属性或setJdbcInterceptors方法配置的。侦听器可以具有属性,并且可以像这样配置
String jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.ConnectionState(useEquals=true,fast=yes)"
侦听器属性
由于拦截器可以具有属性,因此需要能够在拦截器中读取这些属性的值。以上面所示的示例为例,可以覆盖 setProperties 方法。
public void setProperties(Map<String, InterceptorProperty> properties) {
super.setProperties(properties);
final String myprop = "myprop";
InterceptorProperty p1 = properties.get(myprop);
if (p1!=null) {
setMyprop(Long.parseLong(p1.getValue()));
}
}
获取实际的 JDBC 连接
连接池围绕实际连接创建包装器,以便正确地将它们池化。
还在这些包装器中创建拦截器,以便能够执行某些功能。
如果需要检索实际连接,可以使用 javax.sql.PooledConnection
接口进行检索。
Connection con = datasource.getConnection();
Connection actual = ((javax.sql.PooledConnection)con).getConnection();
构建
使用 1.6 构建 JDBC 池代码,但对于运行时环境,它向后兼容 1.5。 对于单元测试,使用 1.6 及更高版本
可以在 Tomcat 文档中找到用于 JDBC 的 Tomcat 配置的其他示例。
从源码构建
构建非常简单。该池依赖于 tomcat-juli.jar,如果想要 SlowQueryReportJmx
javac -classpath tomcat-juli.jar \
-d . \
org/apache/tomcat/jdbc/pool/*.java \
org/apache/tomcat/jdbc/pool/interceptor/*.java \
org/apache/tomcat/jdbc/pool/jmx/*.java
可以在 Tomcat 源存储库中找到构建文件。
为方便起见,还包括一个构建文件,其中一个简单的构建命令将生成所需的所有文件。
ant download(下载依赖项)
ant build(编译并生成 .jar 文件)
ant dist (创建发布包)
ant test (运行测试,期望设置测试数据库)
该系统是为 Maven 构建构建的,但会生成发布工件。只是库本身。