Controlling Database Connections

Using DataSource

Spring 通过 DataSource 获取与数据库的连接。DataSource 是 JDBC 规范的一部分,是一个通用的连接工厂。它允许容器或框架对应用程序代码隐藏连接池和事务管理问题。作为开发人员,您不需要了解如何连接到数据库的详细信息。这是设置数据源的管理员的职责。您很可能在开发和测试代码时扮演这两个角色,但您不必一定要知道生产数据源是如何配置的。

Spring obtains a connection to the database through a DataSource. A DataSource is part of the JDBC specification and is a generalized connection factory. It lets a container or a framework hide connection pooling and transaction management issues from the application code. As a developer, you need not know details about how to connect to the database. That is the responsibility of the administrator who sets up the datasource. You most likely fill both roles as you develop and test code, but you do not necessarily have to know how the production data source is configured.

当您使用 Spring 的 JDBC 层时,可以从 JNDI 获取数据源,也可以使用第三方提供的连接池实现配置您自己的数据源。传统的选项是用 bean 样式的 DataSource 类的 Apache Commons DBCP 和 C3P0;对于现代 JDBC 连接池,请考虑使用构建器样式 API 的 HikariCP。

When you use Spring’s JDBC layer, you can obtain a data source from JNDI, or you can configure your own with a connection pool implementation provided by a third party. Traditional choices are Apache Commons DBCP and C3P0 with bean-style DataSource classes; for a modern JDBC connection pool, consider HikariCP with its builder-style API instead.

你应该只将 DriverManagerDataSourceSimpleDriverDataSource 类(Spring 分发中包含)用于测试目的!这些变体不提供池化,且在对连接进行多次请求时执行效果不佳。

You should use the DriverManagerDataSource and SimpleDriverDataSource classes (as included in the Spring distribution) only for testing purposes! Those variants do not provide pooling and perform poorly when multiple requests for a connection are made.

以下部分使用 Spring 的 DriverManagerDataSource 实现。稍后将涵盖其他几个 DataSource 变体。

The following section uses Spring’s DriverManagerDataSource implementation. Several other DataSource variants are covered later.

要配置 DriverManagerDataSource

To configure a DriverManagerDataSource:

  1. Obtain a connection with DriverManagerDataSource as you typically obtain a JDBC connection.

  2. Specify the fully qualified class name of the JDBC driver so that the DriverManager can load the driver class.

  3. Provide a URL that varies between JDBC drivers. (See the documentation for your driver for the correct value.)

  4. Provide a username and a password to connect to the database.

以下示例展示了如何配置 DriverManagerDataSource

The following example shows how to configure a DriverManagerDataSource:

  • Java

  • Kotlin

  • Xml

@Bean
DriverManagerDataSource dataSource() {
	DriverManagerDataSource dataSource = new DriverManagerDataSource();
	dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
	dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
	dataSource.setUsername("sa");
	dataSource.setPassword("");
	return dataSource;
}
@Bean
fun dataSource() = DriverManagerDataSource().apply {
	setDriverClassName("org.hsqldb.jdbcDriver")
	url = "jdbc:hsqldb:hsql://localhost:"
	username = "sa"
	password = ""
}
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	<property name="driverClassName" value="${jdbc.driverClassName}"/>
	<property name="url" value="${jdbc.url}"/>
	<property name="username" value="${jdbc.username}"/>
	<property name="password" value="${jdbc.password}"/>
</bean>

<context:property-placeholder location="jdbc.properties"/>

接下来的两个示例展示了 DBCP 和 C3P0 的基本连接性和配置。要了解有关帮助控制池特性的更多选项,请参阅各个连接池实现的产品文档。

The next two examples show the basic connectivity and configuration for DBCP and C3P0. To learn about more options that help control the pooling features, see the product documentation for the respective connection pooling implementations.

以下示例展示了 DBCP 配置:

The following example shows DBCP configuration:

  • Java

  • Kotlin

  • Xml

@Bean(destroyMethod = "close")
BasicDataSource dataSource() {
	BasicDataSource dataSource = new BasicDataSource();
	dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
	dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
	dataSource.setUsername("sa");
	dataSource.setPassword("");
	return dataSource;
}
@Bean(destroyMethod = "close")
fun dataSource() = BasicDataSource().apply {
	driverClassName = "org.hsqldb.jdbcDriver"
	url = "jdbc:hsqldb:hsql://localhost:"
	username = "sa"
	password = ""
}
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
	<property name="driverClassName" value="${jdbc.driverClassName}"/>
	<property name="url" value="${jdbc.url}"/>
	<property name="username" value="${jdbc.username}"/>
	<property name="password" value="${jdbc.password}"/>
</bean>

<context:property-placeholder location="jdbc.properties"/>

以下示例展示了 C3P0 配置:

The following example shows C3P0 configuration:

  • Java

  • Kotlin

  • Xml

@Bean(destroyMethod = "close")
ComboPooledDataSource dataSource() throws PropertyVetoException {
	ComboPooledDataSource dataSource = new ComboPooledDataSource();
	dataSource.setDriverClass("org.hsqldb.jdbcDriver");
	dataSource.setJdbcUrl("jdbc:hsqldb:hsql://localhost:");
	dataSource.setUser("sa");
	dataSource.setPassword("");
	return dataSource;
}
@Bean(destroyMethod = "close")
fun dataSource() = ComboPooledDataSource().apply {
	driverClass = "org.hsqldb.jdbcDriver"
	jdbcUrl = "jdbc:hsqldb:hsql://localhost:"
	user = "sa"
	password = ""
}
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
	<property name="driverClass" value="${jdbc.driverClassName}"/>
	<property name="jdbcUrl" value="${jdbc.url}"/>
	<property name="user" value="${jdbc.username}"/>
	<property name="password" value="${jdbc.password}"/>
</bean>

<context:property-placeholder location="jdbc.properties"/>

Using DataSourceUtils

DataSourceUtils 类是一个方便且强大的帮助程序类,它提供“静态”方法来从 JNDI 获取连接并在必要时关闭连接。它支持使用 DataSourceTransactionManagerJtaTransactionManagerJpaTransactionManager 的线程绑定 JDBC Connection

The DataSourceUtils class is a convenient and powerful helper class that provides static methods to obtain connections from JNDI and close connections if necessary. It supports a thread-bound JDBC Connection with DataSourceTransactionManager but also with JtaTransactionManager and JpaTransactionManager.

请注意,JdbcTemplate 意味着 DataSourceUtils 连接访问,在每次 JDBC 操作后使用它,隐式参与正在进行的事务。

Note that JdbcTemplate implies DataSourceUtils connection access, using it behind every JDBC operation, implicitly participating in an ongoing transaction.

Implementing SmartDataSource

SmartDataSource 接口应该由可以提供与关系数据库的连接的类实现。它扩展了 DataSource 接口,以允许使用它的类查询是否应该在给定操作后关闭连接。当您知道需要重用连接时,此用法很有效。

The SmartDataSource interface should be implemented by classes that can provide a connection to a relational database. It extends the DataSource interface to let classes that use it query whether the connection should be closed after a given operation. This usage is efficient when you know that you need to reuse a connection.

Extending AbstractDataSource

AbstractDataSource 是 Spring DataSource 实现的 abstract 基类。它实现了所有 DataSource 实现通用的代码。如果您编写自己的 DataSource 实现,则应扩展 AbstractDataSource 类。

AbstractDataSource is an abstract base class for Spring’s DataSource implementations. It implements code that is common to all DataSource implementations. You should extend the AbstractDataSource class if you write your own DataSource implementation.

Using SingleConnectionDataSource

SingleConnectionDataSource 类是对 SmartDataSource 接口的一个实现,它封装了一个在每次使用后不会关闭的单个 Connection。这不支持多线程。

The SingleConnectionDataSource class is an implementation of the SmartDataSource interface that wraps a single Connection that is not closed after each use. This is not multi-threading capable.

如果任何客户端代码假设连接池中的连接(如使用持久性工具时)而调用 close,您应将 suppressClose 属性设置为 true。此设置会返回一个抑制关闭的代理,它封装物理连接。请注意,您不能再将它强制转换原生 Oracle Connection 或类似的对象。

If any client code calls close on the assumption of a pooled connection (as when using persistence tools), you should set the suppressClose property to true. This setting returns a close-suppressing proxy that wraps the physical connection. Note that you can no longer cast this to a native Oracle Connection or a similar object.

SingleConnectionDataSource 主要是一个测试类。通常,它能够轻松地测试应用程序服务器外的代码,并与简单的 JNDI 环境结合使用。与 DriverManagerDataSource 相反,它始终重复使用相同连接,避免了物理连接的过度创建。

SingleConnectionDataSource is primarily a test class. It typically enables easy testing of code outside an application server, in conjunction with a simple JNDI environment. In contrast to DriverManagerDataSource, it reuses the same connection all the time, avoiding excessive creation of physical connections.

Using DriverManagerDataSource

DriverManagerDataSource 类是对标准 DataSource 接口的一个实现,它通过 bean 属性配置一个普通的 JDBC 驱动,并每次返回一个新的 Connection

The DriverManagerDataSource class is an implementation of the standard DataSource interface that configures a plain JDBC driver through bean properties and returns a new Connection every time.

此实现对于 Jakarta EE 容器外的测试和单机环境很有用,它既可用作 Spring IoC 容器中的 DataSource bean,也能与简单的 JNDI 环境结合使用。假设池中的 Connection.close() 会关闭连接,因此,任何支持 DataSource 的持久性代码都能运行。但是,使用 JavaBean 样式连接池(如 commons-dbcp)非常容易,即使是在测试环境中,使用此类连接池通常比使用 DriverManagerDataSource 更可取。

This implementation is useful for test and stand-alone environments outside of a Jakarta EE container, either as a DataSource bean in a Spring IoC container or in conjunction with a simple JNDI environment. Pool-assuming Connection.close() calls close the connection, so any DataSource-aware persistence code should work. However, using JavaBean-style connection pools (such as commons-dbcp) is so easy, even in a test environment, that it is almost always preferable to use such a connection pool over DriverManagerDataSource.

Using TransactionAwareDataSourceProxy

TransactionAwareDataSourceProxy 是一个目标 DataSource 的代理。该代理封装目标 DataSource 以添加对 Spring 管理的事务的感知。在这方面,它类似于事务性 JNDI DataSource(由 Jakarta EE 服务器提供)。

TransactionAwareDataSourceProxy is a proxy for a target DataSource. The proxy wraps that target DataSource to add awareness of Spring-managed transactions. In this respect, it is similar to a transactional JNDI DataSource, as provided by a Jakarta EE server.

除了必须调用现有的代码并传递标准 JDBC DataSource 接口实现时,很少需要使用此类。在这种情况下,你仍然可以使用此代码,同时让此代码参与 Spring 管理的事务中。通常最好使用更高级的资源管理抽象编写自己的新代码,例如 JdbcTemplateDataSourceUtils

It is rarely desirable to use this class, except when already existing code must be called and passed a standard JDBC DataSource interface implementation. In this case, you can still have this code be usable and, at the same time, have this code participating in Spring managed transactions. It is generally preferable to write your own new code by using the higher level abstractions for resource management, such as JdbcTemplate or DataSourceUtils.

有关更多详细信息,请参阅 TransactionAwareDataSourceProxy javadoc。

See the TransactionAwareDataSourceProxy javadoc for more details.

Using DataSourceTransactionManager / JdbcTransactionManager

DataSourceTransactionManager 类是一个面向单个 JDBC DataSourcePlatformTransactionManager 实现。它将 JDBC Connection 从指定的 DataSource 绑定到当前正在执行的线程,这可能允许每个 DataSource 有一个线程绑定的 Connection

The DataSourceTransactionManager class is a PlatformTransactionManager implementation for a single JDBC DataSource. It binds a JDBC Connection from the specified DataSource to the currently executing thread, potentially allowing for one thread-bound Connection per DataSource.

应用程序代码需要通过 DataSourceUtils.getConnection(DataSource) 检索 JDBC Connection,而不是 Java EE 的标准 DataSource.getConnection。它抛出未签名的 org.springframework.dao 异常,而不是已签名的 SQLException。所有框架类(如 JdbcTemplate)都隐式使用此策略。如果不与事务管理器配合使用,则查找策略的行为与 DataSource.getConnection 完全相同,因此可以在任何情况下使用。

Application code is required to retrieve the JDBC Connection through DataSourceUtils.getConnection(DataSource) instead of Java EE’s standard DataSource.getConnection. It throws unchecked org.springframework.dao exceptions instead of checked SQLExceptions. All framework classes (such as JdbcTemplate) use this strategy implicitly. If not used with a transaction manager, the lookup strategy behaves exactly like DataSource.getConnection and can therefore be used in any case.

DataSourceTransactionManager 类支持保存点(PROPAGATION_NESTED)、自定义隔离级别和超时,这些超时将适当地用作 JDBC 语句查询超时。为了支持后者,应用程序代码必须在为每个创建的语句使用 JdbcTemplate 或调用 DataSourceUtils.applyTransactionTimeout(..) 方法。

The DataSourceTransactionManager class supports savepoints (PROPAGATION_NESTED), custom isolation levels, and timeouts that get applied as appropriate JDBC statement query timeouts. To support the latter, application code must either use JdbcTemplate or call the DataSourceUtils.applyTransactionTimeout(..) method for each created statement.

您可以使用 DataSourceTransactionManager(而不是 JtaTransactionManager)来处理单一资源情形,因为它不要求容器支持 JTA 事务协调器。在这些事务管理器之间切换仅是一个配置问题,前提是坚持必需的连接查找模式。请注意,JTA 不支持保存点或自定义隔离级别,并且具有不同的超时机制,但在 JDBC 资源和 JDBC 提交/回滚管理方面表现出类似的行为。

You can use DataSourceTransactionManager instead of JtaTransactionManager in the single-resource case, as it does not require the container to support a JTA transaction coordinator. Switching between these transaction managers is just a matter of configuration, provided you stick to the required connection lookup pattern. Note that JTA does not support savepoints or custom isolation levels and has a different timeout mechanism but otherwise exposes similar behavior in terms of JDBC resources and JDBC commit/rollback management.

对于 JTA 风格的实际资源连接的延迟检索,Spring 为目标连接池提供了一个对应的 DataSource 代理类:请参阅 LazyConnectionDataSourceProxy。这对于可能没有实际语句执行的潜在空事务特别有用(在这种情况下从不获取实际资源),并且也适用于希望考虑事务同步只读标志和/或隔离级别的路由 DataSource 前面(例如 IsolationLevelDataSourceRouter)。

For JTA-style lazy retrieval of actual resource connections, Spring provides a corresponding DataSource proxy class for the target connection pool: see LazyConnectionDataSourceProxy. This is particularly useful for potentially empty transactions without actual statement execution (never fetching an actual resource in such a scenario), and also in front of a routing DataSource which means to take the transaction-synchronized read-only flag and/or isolation level into account (e.g. IsolationLevelDataSourceRouter).

LazyConnectionDataSourceProxy 还为只读连接池提供特殊支持,以便在只读事务期间使用,从而避免在从主连接池获取 JDBC 连接时在每个事务的开头和结尾处切换 JDBC 连接的只读标记(这可能因 JDBC 驱动而有所不同)。

LazyConnectionDataSourceProxy also provides special support for a read-only connection pool to use during a read-only transaction, avoiding the overhead of switching the JDBC Connection’s read-only flag at the beginning and end of every transaction when fetching it from the primary connection pool (which may be costly depending on the JDBC driver).

从 5.3 开始,Spring 提供了一个扩展的 JdbcTransactionManager 变体,它在提交/回滚时增加了异常转换功能(与 JdbcTemplate 一致)。DataSourceTransactionManager 将只会引发 TransactionSystemException(类似于 JTA),而 JdbcTransactionManager 会将数据库锁定失败等转换为相应的 DataAccessException 子类。请注意,应用程序代码需要为这些异常做好准备,而不仅仅是期望 TransactionSystemException。在这样的情况下,JdbcTransactionManager 是推荐的选择。

As of 5.3, Spring provides an extended JdbcTransactionManager variant which adds exception translation capabilities on commit/rollback (aligned with JdbcTemplate). Where DataSourceTransactionManager will only ever throw TransactionSystemException (analogous to JTA), JdbcTransactionManager translates database locking failures etc to corresponding DataAccessException subclasses. Note that application code needs to be prepared for such exceptions, not exclusively expecting TransactionSystemException. In scenarios where that is the case, JdbcTransactionManager is the recommended choice.

在异常行为方面,JdbcTransactionManager 大致等同于 JpaTransactionManagerR2dbcTransactionManager,是彼此的直接伴侣/替换。另一方面,DataSourceTransactionManager 等同于 JtaTransactionManager,并可以在其中作为直接替换。

In terms of exception behavior, JdbcTransactionManager is roughly equivalent to JpaTransactionManager and also to R2dbcTransactionManager, serving as an immediate companion/replacement for each other. DataSourceTransactionManager on the other hand is equivalent to JtaTransactionManager and can serve as a direct replacement there.