如果不使用连接池,针对数据库工作的多用户应用程序就不会高效。中间件可以提供这种服务,但并不是所有的应用程序都依赖这种中间件。然后,这些应用程序必须想出自己的方式来池连接。Apache Tomcat 项目不仅带有最流行的 Web 容器,还带有高性能连接池库 Tomcat JDBC。这篇文章介绍了如何在典型的 Maven + Spring 应用程序中配置 Tomcat JDBC。
为什么要使用独立的连接池?
以下是使用独立连接池的一些充分理由:
- 应用程序在容器中运行,但您不想使用容器连接池(不是有效的或专有的配置机制,如 Web 控制台或一些复杂的 XML 文件等)。然后,应用程序在启动时创建连接。连接参数(驱动程序、url、用户、密码)和池配置可以在一个简单的属性文本文件中进行外部化。
- 应用程序在运行时需要一些连接池。这不是一个常见的要求,但是像商业智能应用程序这样的一些系统需要连接到不同的数据库,并且连接是在运行时由高级用户配置的。
- 应用程序没有在容器中运行,需要连接到数据库。无容器部署越来越流行,例如,Dropwizard微服务框架使用 Tomcat JDBC 来管理其连接池。
如果您的应用程序属于这些用例中的任何一个,那么使用 Tomcat JDBC 是一个不错的选择。Tomcat JDBC 是在 Tomcat 7 中引入的,作为 Commons DBCP 的替代品(请参阅此处的一些原因)。Tomcat Expert 博客中
的几篇文章彻底解释了 Tomcat JDBC 的特性,另一篇文章甚至提供了 Tomcat JDBC、Commons DBCP 和 C3P0 之间的比较。简而言之,Tomcat JDBC 比其他实现更简单、更快,而且不会牺牲功能。
Tomcat 导出帖子错过的是在独立环境中使用池。所以本文重点介绍Tomcat JDBC在典型的独立环境(Maven、Spring)中的使用,而不是功能或性能。
添加 Maven 依赖项
Tomcat JDBC 在公共 Maven 存储库中可用。这意味着您可以使用您最喜欢的构建/依赖管理工具(Maven、Gradle、Ivy)轻松获取依赖关系。这是Maven的依赖代码:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
<version>7.0.35</version>
</dependency>
如果您在 Tomcat 7 上部署或提供 Tomcat JDBC 作为基础架构库,则可以将范围设置为“提供”:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
<version>7.0.35</version>
<scope>provided</scope>
</dependency>
这样,Tomcat JDBC 将不会包含在最终存档中。
声明一个池
Tomcat JDBC 使用起来很简单:需要创建一个实例org.apache.tomcat.jdbc.pool.DataSource
并使用适当的设置器来配置池:
import org.apache.tomcat.jdbc.pool.DataSource;
(...)
DataSource ds = new DataSource();
ds.setDriverClassName("org.h2.Driver");
ds.setUrl("jdbc:h2:java-config");
ds.setUsername("sa");
ds.setPassword("");
ds.setInitialSize(5);
ds.setMaxActive(10);
ds.setMaxIdle(5);
ds.setMinIdle(2);
Noteorg.apache.tomcat.jdbc.pool.DataSource
实现了javax.sql.DataSource
,因此可以在任何需要标准的地方使用池的实例DataSource
。
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.tomcat.jdbc.pool.DataSource;
public class TomcatJDBCPool {
// 数据库驱动名称
final static String driver = "com.mysql.cj.jdbc.Driver";
// 数据库连接地址
final static String jdbcUrl = "jdbc:mysql://127.0.0.1:3306/testdb";
// 数据库用户名
final static String user = "root";
// 数据库密码
final static String passwd = "root";
// 连接池初始化大小
final static int initialSize = 5;
// 连接池最小空闲
final static int minPoolSize = 10;
// 连接池最大连接数量
final static int maxPoolSize = 50;
// 最小逐出时间,100秒
final static int maxIdleTime = 100000;
// 连接失败重试次数
final static int retryAttempts = 10;
// 当连接池连接耗尽时获取连接数
final static int acquireIncrement = 5;
//sql
private final static String sql = "Select * from employee where id =1";
public static void main(String[] args) throws IOException, SQLException {
DataSource ds = new DataSource();
ds.setUrl(jdbcUrl);
ds.setUsername(user);
ds.setPassword(passwd);
ds.setDriverClassName(driver);
ds.setInitialSize(initialSize);
ds.setMaxIdle(minPoolSize);
ds.setMaxActive(maxPoolSize);
ds.setTestWhileIdle(false);
ds.setTestOnBorrow(false);
ds.setTestOnConnect(false);
ds.setTestOnReturn(false);
try {
Connection conn = ds.getConnection();
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);
while (rs.next()) {
System.out.println("id : " + rs.getInt("id") + " Name : "
+ rs.getString("name") + " Age : " + rs.getInt("age"));
}
rs.close();
st.close();
//conn.close();
for (int i = 1; i <= 1000; i++) {
conn = ds.getConnection();
System.out.println(conn + " : " + i);
System.out.println("正在使用连接数:" + ds.getNumActive());
System.out.println("空闲连接数:" + ds.getNumIdle());
System.out.println("总连接数:" + ds.getCreatedCount());
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
将 Tomcat JDBC 与 Spring 结合使用(Java 配置)
Spring 是 Java 企业应用程序中的常见选择。该框架为应用程序带来了可移植性,因此在 Spring 应用程序中使用独立的连接池是有意义的。下面是一个使用基于 Spring Java 的配置的 Tomcat JDBC 的示例:
@Configuration
public class DataAccessConfiguration {
@Bean(destroyMethod = "close")
public javax.sql.DataSource datasource() {
org.apache.tomcat.jdbc.pool.DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource();
ds.setDriverClassName("org.h2.Driver");
ds.setUrl("jdbc:h2:java-config");
ds.setUsername("sa");
ds.setPassword("");
ds.setInitialSize(5);
ds.setMaxActive(10);
ds.setMaxIdle(5);
ds.setMinIdle(2);
return ds;
}
@Bean public JdbcOperations tpl() {
return new JdbcTemplate(datasource());
}
}
注意destroyMethod
属性的使用:Spring 会close
在其容器关闭时调用该方法,以使池将连接返回给数据库。还要注意JdbcTemplate
: 它的声明需要javax.sql.DataSource
被创建,因此接受 Tomcat JDBCDataSource
实现。
将 Tomcat JDBC 与 Spring 结合使用(XML 配置)
XML 配置在 Spring 应用程序中仍然很流行,尤其是对于像连接池这样的基础设施组件:
<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
<property name="driverClassName" value="org.h2.Driver" />
<property name="url" value="jdbc:h2:mem:xml-config" />
<property name="username" value="sa" />
<property name="password" value="" />
<property name="initialSize" value="5" />
<property name="maxActive" value="10" />
<property name="maxIdle" value="5" />
<property name="minIdle" value="2" />
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="dataSource" />
</bean>
注意该destroy-method
属性的使用,以确保在 Spring 容器关闭时关闭池。
注意:配置参数不必硬编码!Spring 提供了几种外部化此类参数的方法(属性占位符、类似 ${...} 的语法以及Environment
带有 的抽象PropertySource
)。查看 Spring 文档以获取更多信息。
方便的功能:连接初始化 SQL 和验证
Tomcat JDBC 提供了许多特性。如果您比连接池的基本用法更进一步,您可能需要其中的 2 个。
第一个特性是在创建新连接时执行一些 SQL 指令。因此,对于每个连接,SQL 代码指令只执行一次。当应用程序需要对连接进行“标记”以使监控更容易时,这会派上用场。对于 PostgreSQL,可以这样做:
ds.setInitSQL("SET application_name = 'my-app'");
通过这样做,我们的池实例创建的连接将在执行时显示该列的my-app
值。对监控非常有用! 另一个功能是连接验证。如果某些数据库检测到它们未被使用或连接可能由于网络故障而丢失,它们会非常积极地关闭打开的连接。然后,每次应用程序借用连接时,池都可以执行验证查询。如果验证查询失败,则池假定它已死并创建一个新的。这对应用程序来说都是透明的,不必担心死连接或无效连接。 验证查询在 Tomcat JDBC 中有一个设置器:application_name
select * from pg_stat_activity
ds.setValidationQuery("select 1");
结论
Tomcat JDBC 是一个健壮、轻量级和高性能的连接池库。它是较旧但流行的 Commons DBCP 项目的可行替代方案,并且可以轻松嵌入到任何应用程序中。不要等待尝试一下!
源代码