- 简介
- 如何使用
- 属性
- 高级用法
- JDBC 拦截器
- 配置 JDBC 拦截器
- org.apache.tomcat.jdbc.pool.JdbcInterceptor
- org.apache.tomcat.jdbc.pool.interceptor.ConnectionState
- org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer
- org.apache.tomcat.jdbc.pool.interceptor.StatementCache
- org.apache.tomcat.jdbc.pool.interceptor.StatementDecoratorInterceptor
- org.apache.tomcat.jdbc.pool.interceptor.QueryTimeoutInterceptor
- org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport
- org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx
- org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer
- 代码示例
- 构建
Tomcat JDBC 连接池
目录
简介
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 池是 Tomcat 的一个模块,它依赖于 Tomcat JULI,一个在 Tomcat 中使用的简化日志框架。
- 使用
javax.sql.PooledConnection
接口获取底层连接。 - 防止饥饿。如果池为空,并且线程正在等待连接,当连接返回时,池将唤醒正在等待的正确线程。大多数池只会简单地饿死。
与其他连接池实现相比新增的功能
- 支持高并发环境和多核/多 CPU 系统。
- 接口的动态实现,将支持
java.sql
和javax.sql
接口以适应您的运行时环境(只要您的 JDBC 驱动程序也这样做),即使是在较低版本的 JDK 上编译也是如此。 - 验证间隔 - 我们不必每次使用连接时都进行验证,我们可以在借用或返回连接时进行验证,只是不要比我们可以配置的间隔更频繁。
- 运行一次查询,一个可配置的查询,只会在建立与数据库的连接时运行一次。非常有用,可以设置您希望在连接建立期间一直存在的会话设置。
- 能够配置自定义拦截器。这允许您编写自定义拦截器来增强功能。您可以使用拦截器来收集查询统计信息、缓存会话状态、在失败时重新连接连接、重试查询、缓存查询结果等等。您的选择是无限的,拦截器是动态的,不绑定到
java.sql
/javax.sql
接口的 JDK 版本。 - 高性能 - 我们将在后面展示一些性能差异
- 极其简单,由于实现非常简单,代码行数和源文件数量非常少,与 c3p0(上次我们检查时有超过 200 个源文件)相比,Tomcat jdbc 的核心只有 8 个文件,连接池本身大约是它的二分之一。由于可能出现错误,它们将更容易追踪,也更容易修复。从一开始,复杂性降低就是一个重点。
- 异步连接获取 - 您可以将您的连接请求排队,并接收一个
Future<Connection>
回来。 - 更好的空闲连接处理。它不会直接关闭连接,而是可以继续池化连接,并使用更智能的算法来调整空闲池的大小。
- 您可以决定何时将连接视为已放弃,是在池满时,还是直接在指定池使用阈值的超时时。
- 当执行语句/查询操作时,放弃连接计时器将重置。允许长时间使用的连接不会超时。这是通过使用 `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
- 将公平标志设置为 true 以实现线程公平性或使用异步连接检索。
Apache Tomcat 容器内部
Tomcat 连接池被配置为一个资源,如 Tomcat JDBC 文档 中所述。唯一的区别是您必须指定 `factory` 属性并将值设置为 `org.apache.tomcat.jdbc.pool.DataSourceFactory`。
独立
连接池只有一个依赖项,即 tomcat-juli.jar。要使用 bean 实例化在独立项目中配置池,要实例化的 bean 是 `org.apache.tomcat.jdbc.pool.DataSource`。与您用于将连接池配置为 JNDI 资源相同的属性(在下面有记录)用于将数据源配置为 bean。
JMX
连接池对象公开了一个可以注册的 MBean。为了让连接池对象创建 MBean,标志jmxEnabled
必须设置为 true。这并不意味着池将注册到 MBean 服务器,而仅仅是 MBean 被创建。在像 Tomcat 这样的容器中,Tomcat 本身会将 DataSource 注册到 MBean 服务器,org.apache.tomcat.jdbc.pool.DataSource
对象将随后注册实际的连接池 MBean。如果您在容器外部运行,您可以自己将 DataSource 注册到您指定的任何对象名称下,它会将注册传播到底层池。为此,您需要调用mBeanServer.registerMBean(dataSource.getPool().getJmxPool(),objectname)
。在此调用之前,请确保通过调用dataSource.createPool()
创建了池。
属性
为了提供一个非常简单的 commons-dbcp 和 tomcat-jdbc-pool 之间的切换,大多数属性是相同的,并且具有相同的含义。
JNDI 工厂和类型
属性 | 描述 |
---|---|
factory |
factory 是必需的,并且值应为 |
type |
Type 应始终为 根据类型,将创建 |
系统属性
系统属性是 JVM 范围的,影响 JVM 中创建的所有池
属性 | 描述 |
---|---|
org.apache.tomcat.jdbc.pool.onlyAttemptCurrentClassLoader |
(boolean) 控制动态类的类加载,例如 JDBC 驱动程序、拦截器和验证器。如果设置为 |
通用属性
这些属性在 commons-dbcp 和 tomcat-jdbc-pool 之间共享,在某些情况下,默认值不同。
属性 | 描述 |
---|---|
defaultAutoCommit |
(boolean) 此池创建的连接的默认自动提交状态。如果未设置,则默认为 JDBC 驱动程序默认值(如果未设置,则不会调用 |
defaultReadOnly |
(boolean) 此池创建的连接的默认只读状态。如果未设置,则不会调用 |
defaultTransactionIsolation |
(字符串) 此池创建的连接的默认事务隔离状态。以下之一:(参见 javadoc)
如果未设置,则不会调用该方法,它默认为 JDBC 驱动程序。 |
defaultCatalog |
(字符串) 此池创建的连接的默认目录。 |
driverClassName |
(字符串) 要使用的 JDBC 驱动的完全限定 Java 类名。驱动程序必须与 tomcat-jdbc.jar 相同的类加载器访问。 |
username |
(字符串) 要传递给我们的 JDBC 驱动程序以建立连接的连接用户名。请注意,默认情况下,方法 |
password |
(字符串) 要传递给我们的 JDBC 驱动程序以建立连接的连接密码。请注意,默认情况下,方法 |
maxActive |
(int) 可以同时从此池分配的最大活动连接数。默认值为 |
maxIdle |
(int) 始终应保留在池中的最大连接数。默认值为 |
minIdle |
(int) 始终应保留在池中的最小已建立连接数。如果验证查询失败,连接池可以缩小到此数字以下。默认值从 |
initialSize |
(int) 池启动时创建的初始连接数。默认值为 |
maxWait |
(int) 池在抛出异常之前等待连接返回(当没有可用连接时)的最大毫秒数。默认值为 |
testOnBorrow |
(布尔值) 指示是否在从池中借用对象之前验证对象。如果对象验证失败,它将从池中删除,我们将尝试借用另一个对象。为了更有效地验证,请参见 |
testOnConnect |
(布尔值) 指示是否在首次创建连接时验证对象。如果对象验证失败,它将抛出 |
testOnReturn |
(布尔值) 指示是否在将对象返回到池之前验证对象。默认值为 |
testWhileIdle |
(布尔值) 指示是否由空闲对象逐出器(如果有)验证对象。如果对象验证失败,它将从池中删除。默认值为 |
validationQuery |
(字符串) 将用于验证来自此池的连接(在将它们返回给调用者之前)的 SQL 查询。如果指定,此查询不必返回任何数据,它只是不能抛出 |
validationQueryTimeout |
(整数) 连接验证查询失败之前的超时时间(以秒为单位)。这是通过在执行 |
validatorClassName |
(字符串) 实现 |
timeBetweenEvictionRunsMillis |
(整数) 空闲连接验证/清理线程运行之间要休眠的毫秒数。此值不应设置为低于 1 秒。它决定我们检查空闲、放弃的连接的频率,以及我们验证空闲连接的频率。如果后者非零且更低,此值将被 |
numTestsPerEvictionRun |
(int) tomcat-jdbc-pool 中未使用此属性。 |
minEvictableIdleTimeMillis |
(int) 对象在池中处于空闲状态的最小时间,在此时间之后,该对象才有资格被驱逐。默认值为 |
accessToUnderlyingConnectionAllowed |
(boolean) 未使用的属性。可以通过在池化连接上调用 |
removeAbandoned |
(boolean) 如果连接超过 |
removeAbandonedTimeout |
(int) 在可以删除已放弃(正在使用)的连接之前的时间(以秒为单位)。默认值为 |
logAbandoned |
(boolean) 标志,用于记录放弃连接的应用程序代码的堆栈跟踪。记录已放弃的连接会为每次连接借用增加开销,因为必须生成堆栈跟踪。默认值为 |
connectionProperties |
(String) 在建立新连接时将发送到我们的 JDBC 驱动程序的连接属性。字符串的格式必须为 [propertyName=property;]* 注意 - "user" 和 "password" 属性将被显式传递,因此无需在此处包含它们。默认值为 |
poolPreparedStatements |
(boolean) 未使用的属性。 |
maxOpenPreparedStatements |
(int) 未使用的属性。 |
Tomcat JDBC 增强属性
属性 | 描述 |
---|---|
initSQL |
(String) 首次创建连接时要运行的自定义查询。默认值为 |
jdbcInterceptors |
(String) 以分号分隔的扩展
这些拦截器将作为拦截器插入到
预定义拦截器 更多预定义拦截器在JDBC 拦截器部分中详细描述。 |
validationInterval |
(long) 避免过度验证,仅以该频率(以毫秒为单位)运行验证。如果连接需要验证,但在此间隔内之前已验证过,则不会再次验证。默认值为 |
jmxEnabled |
(boolean) 是否将池注册到 JMX。默认值为 |
fairQueue |
(boolean) 如果希望对 getConnection 的调用以真正的 FIFO 方式公平对待,则将其设置为 true。这将使用 |
abandonWhenPercentageFull |
(int) 只有当正在使用的连接数量超过 |
maxAge |
(long) 保留连接的时间(以毫秒为单位),然后重新创建它。当从池中借用连接时,池将检查是否已达到 |
useEquals |
(布尔值) 如果希望 |
suspectTimeout |
(整数) 超时值,以秒为单位。默认值为 |
rollbackOnReturn |
(布尔值) 如果 |
commitOnReturn |
(布尔值) 如果 |
alternateUsernameAllowed |
(布尔值) 默认情况下,jdbc-pool 会忽略
但是,可以配置池以允许每次请求连接时使用不同的凭据。要启用 |
dataSource |
(javax.sql.DataSource) 将数据源注入连接池,池将使用数据源检索连接,而不是使用 |
dataSourceJNDI |
(字符串) 要在 JNDI 中查找的数据源的 JNDI 名称,然后使用它建立与数据库的连接。请参见 |
useDisposableConnectionFacade |
(布尔值) 如果您希望在连接上放置一个外观,以便在连接关闭后无法重用它,请将其设置为 true。这可以防止线程持有对它已经调用过关闭的连接的引用,以在其上执行查询。默认值为 |
logValidationErrors |
(布尔值) 将此设置为 true 以将验证阶段期间的错误记录到日志文件。如果设置为 true,错误将被记录为 SEVERE。默认值为 |
propagateInterruptState |
(布尔值) 将此设置为 true 以传播已中断线程的中断状态(不清除中断状态)。默认值为 |
ignoreExceptionOnPreLoad |
(布尔值) 标志是否忽略在初始化池时创建连接的错误。如果要忽略在初始化池时创建连接的错误,请将其设置为 true。如果要通过抛出异常来使池的初始化失败,请将其设置为 false。默认值为 |
useStatementFacade |
(布尔值) 如果您希望包装语句以启用在关闭的语句上调用 |
高级用法
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
所有拦截器的抽象基类,不能实例化。
属性 | 描述 |
---|---|
useEquals |
(布尔值) 如果希望 |
org.apache.tomcat.jdbc.pool.interceptor.ConnectionState
缓存连接的以下属性:autoCommit
、readOnly
、transactionIsolation
和 catalog
。这是一种性能增强,可以避免在调用 getter 或使用已设置的值调用 setter 时进行往返数据库操作。
属性 | 描述 |
---|
org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer
跟踪使用 createStatement
、prepareStatement
或 prepareCall
创建的所有语句,并在连接返回到池时关闭这些语句。
属性 | 描述 |
---|---|
跟踪 |
(布尔值作为字符串) 启用对未关闭语句的跟踪。启用后,如果连接关闭且语句未关闭,拦截器将记录所有堆栈跟踪。默认值为 |
org.apache.tomcat.jdbc.pool.interceptor.StatementCache
缓存 PreparedStatement
和/或 CallableStatement
实例到连接上。
语句在每个连接上缓存。计数限制对属于同一池的所有连接全局计数。一旦计数达到 max
,后续语句将不会返回到缓存,而是立即关闭。
属性 | 描述 |
---|---|
准备好的 |
(布尔值作为字符串) 启用对使用 |
可调用的 |
(布尔值作为字符串) 启用对使用 |
最大值 |
(整数作为字符串) 连接池中缓存语句数量的限制。默认值为 |
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 驱动程序来强制执行查询超时。
属性 | 描述 |
---|---|
查询超时 |
(int 作为字符串) 设置查询超时时间的秒数。小于或等于零的值将禁用此功能。默认值为 |
org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport
跟踪查询性能,并在查询超过时间阈值或失败时发出日志条目。使用的日志级别为 WARN
属性 | 描述 |
---|---|
阈值 |
(int 作为字符串) 查询必须超过的毫秒数,才会发出日志警报。默认值为 |
最大查询数 |
(int 作为字符串) 为保留内存空间而跟踪的查询最大数量。小于或等于 0 的值将禁用此功能。默认值为 |
记录慢查询 |
(boolean 作为字符串) 如果您希望记录慢速查询,请将其设置为 |
记录失败查询 |
(boolean 作为字符串) 如果您希望记录失败的查询,请将其设置为 |
org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx
扩展 SlowQueryReport
,除了日志条目之外,它还会为监控工具发出 JMX 通知以做出反应。继承其父类中的所有属性。此类使用 Tomcat 的 JMX 引擎,因此它在 Tomcat 容器之外无法工作。默认情况下,如果启用了 ConnectionPool mbean,则 JMX 通知将通过它发送。如果 notifyPool=false
,SlowQueryReportJmx
也可以注册一个 MBean
属性 | 描述 |
---|---|
通知池 |
(boolean 作为字符串) 如果您希望 JMX 通知发送到 |
对象名称 |
(String) 定义一个有效的 |
org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer
放弃计时器在从池中检出连接时启动。这意味着如果您有 30 秒的超时时间,并且使用连接运行 10 次 10 秒的查询,它将被标记为放弃,并且可能会根据 abandonWhenPercentageFull
属性被回收。使用此拦截器,它将在您对连接执行操作或成功执行查询时重置检出计时器。
属性 | 描述 |
---|
代码示例
有关 JDBC 使用的 Tomcat 配置的其他示例,请参阅 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 method
是实际调用的方法,Object[] args
是参数。为了查看一个非常简单的示例,我们演示了如何在连接已关闭的情况下如何使对java.sql.Connection.close()
的调用成为无操作。
if (CLOSE_VAL==method.getName()) {
if (isClosed()) return null; //noop for already closed.
}
return super.invoke(proxy,method,args);
正在进行观察。它是对方法名称的比较。一种方法是执行"close".equals(method.getName())
。在上面,我们看到了方法名称和static final String
引用之间的直接引用比较。根据 JVM 规范,方法名称和静态最终字符串最终会进入共享常量池,因此引用比较应该有效。当然也可以这样做
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 及更高版本。
有关 JDBC 使用的 Tomcat 配置的其他示例,请参阅 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 (downloads dependencies)
ant build (compiles and generates .jar files)
ant dist (creates a release package)
ant test (runs tests, expects a test database to be setup)
该系统结构用于 Maven 构建,但会生成发布工件。仅限库本身。